Interaction Testing in Storybook
When it comes to visually testing a collection of components, no other tool stands out quite like Storybook . Not only does Storybook provide an isolated environment for developing components and display their different rendered states, but Storybook also integrates with Testing Library to simulate and test component functionality that's triggered by a user interaction. For example, instead of just checking if a form renders correctly, you can also check if behavior like form submission and validation work correctly. This integration with Testing Library allows Storybook to capture all component states, those that can be reproduced solely with props and those that require user interaction to be reached. Conventionally, developers run and receive feedback on tests written in Jest and Testing Library in the CLI. It prints several lines of text that report which assertions ( expect statements) passed and failed. However, it never shows what the component looks like when rendered. With Storybook and a Storybook addon called @storybook/addon-interactions , you get to view and interact with the rendered component in Storybook's Canvas. Plus, you get to see line-by-line reporting of the simulated user interactions and passed/failed assertions in Storybook's Interactions panel. By having everything under one roof this way, you only need to set up decorators like theming and routing just once, and the Storybook GUI trumps over the CLI for debugging UI issues. In v6.4.0 , Storybook introduced browser-compatible wrappers of Testing Library and Jest that power interactive stories. These type of stories automate user interactions using a play function. A new feature to Component Story Format (CSF) 3.0, the play function executes after the component has rendered in Storybook. Using methods from the Testing Library, the play function dispatches events, such as userEvent.click / fireEvent.click for clicking an element, that simulate user interactions in the browser. Below, I'm going to show you how to write interaction tests directly in Storybook. For this tutorial, we will be testing the functionality of a line chart built with React and D3: Specifically, we will test... To get started, clone the following repository: This repository contains a visualization component library with a customizable, multi-line chart written in React, D3 and TypeScript. Inside of the project directory, install the dependencies: Then, run Storybook to view and interact with the multi-line chart component <TimelineChart /> : Currently, there are three stories defined for this component in Storybook: The Default and Legend stories visualize the closing prices of several popular technology stocks from May 2013 to May 2022: Apple (AAPL), Amazon (AMZN), Facebook (FB), Google (GOOGL), Netflix (NFLX) and Tesla (TSLA). Before it renders a story, Storybook fetches the stock data with loaders . Once fetched, the loaded data gets injected into the story via loaded field on the story context, like so: For interaction testing, we must have three addons/integrations installed: Since initializing Storybook for a React project (via the sb init command) automatically installs @storybook/addon-interactions and @storybook/testing-library , all that's left to install is @storybook/jest . Note : The React project must have react and react-dom installed prior to running sb init for the command to choose the correct Storybook project type and install these Storybook addons/integrations. When you open the .storybook/main.ts file, you will find the @storybook/addon-interactions addon already registered. To enable the addon's experimental step debugging features, set interactionsDebugger to true , like so: ( .storybook/main.ts ) Let's write some interaction tests for the <TimelineChart /> component. First, we will write an interaction test for validating the toggling behavior in the chart's legend. We expect the user to be able to toggle lines in the chart by checking/unchecking their corresponding legend keys. Initially, all the lines will be visible in the chart. The play function will simulate a user unchecking all of the legend keys but the first one. By the end, only the line corresponding to the first legend key that's left checked will remain rendered in the chart. The other lines will have been removed. Start by defining a story named ToggledLines . Since the chart requires a legend for toggling, the story will accept the same set of arguments as the Legend story. ( src/stories/TimelineChart.stories.tsx ) Next, let's define a play function on this story, like so: ( src/stories/TimelineChart.stories.tsx ) The play function receives a context object that contains lots of information about the story. For most interaction tests, you will be concerned with only three pieces of information: Now, at the top of the src/stories/TimelineChart.stories.tsx file, import several methods and objects from @storybook/testing-library and @storybook/jest to help simulate user interactions and perform assertions in the play function: ( src/stories/TimelineChart.stories.tsx ) Within the play function... ( src/stories/TimelineChart.stories.tsx ) Challenge : Change the play function so that it asserts that the lines corresponding to all but the first legend key no longer exist within the document. When you visit the ToggledLines story in the Storybook UI, you will notice that Storybook successfully simulated the user interactions and the assertion passed. Here, the canvas displays the component with only one line, the one that corresponds to the first legend key, which we left checked. Let's move on to the next interaction test. For this interaction test, we will validate the behavior of the chart's bisector line and cursor. When the user clicks on a line, the other lines fade out, and the bisector line and cursor appears and attaches to the clicked line. As the user moves their cursor, the bisector's infobox should display the exact value based on the nearest x-axis value. Start by defining a story named SelectedLine . Since multiple lines should fade out when the user clicks on a line, and coloring each line helps the user track the stock that they're interested in, the story will accept the same set of arguments as the Legend story. ( src/stories/TimelineChart.stories.tsx ) Next, let's define a play function on this story, like so: ( src/stories/TimelineChart.stories.tsx ) Since userEvent lacks support for a mousemove event, we need to import fireEvent from @storybook/testing-library . ( src/stories/TimelineChart.stories.tsx ) Within the play function... ( src/stories/TimelineChart.stories.tsx ) When you visit the SelectedLine story in the Storybook UI, you will notice that Storybook successfully simulated the user interactions and the assertions passed. Here, the canvas displays the component with only one line selected, the one that corresponds to the first legend key. A bisector line and cursor is attached to this line and is positioned in the center of the chart's bounded area. On September 1, 2017, the closing price of Apple stock (AAPL) was ~$41.01. What if you wanted to test a scenario in which the user unchecks all but the first legend key, clicks on the line that corresponds to the first legend key and moves their mouse to the center of the chart's bounded area to see what the stock's closing price was at that date within the bisector's infobox? A story that tests this scenario is a combination of the ToggledLines and SelectedLine stories. Rather than duplicating the simulated user interactions and assertions in these stories' play functions, you can re-use these stories' play functions and compose them to simulate complex scenarios like the one just mentioned, like so: ( src/stories/TimelineChart.stories.tsx ) All you need to do is pass the story's play function's context object to both play functions and call them in succession. When you visit the SelectedLine story in the Storybook UI, you will notice that Storybook successfully simulated the user interactions and the assertions passed. Here, the canvas displays exactly what the canvas for the SelectedLine story displayed, but with only one line shown, the one that corresponds to the first legend key. This is the final code for the src/stories/TimelineChart.stories.tsx file. If you find yourself stuck at any point while working through this tutorial, then feel free to visit the main branch of this GitHub repository here for the code. Try migrating your own project's testing suite over to Storybook. If you want to learn more advanced techniques with Storybook, then check out the Storybook for React Apps course by Yann Braga, a senior software engineer at Chromatic and a maintainer of Storybook: As a member of the Storybook team, Yann also worked on many of Storybook's testing-related packages, such as @storybook/addon-interactions , @storybook/test-runner , @storybook/testing-react , @storybook/testing-library and @storybook/jest .