Manipulate Story State with Play Functions and Testing Library Events
Let’s build up a story that utilizes user events.
[Show Page component code].
Start by creating a new component file: src/components/Page.js.
importthePagecomponent.- Assign in to
componenton story meta - And export a single story that rendres the page in its no-args logged-out state.
import {Page} from './Page'
export default { component: Page,}
export const LoggedOut = {}Great! Now we have a story that renders this page.
We can interact with it right in Storybook.
And see that clicking the Log in button renderes the logged in state of the component.
But there’s a problem.
We can’t use args to make a new story for this. We have to actually click this button.
If you’ve been testing UI as long as I have, you know that manual testing is a recipe for bugs.
Fortunately, Storybook automatically simplate user events: Play functions.
Let’s implement one.
- Add a story. I’ll name mine
LoggedIn- By default, it renders in the default logged in state. That’s expected.
- Define a
playproperty on the story object. - Assign an anonymous async function that takes
contextas an argument. - Use the
withinhelper to create a testingcanvas. - And query that
canvasfor aLog inbutton, using atesting-libraryquery. - Finally, simulate a user click event, using
testing-libraryevents.
import { within, userEvent } from '@storybook/test';
import {Page} from './Page'
export default { component: Page,}
export const LoggedOut = {}
export const LoggedIn = { play: async (context) => { const canvas = within(context.canvasElement) const loginButton = canvas.getByRole('button', { name: /Log in/i, }) await userEvent.click(loginButton) },}Once we arrive at the desired (LoggedIn) state, for our story, we can also make assertions about what we expect to see.
- Query the canvas again for the
Log outbutton. expectittoBeInTheDocument
import { within, userEvent, expect } from '@storybook/test';
export const LoggedIn = { play: async (context) => { const canvas = within(context.canvasElement) const loginButton = canvas.getByRole('button', { name: /Log in/i, })
await userEvent.click(loginButton)
const logoutButton = canvas.getByRole('button', { name: /Log out/i, }) await expect(logoutButton).toBeInTheDocument() },}With Play function, simple examples like this or complex user flows. This is an incredible way to bring UI narratives to life and protect real-world experiences.