TutorialsApr 15, 202512 min read

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

Daniel Efe

Front-end Developer

Developer A works on an intermediate-level project.

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:

  1. A knowledge of React.
  2. A Github account.
  3. A CircleCI account.
  4. 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.

Select how to use Storybook

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.

Storybook default setup wizard

Next, head over to your project folder (storybook-project). Inside it, go to the Component folder and create a file named project.stories.jsx.

Story file structure

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 using export const Default = () => <Project/>.
  • The SingleProduct story reuses the template with export const SingleProduct = Template.bind({});, specifying props using SingleProduct.args = { ... };.
  • The DiscountedProduct and OutOfStockProduct stories follow the same approach as the SingleProduct story to represent different product states.

Finally, head over to your Storybook dashboard in your browser to view updated changes.

Product card stories

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.

Mdx File

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 importing Meta and Story, referencing all exported stories from project.stories.jsx.
  • The <Meta of={Stories} /> tag links the MDX documentation to the stories defined in your project.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 the Example Usage section renders the various stories from your project.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.

Mdx Documentation

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 of within, a method for querying elements inside the Storybook canvas. The @testing-library/user-event package provides userEvent making it possible to simulate user actions. Lastly, @storybook/jest imports expect, which allows assertions in your interaction test.
  • The statement export const AddToCartTest = Default.bind({}); creates the interaction test by binding it to the Default story defined in stories.jsx

Finally, execute your interaction test in the terminal by running the following command:

npm run test-storybook

Run interaction tests

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.

Failed interaction test

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 uses cimg/node: 20.10 as its Docker image. It installs necessary dependencies using the npm ci command, subsequently building storybook and saving the static output to the workspace.
  • Like the build-storybook job, the run-interaction-test job also uses cimg/node 20.10 as its Docker image. Necessary dependencies are installed using the npm ci command. Since interaction tests require storybook to run before commencing, the npx storybook command enables this. Playwright is installed and the interaction test finally gets executed using the npm 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.

Search project repository

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.

Select fastest option

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.

Storybook project success

Click the build-storybook workflow to view the pipeline details.

Build Storybook workflow Click the run-interaction-tests workflow to view the pipeline details.

Run interaction tests workflow

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.

Choose project for visual regression test

This will allow Chromatic to provide you with an installation package and a project token.

Chromatic installation package

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.

Chromatic tests and publish stories

Check your terminal for the Chromatic deployment URL and follow the View your Storybook link to access your published Storybook.

View Storybook in Chromatic

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.

Add Chromatic project token

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 uses cimg/node: 20.10 as its Docker image. Necessary dependencies are installed using the command npm ci. The npx 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.

Chromatic pipeline build

Click the publish-to-chromatic workflow to view the pipeline details.

Publish Storybook to Chromatic

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.


Daniel Efe is a front-end developer and technical writer with expertise in JavaScript, React, and technical documentation. Daniel specializes in building web applications and creating accessible, comprehensive content designed to educate developers across all skill levels.

Copy to clipboard