You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Storybook Test makes it ergonomic to write and run tests on stories using the play function. To do so, it relies on @testing-library/user-event to simulate events in the DOM. Simulated events make it possible to run stories in any browser and in both local and deployed Storybooks. Unfortunately, simulated events produce incorrect results in a few known scenarios. This RFC proposes a realEvent API in Storybook to overcome these limitations.
Problem Statement
Testing Library’s user-event package uses synthetic events that run in "user space" within the DOM. This means that user-event calls can run in any environment without any special security privileges. You can run it locally, or in a static Storybook that’s been deployed to a server, and you don’t need a special browser to view stories.
The price of this flexibility is fidelity. In most cases, the simulated behavior is close enough to how a “real” browser event would behave. However, there are a few cases where simulated events fall short:
Visual occlusion: TL may interact with hidden or off-screen elements that a real user wouldn't be able to.
Hover interactions: Synthetic hover events don’t always trigger styles or behaviors that require precise cursor positioning.
Drag-and-drop: Native browser interactions for drag-and-drop can behave differently from TL synthetic interactions.
Achieving these behaviors is only possible using real browser events. Tools like Playwright and WebDriverIO fire real browser events using Chrome DevTools Protocol (CDP), which instructs the browser to handle the events rather than interacting with the DOM. This means that tests need to run a specific browser, and that they are given elevated security access to interact with privileged browser APIs. This makes it impossible to browse and run those tests in a typical, non-privileged browser.
The goal of this RFC is to provide access to “real events” for tests that need them, without sacrificing the convenience of Storybook’s existing workflow. Thus our goal is not to “solve” real events, but rather to provide an escape hatch for tests that explicitly need them.
Non-goals
As stated above, we are interested in creating an escape hatch for using real events in specific tests. We are not trying to create a one-size-fits-all “real events” solution that can be used across all your stories. We propose taking that approach as a possible follow-up project depending on the usage of the solution proposed here.
Implementation
There are two aspects to the proposed solution:
A realEvent API that is injected on test context
Updated UI to NOT display realEvent stories with link to story in Vitest browser mode.
Let’s consider a simple story that hovers on a button, and how it would work in the proposed solution.
It’s important to note that the realEvent API does not need to match the userEvent API. We are patterning realEvent off of Vitest’s Interactivity API which provides different type signatures from its testing library default. However, unlike Vitest, we are proposing to make the usage of userEvent vs realEvent more obvious in the code.
To illustrate this idea, we’ve created a recipe that you can use today in your Storybook.
2. Updated UI
It’s important to note that a story that uses realEvent cannot be executed in a standard browser. So if you navigate to the Hovered story above in a standard browser, you would not be able to view it.
We could make it possible to view the story without the play function for convenience:
The “Learn more” button takes you to documentation for this screen. The “Open in Vitest” button opens the story in Vitest browser mode if the user is using addon-test. If not, the button will not be shown.
Furthermore, we propose that the “Component Tests” addon panel also has an “Open in Vitest” button in the upper right corner. This will be useful for debugging any discrepancies between the tests running in the user’s browser and the tests running in the background in Vitest.
Prior Art
This RFC is heavily inspired by the Vitest Interactivity API. Unlike Vitest, which tries to maintain a suite of different APIs depending on which test provider you use, this API makes the split more obvious and forces the developer to choose between test portability and fidelity.
Deliverables
Update Storybook to inject existing userEvent into test context.
Also inject realEvent API as described above. Exact interface TBD.
New UI as described above. Visual design and exact wording TBD.
Handle the case where we are running in a statically built Storybook.
Updated documentation for both.
Risks
The biggest risk is that the above solution is confusing. This is not a one-size-fits-all solution where the user can use realEvent across the board. We will encourage users to use realEvent on selected stories that need it, and for other stories to use the existing userEvent. However, that might not be the right set of tradeoffs, and perhaps a more aggressive solution that tries to provide real events across the board would be a better approach.
Unresolved Questions
Is this escape hatch sufficient? Or do we need a more "native" approach such as a "browser mode" for Storybook?
Do we force users to use tags to identify realEvents stories, or should we do it automatically?
Alternatives considered / Abandoned Ideas
Browser extension. Considered exposing CDP via a browser extension to make it possible to run real event tests statically built Storybooks. Out of scope for now because it adds too much maintenance burden.
Browser mode. Considered exposing CDP via a dev mode browser to make it possible to run real event tests without needing a browser extension to run, but only in dev mode. Out of scope for now because it adds too much maintenance burden.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Summary
Storybook Test makes it ergonomic to write and run tests on stories using the play function. To do so, it relies on @testing-library/user-event to simulate events in the DOM. Simulated events make it possible to run stories in any browser and in both local and deployed Storybooks. Unfortunately, simulated events produce incorrect results in a few known scenarios. This RFC proposes a realEvent API in Storybook to overcome these limitations.
Problem Statement
Testing Library’s user-event package uses synthetic events that run in "user space" within the DOM. This means that user-event calls can run in any environment without any special security privileges. You can run it locally, or in a static Storybook that’s been deployed to a server, and you don’t need a special browser to view stories.
The price of this flexibility is fidelity. In most cases, the simulated behavior is close enough to how a “real” browser event would behave. However, there are a few cases where simulated events fall short:
Achieving these behaviors is only possible using real browser events. Tools like Playwright and WebDriverIO fire real browser events using Chrome DevTools Protocol (CDP), which instructs the browser to handle the events rather than interacting with the DOM. This means that tests need to run a specific browser, and that they are given elevated security access to interact with privileged browser APIs. This makes it impossible to browse and run those tests in a typical, non-privileged browser.
The goal of this RFC is to provide access to “real events” for tests that need them, without sacrificing the convenience of Storybook’s existing workflow. Thus our goal is not to “solve” real events, but rather to provide an escape hatch for tests that explicitly need them.
Non-goals
As stated above, we are interested in creating an escape hatch for using real events in specific tests. We are not trying to create a one-size-fits-all “real events” solution that can be used across all your stories. We propose taking that approach as a possible follow-up project depending on the usage of the solution proposed here.
Implementation
There are two aspects to the proposed solution:
realEvent
API that is injected on test contextrealEvent
stories with link to story in Vitest browser mode.Let’s consider a simple story that hovers on a button, and how it would work in the proposed solution.
Here’s how the story would be written today:
1. Real Event API
Here’s how that story would be rewritten with
userEvent
injected into the test context:And here’s how the story would be rewritten using the
realEvent
API:It’s important to note that the
realEvent
API does not need to match theuserEvent
API. We are patterningrealEvent
off of Vitest’s Interactivity API which provides different type signatures from its testing library default. However, unlike Vitest, we are proposing to make the usage ofuserEvent
vsrealEvent
more obvious in the code.To illustrate this idea, we’ve created a recipe that you can use today in your Storybook.
2. Updated UI
It’s important to note that a story that uses
realEvent
cannot be executed in a standard browser. So if you navigate to theHovered
story above in a standard browser, you would not be able to view it.We could make it possible to view the story without the play function for convenience:
The “Learn more” button takes you to documentation for this screen. The “Open in Vitest” button opens the story in Vitest browser mode if the user is using
addon-test
. If not, the button will not be shown.Furthermore, we propose that the “Component Tests” addon panel also has an “Open in Vitest” button in the upper right corner. This will be useful for debugging any discrepancies between the tests running in the user’s browser and the tests running in the background in Vitest.
Prior Art
This RFC is heavily inspired by the Vitest Interactivity API. Unlike Vitest, which tries to maintain a suite of different APIs depending on which test provider you use, this API makes the split more obvious and forces the developer to choose between test portability and fidelity.
Deliverables
userEvent
into test context.realEvent
API as described above. Exact interface TBD.Risks
The biggest risk is that the above solution is confusing. This is not a one-size-fits-all solution where the user can use realEvent across the board. We will encourage users to use realEvent on selected stories that need it, and for other stories to use the existing userEvent. However, that might not be the right set of tradeoffs, and perhaps a more aggressive solution that tries to provide real events across the board would be a better approach.
Unresolved Questions
tags
to identify realEvents stories, or should we do it automatically?Alternatives considered / Abandoned Ideas
Browser extension. Considered exposing CDP via a browser extension to make it possible to run real event tests statically built Storybooks. Out of scope for now because it adds too much maintenance burden.
Browser mode. Considered exposing CDP via a dev mode browser to make it possible to run real event tests without needing a browser extension to run, but only in dev mode. Out of scope for now because it adds too much maintenance burden.
Beta Was this translation helpful? Give feedback.
All reactions