Automating UI testing and documentation with Storybook, Chromatic, and CircleCI

Front-end Developer

As the web evolves, front-end developers face increasingly complex challenges, particularly in testing, documentation, and debugging. While frameworks like React, Angular, and Vue offer a means of breaking complex UIs into simplified, reusable components, their solutions do not fully address the challenges of managing and maintaining these components as applications scale.
This is where Storybook comes in. Storybook is a UI development tool that helps maintain UI consistency by providing isolated builds, tests, and documentation for components. With Storybook, components can be rendered independently of the main application, making debugging, testing, and UI collaboration much easier.
In this article, you will learn how to set up Storybook and use it to test components in a React project for interaction and visual regression issues. You will also learn how to integrate Storybook with CircleCI to automate UI testing and documentation, as well as deploy and share with Chromatic to ensure a more efficient development workflow.
Prerequesites
To follow through with this article, you will need the following:
- A knowledge of React.
- A Github account.
- A CircleCI account.
- A Chromatic account.
Pushing existing React project to GitHub
To successfully set up Storybook for testing and documentation, your project must be version-controlled with Git and pushed to a GitHub repository.
Note: This tutorial uses a product card component built with React as an example. You can either follow along with this project or use any other project of your choice.
To get started, clone the project repository using the following command:
git clone https://github.com/CIRCLECI-GWP/storybook-project
cd storybook-project
git checkout new-branch
Note: The master
branch of this project already has Storybook and CircleCI fully implemented. That is why you are asked to start on the new-branch
branch
Next, initialize Git and push project to your GitHub repository by following the steps outlined in the linked blog post.
With your project now pushed to a GitHub repository, it is time to install and run Storybook.
Setting up Storybook
Storybook offers a variety of features, including the ability to create stories for components. One notable function of stories is that they allow the visualization of different states of a component, making React development and testing more efficient.
To get started with creating stories, you first need to set up Storybook. Run the following command in your terminal:
npm create storybook@latest
This command will prompt you to select how you want to use Storybook. Since you need it for both documentation and testing, select both options by pressing the ‘a’ key and press Enter.
Click Enter will start the installation process. This will take a couple of minutes to install Storybook as well as its dependencies. Once successfully installed, a default storybook setup wizard will be opened in your browser. Where Storybook fails to start automatically on your browser, enter the url: http://localhost:6006/ to start Storybook.
Next, head over to your project folder (storybook-project
). Inside it, go to the Component
folder and create a file named project.stories.jsx
.
Within your project.stories.jsx
file, input the following code:
import React from 'react'
import Project from './Project'
import ImageOne from '../assets/apple-watch.jpg'
import ImageTwo from '../assets/headphone.jpg'
import ImageThree from '../assets/game-pad.jpg'
export default {
title: 'Components/Project',
component: Project,
};
const Template = (args) => <Project {...args} />
export const Default =() => (
<div style={{ display: 'flex', gap: '20px', flexWrap: 'wrap'}}>
<Project
image={ImageOne}
name="Smart Watch"
description="Stylish and versatile smartwatch designed to keep you connected and on track."
price="$500"
bgColor="rgb(40, 71, 40)"
/>
<Project
image={ImageTwo}
name="Headphone"
description="Premium wireless headphones delivering immersive sound and all-day comfort."
price="$300"
bgColor="rgb(30, 113, 190)"
/>
<Project
image={ImageThree}
name="Game pad"
description="Ergonomic game pad designed for precision control and an enhanced gaming experience."
price="$700"
bgColor="rgb(114, 6, 114)"
/>
</div>
);
export const SingleProduct = Template.bind({});
SingleProduct.args = {
image:ImageOne,
name:"Smart Watch",
description:"Stylish and versatile smartwatch designed to keep you connected and on track.",
price:"$500",
bgColor:"rgb(40, 71, 40)",
}
export const DiscountedProduct = Template.bind({});
DiscountedProduct.args = {
image:ImageTwo,
name:"Headphone (Discount)",
description:"Premium wireless headphones delivering immersive sound and all-day comfort.",
price:"$250 (2% off)",
bgColor:"rgb(169, 180, 247)",
}
export const OutOfStockProduct = Template.bind({});
OutOfStockProduct.args = {
image:ImageThree,
name:"Game pad (out of stock)",
description:"Ergonomic game pad designed for precision control and an enhanced gaming experience.",
price:"sold",
bgColor:"rgb(146, 35, 103)",
}
The configuration above showcases different state of your product card project.
- The
export default {}
method registers the project in Storybook. - The
const template (args) => <Project {…args} />
function allows props to be passed dynamically for different variations of your product card. - Four stories are defined for your product card project in Storybook:
- The
Default
story renders multiple product cards usingexport const Default = () => <Project/>
. - The
SingleProduct
story reuses the template withexport const SingleProduct = Template.bind({});
, specifying props usingSingleProduct.args = { ... };
. - The
DiscountedProduct
andOutOfStockProduct
stories follow the same approach as theSingleProduct
story to represent different product states.
Finally, head over to your Storybook dashboard in your browser to view updated changes.
There you have it! Storybook is now set up in your product card project.
Automating documentation with Storybook docs
Aside from creating stories, another powerful feature of Storybook is its ability to generate documentation for your project. It does this by providing auto-generated documentation for components, making it a lot easier for teams to understand and use UI components consistently.
To enable Storybook for component auto-documentation and customize it with MDX, navigate to the preview
block in your .storybook/preview.js
file and add the following script:
tags: ['autodocs']
This will automatically generate documentation for all your stories.
Note: You can enable documentation for specific components by going to their ‘.stories.js’ file and adding ‘tags: [‘autodocs’]’ to it
Next, create a project_list.mdx
file in your Component
folder.
In your project_list.mdx
file, add the following code:
import { Meta, Story } from '@storybook/addon-docs';
import * as Stories from './project.stories.jsx';
<Meta of={Stories} />
# Product List component
This components displays a list of products.
## Properties
<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>name</code></td>
<td>string</td>
<td>The name of the product.</td>
</tr>
<tr>
<td><code>price</code></td>
<td>string</td>
<td>The price of the product.</td>
</tr>
<tr>
<td><code>image</code></td>
<td>string</td>
<td>The image source for the product.</td>
</tr>
<tr>
<td><code>description</code></td>
<td>string</td>
<td>A brief description of the product.</td>
</tr>
<tr>
<td><code>bgColor</code></td>
<td>string</td>
<td>Background color for styling.</td>
</tr>
</tbody>
</table>
## Example Usage
### Default Variant
<Story name="Default" of={Stories.Default} />
### Single Variant
<Story name="SingleProduct" of={Stories.SingleProduct} />
### Discounted Variant
<Story name="DiscountedProduct" of={Stories.DiscountedProduct} />
### OutOfStock Variant
<Story name="OutOfStockProduct" of={Stories.OutOfStockProduct} />
The above file shows a basic MDX based storybook documentation for your product cards.
- The
@storybook/addon-docs
allows importingMeta
andStory
, referencing all exported stories fromproject.stories.jsx
. - The
<Meta of={Stories} />
tag links the MDX documentation to the stories defined in yourproject.stories.jsx
file ensuring they are properly defined within the storybook Docs tab. - The file also includes a property section with a
<table></table>
tag, listing the various props used in your product card project, their types and their description. - Lastly, the
<Story/>
component specified in theExample Usage
section renders the various stories from yourproject.stories.jsx
file, allowing users to preview different states of the Product card within the documentation.
Finally, head over to your storybook dashboard in your browser to view updated changes.
There you have it! Your project_list.mdx
file now provides custom documentation for your component alongside Storybook AutoDocs.
Writing and running test in Storybook
Testing is another key feature of Storybook, as it enables developers validate component behavour and maintain UI consistency. While Storybook offers various testing approaches, this tutorial will focus on writing interaction and visual regression tests.
Note: In this section, you will start by writing interaction tests to verify component behavior. Visual regression testing, which helps catch unintended UI changes, will be covered in a later section.
Interaction testing with Storybook
Storybook interaction testing checks out how UI components behave when users interact with them in ways such as clicking of buttons, filling out forms or navigating through various states. it ensures that functional aspects of a UI component work as expected by simulating user interactions within Storybook stories.
Note: The product card project used in this tutorial includes an “Add to Cart” button. This button will be used to demonstrate Storybook’s interaction testing capabilities.
To enable interaction testing within your product card project, start by installing the Storybook test runner, which enables you run tests automatically:
npm install @storybook/test-runner --save-dev
Next, open your package.json file and add the following script in the scripts
object block :
"test-storybook": "test-storybook"
With storybook test runner now installed and activated, continue your setup by entering the following command in your terminal:
npm install @storybook/testing-library @storybook/jest --save-dev
This command installs the necessary dependencies for Storybook interaction testing.
Next, navigate to your project.stories.jsx
file and update it with the following import statements:
import { within } from '@storybook/testing-library';
import userEvent from '@testing-library/user-event';
import { expect } from '@storybook/jest';
Next, update your project.stories.jsx
file with the following code:
// Interaction test for Add To Cart functionality
export const AddToCartTest = Default.bind({});
AddToCartTest.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
console.log("Rendered HTML:", canvasElement.innerHTML);
// Find the 'Add To Cart' button(s)
const addToCartButtons = canvasElement.querySelectorAll('.cart');
if (addToCartButtons.length === 0) {
throw new Error('Add To Cart button(s) not found');
}
// Simulate clicking the first 'Add To Cart' button
await userEvent.click(addToCartButtons[0]);
let logMessage = "";
const originalConsoleLog = console.log;
console.log = (message) => {
logMessage = message;
originalConsoleLog(message);
};
await userEvent.click(addToCartButtons[0]);
expect(logMessage).toBe('Smart Watch added to cart');
console.log = originalConsoleLog
};
The above code demonstrates a basic interaction test defined for the Add To Cart
functionality of your product card project.
- The
@storybook/testing-library
package allows importing ofwithin
, a method for querying elements inside the Storybook canvas. The@testing-library/user-event
package providesuserEvent
making it possible to simulate user actions. Lastly,@storybook/jest
importsexpect
, which allows assertions in your interaction test. - The statement
export const AddToCartTest = Default.bind({});
creates the interaction test by binding it to theDefault
story defined instories.jsx
Finally, execute your interaction test in the terminal by running the following command:
npm run test-storybook
With this, you now have Storybook running interaction tests locally on your product card project.
Simulating a failed interaction test
Now that Storybook is successfully running interaction tests on your product card project, what happens if a wrong assertion is made?
Currently, your tests expects console.log
to output ‘Smart Watch added to cart’. Let us change the expected message to something incorrect to see how Storybook handles test failures.
To your interaction test section in your project.stories.jsx
file, change the following code line:
expect(logMessage).toBe('Smart Watch added to cart');
To this:
expect(logMessage).toBe('Wrong message');
Next, run interaction test locally using the following command:
npm run test-storybook
You will get a failed test, demonstrating how Storybook detects incorrect assertions.
Finally, revert the change to allow the test run successfully.
Integrating Storybook into CircleCI
With Storybook now set up to create custom stories, automate documentation, and run interaction tests in your product card, you can take it a step further by automating the entire process using CircleCI. This means CircleCI will build Storybook and run interaction tests automatically for your product card project.
To integrate CircleCI with Storybook, start by creating a .circleci
folder in your storybook-project
directory:
mkdir .circleci
Next, create a config.yml
file within your .circleci
folder and add the following scripts:
{% raw %}
version: 2.1
jobs:
build-storybook:
docker:
- image: cimg/node:20.10
steps:
- checkout
- restore_cache:
keys:
- node-deps-{{ checksum "package-lock.json" }}
- run:
name: Install dependencies
command: npm ci
- save_cache:
key: node-deps-{{ checksum "package-lock.json" }}
paths:
- node_modules
- run:
name: Build Storybook
command: npm run build-storybook
- persist_to_workspace:
root: .
paths:
- storybook-static
run-interaction-tests:
docker:
- image: cimg/node:20.10
steps:
- checkout
- restore_cache:
keys:
- node-deps-{{ checksum "package-lock.json"}}
- run:
name: install dependencies
command: npm ci
- attach_workspace:
at: .
- run:
name: Start Storybook
command: npx storybook dev -p 6006 --host 0.0.0.0
background: true
- run:
name: Install Playwright
command: npx playwright install --with-deps
- run:
name: Run Storybook interaction tests
command: npm run test-storybook
workflows:
version: 2
test_and_build:
jobs:
- build-storybook
- run-interaction-tests:
requires:
- build-storybook
For the CircleCI configuration file above:
- The
build-storybook
job usescimg/node: 20.10
as its Docker image. It installs necessary dependencies using thenpm ci
command, subsequently building storybook and saving the static output to the workspace. - Like the
build-storybook
job, therun-interaction-test
job also usescimg/node 20.10
as its Docker image. Necessary dependencies are installed using thenpm ci
command. Since interaction tests require storybook to run before commencing, thenpx storybook
command enables this. Playwright is installed and the interaction test finally gets executed using thenpm run test-storybook
command. - The workflow section uses the
requires: - build-storybook
method to ensure interaction tests only run after the storybook build is completed.
Commit and push changes to GitHub.
Setting up your project on CircleCI
To continue with your CircleCI integration, log in to your CircleCI account.
Navigate to your CircleCI project dashboard, search for your project repository name, and click Set up Project.
Next, select the Fastest option and enter the branch name that contains your CircleCI config file (in this case, your main branch). Click Set Up Project to trigger a pipeline build.
Once your setup is complete, your pipeline will be automatically triggered. If it’s not automatically triggered, click the Trigger Pipeline button. While the build may take some time, it should finish successfully.
Click the build-storybook
workflow to view the pipeline details.
Click the
run-interaction-tests
workflow to view the pipeline details.
With CircleCI now set up in your Product Card project, subsequent code changes and commits will automatically trigger a pipeline build that sets up Storybook and runs interaction tests in CircleCI.
Visual regression tests, deployment and sharing storybook with chromatic
Aside from interaction tests, Storybook also supports visual regression tests. However, it doesn’t perform these tests directly; instead, it utilizes Chromatic for this purpose.
Visual regression testing is a technique used to compare snapshots or images of UIs before and after changes are made. This allows developers to evaluate whether changes are intended or not, thereby helping maintain a consistent and high-quality interface.
To get started with running visual regression tests in your Product Card using Chromatic, head over to the Chromatic website and choose the project you want to run visual regression tests on from GitHub.
This will allow Chromatic to provide you with an installation package and a project token.
Head over to your terminal and input the following command:
npm install --save-dev chromatic
This command will install Chromatic for your project.
Next, publish your Storybook to Chromatic by entering the following command:
npx chromatic --project-token="your chromatic project token"
This will prompt Chromatic to run a series of tests and publish your stories.
Check your terminal for the Chromatic deployment URL and follow the View your Storybook link to access your published Storybook.
That is it! Chromatic now runs visual regression tests on your product card project. This means that whenever visual changes are made to your project, Chromatic will automatically capture snapshots of your components and compare them with previous UI versions. Additionally, you have successfully published your Storybook to Chromatic.
Note: During the process of publishing Storybook to Chromatic, your Chromatic project token was added to your ‘package.json’ file. However, before automating the deployment and publishing of your stories to Chromatic, ensure you delete the token. An exposed token poses a security risk, potentially allowing unauthorized deployments or modifications to your project.
Automating the process
To automate visual regression testing, sharing and publishing of your stories to Chromatic with CircleCI, navigate to your package.json
file and delete the below script:
"chromatic": "npx chromatic --project-token=your project token"
Next, head over to your CircleCI project dashboard, navigate to Project Settings > Environment Variables, click Add Environment Variable, and enter a name along with the token of your Chromatic project.
Click Add Environment Variable, then head over to your .circleci/config.yml
file and update it by adding the following script below. You can confirm the updated changes using the complete config file.
publish-to-chromatic:
docker:
- image: cimg/node:20.10
steps:
- checkout
- restore_cache:
keys:
- node-deps-{{ checksum "package-lock.json" }}
- run:
name: Install dependencies
command: npm ci
- run:
name: Publish Storybook to Chromatic
command: npx chromatic --project-token=$CHROMATIC_PROJECT_TOKEN
workflows:
version: 2
test_and_build:
jobs:
- publish-to-chromatic:
requires:
- run-interaction-tests
In the above CircleCI configuration:
- The
publish-to-chromatic
job usescimg/node: 20.10
as its Docker image. Necessary dependencies are installed using the commandnpm ci
. Thenpx chromatic
command ensures Storybook is published to Chromatic while authenticating the Chromatic project token via$CHROMATIC_PROJECT_TOKEN
. - The workflow section uses
requires: - run-interaction-tests
to ensure Storybook is only published after interaction tests pass. This enforces a reliable testing process before deployment.
Finally, commit and push your changes to GitHub to trigger a pipeline build.
Click the publish-to-chromatic
workflow to view the pipeline details.
Conclusion
In this tutorial, you learned how to streamline debugging, testing, and documentation of components by integrating Storybook into your projects. You also explored how to publish stories to Chromatic for visual regression testing and automate the entire process using CircleCI, a powerful continuous integration and deployment tool.
By incorporating Storybook, Chromatic, and CircleCI into your workflow, you ensure better UI consistency, automated documentation, and reliable testing. Moving forward, endeavor to implement all you have learned on new projects and also existing ones.
You can explore the entire code used in this tutorial on GitHub.