No response
","upvoteCount":1,"answerCount":4,"acceptedAnswer":{"@type":"Answer","text":"You are correct. The issue you're encountering stems from the difference between synchronous and asynchronous operations in your Storybook play function, especially when running in a CI environment like Chromatic which can have different rendering timings than your local machine.
\nThe error message Unable to find an accessible element with the role \"button\" and name /back/i
occurs because you are using getByRole
. This is a synchronous query from the Testing Library. It expects the \"back\" button to be present in the DOM the instant the code executes. When the component takes a fraction of a second longer to render in Chromatic, the test fails because the button isn't there yet.
The correct solution, as you pointed out, is to use an asynchronous query.
\nThe Solution: Use findByRole
\nInstead of getByRole, you should use findByRole. The findBy... queries are asynchronous and will wait for the element to appear in the DOM, retrying for a brief period before timing out. This makes your tests resilient to variations in rendering speed.
\nYou need to await the result of the findByRole call.
\nHere is how you should adjust your play function:
\nIncorrect Code (Current):
\n// This can fail if the button isn't rendered immediately\nconst backButton = within(canvasElement).getByRole(\"button\", { name: /back/i });\nawait userEvent.click(backButton);
Correct Code (Proposed):
\nimport { within, userEvent } from '@storybook/test';\n\n// ... inside your play function\n\n// The 'await' pauses execution until the button is found or the query times out.\nconst backButton = await within(canvasElement).findByRole(\"button\", { name: /back/i });\nawait userEvent.click(backButton);
By making this change, your test will now patiently wait for the button to become available before attempting to interact with it, resolving the inconsistency between your local and Chromatic test runs.
","upvoteCount":1,"url":"https://github.com/storybookjs/storybook/discussions/32010#discussioncomment-13724327"}}}-
Describe the bugI had a number of stories that involved interactions locally, but when they ran in Chromatic, some of them failed because it could not find certain elements. The solution is adding the waitFor in those places because Chromatic didn't render the component or run a re-render, but the tests run before that. The problem is that I can't catch those errors until I deploy to Chromatic, spending time and snapshot credits. Reproduction linkhttps://www.chromatic.com/test?appId=683dceb1b0c1897db07f656a&id=686ec2dc7d819d77d728bfd8 Reproduction stepsNo response SystemStorybook Environment Info:
System:
OS: macOS 15.5
CPU: (14) arm64 Apple M4 Max
Shell: 5.9 - /bin/zsh
Binaries:
Node: 22.17.0 - ~/.nvm/versions/node/v22.17.0/bin/node
Yarn: 1.22.22 - ~/.nvm/versions/node/v22.17.0/bin/yarn <----- active
npm: 10.9.2 - ~/.nvm/versions/node/v22.17.0/bin/npm
Browsers:
Chrome: 138.0.7204.100
Safari: 18.5 Additional contextNo response |
Beta Was this translation helpful? Give feedback.
-
Hi @alanTechGC! I'm Dosu and I’m helping the storybook team. This kind of inconsistency is common when interaction tests rely on elements being immediately available, but Chromatic (and CI/headless environments) can have different rendering or asset loading timing than your local dev setup. The best way to make your tests robust across both environments is to always use async Testing Library queries like For even more reliability, you can use the There’s no built-in flag to simulate Chromatic’s environment locally, but running your tests with the Storybook test runner (headless mode, matching browser version) and following these async patterns will help you catch these issues before pushing to Chromatic. Also, make sure your global styles, fonts, and assets are loaded identically in both environments, and check that your If you still see discrepancies, try comparing DOM snapshots or enabling debug logging to spot differences in asset loading or DOM structure between local and Chromatic. To reply, just mention @dosu. How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other |
Beta Was this translation helpful? Give feedback.
-
Thank you @dosu. |
Beta Was this translation helpful? Give feedback.
-
You're right—waitForPageReady is specific to the Storybook Test Runner (Playwright-based) and isn't available for Vitest. There isn't a direct equivalent for Vitest, but the recommended approach is to use async Testing Library patterns in your play functions and tests. That means always using async queries like findBy..., wrapping assertions in waitFor, and using waitForElementToBeRemoved when waiting for elements to disappear. These patterns are shown in the official Storybook Vitest templates: import { waitFor, waitForElementToBeRemoved, within } from '@storybook/test';
export const SyncWaitFor = {
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await step('Submit form', Callback.play);
await waitFor(() => canvas.getByText('Completed!!'));
await waitForElementToBeRemoved(() => canvas.queryByText('Completed!!'), { timeout: 2000 });
},
}; You can also use loaders to simulate network or rendering delays if needed: export const WithLoaders = {
loaders: [async () => new Promise((resolve) => setTimeout(resolve, 2000))],
play: async ({ step }) => {
await step('Submit form', Callback.play);
},
}; There’s no global DOM readiness hook in addon-vitest, so you need to handle async waits explicitly in each test or play function. This is the best way to ensure your tests are robust across local, CI, and Chromatic runs. More details and examples are in the addon-vitest template stories. If you run into persistent issues even with these patterns, let me know what you’re seeing and I can help troubleshoot further. To reply, just mention @dosu. How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other |
Beta Was this translation helpful? Give feedback.
-
You are correct. The issue you're encountering stems from the difference between synchronous and asynchronous operations in your Storybook play function, especially when running in a CI environment like Chromatic which can have different rendering timings than your local machine. The error message The correct solution, as you pointed out, is to use an asynchronous query. The Solution: Use findByRole Instead of getByRole, you should use findByRole. The findBy... queries are asynchronous and will wait for the element to appear in the DOM, retrying for a brief period before timing out. This makes your tests resilient to variations in rendering speed. You need to await the result of the findByRole call. Here is how you should adjust your play function: Incorrect Code (Current): // This can fail if the button isn't rendered immediately
const backButton = within(canvasElement).getByRole("button", { name: /back/i });
await userEvent.click(backButton); Correct Code (Proposed): import { within, userEvent } from '@storybook/test';
// ... inside your play function
// The 'await' pauses execution until the button is found or the query times out.
const backButton = await within(canvasElement).findByRole("button", { name: /back/i });
await userEvent.click(backButton); By making this change, your test will now patiently wait for the button to become available before attempting to interact with it, resolving the inconsistency between your local and Chromatic test runs. |
Beta Was this translation helpful? Give feedback.
You are correct. The issue you're encountering stems from the difference between synchronous and asynchronous operations in your Storybook play function, especially when running in a CI environment like Chromatic which can have different rendering timings than your local machine.
The error message
Unable to find an accessible element with the role "button" and name /back/i
occurs because you are usinggetByRole
. This is a synchronous query from the Testing Library. It expects the "back" button to be present in the DOM the instant the code executes. When the component takes a fraction of a second longer to render in Chromatic, the test fails because the button isn't there yet.The correct …