A guide to add auto accessibility check with storybook
Tags: javascript, storybook, a11y, docker
Published: 2021-03-10 20:07:26

---
title: A guide to add auto accessibility check with storybook
published: true
description: 
tags: javascript, storybook, accessibility, docker
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tc903ogacotqsn6359hq.jpg
canonical_url: https://blog.atrera.com/javascript/pipeline/2021/03/10/a-guide-to-add-auto-accessibility-check-with-storybook.html
---

It's always good to have a proper accessibility check when you are developing front-end components.

When developing with storybook, you can even get more: a pipeline to do auto checks for accessibility!

<!--more-->

## First, install a11y-addon

Storybook has an official addon called `@storybook/addon-a11y`, you can view more details from the [official guide](https://storybook.js.org/addons/@storybook/addon-a11y).

After installed, you can see a tab inside the storybook, shows various accessibility errors or warnings.

![addon](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hx5xysx3yogbfn2vtvd.jpg)

Hurray!!

### Do this even better

One shortcoming of this approach is, the addon itself doesn't throw any errors. When multiple developers are working on several components, **it is easy to overlook those a11y warnings** (or even ignore it on purpose).

How to check it automatically and throw an error when pushing to the repository?

## A pipeline to automatically check accessibility

Here I am going to use [my storybook demo](https://github.com/snowleo208/design-system-demo) in Github with CircleCI. 

>If you aren't familiar with the CircleCI pipeline, see [official documentation](https://circleci.com/docs/2.0/language-javascript/).

First, you have to install `@storybook/addon-storyshots-puppeteer`, it is an official addon to add automatic Snapshot Testing for Storybook. 

```bash
yarn add @storybook/addon-storyshots-puppeteer puppeteer @storybook/addon-storyshots --save-dev
```

Follow the [official guide](https://www.npmjs.com/package/@storybook/addon-storyshots-puppeteer), create a new file `storyshots.spec.js`.

```jsx
// From https://www.npmjs.com/package/@storybook/addon-storyshots-puppeteer

import initStoryshots from '@storybook/addon-storyshots';
import { axeTest } from '@storybook/addon-storyshots-puppeteer';

initStoryshots({ suite: 'A11y checks', test: axeTest() });
```

Add a new command in `package.json`.

```json
"test:a11y": "jest storyshot"
```

Now you can run storybook (`yarn storybook`) and run `yarn test:a11y` afterwards to test your settings. If your tests run correctly, you should see the test results.

For example, in my `design-system-demo`, I saw errors from two components. It also includes a link of how to solve it ([example](https://dequeuniversity.com/rules/axe/3.5/color-contrast?application=axeAPI)).

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ybshm9rz5da2ory2it1.jpg)

After the `storyshots` work locally, it's time to create a pipeline!

## Create a pipeline

> Warning: this section is long, make sure you have some knowledge on docker, docker-compose and CI.

### Dockerfile

First, we need to run both `storybook` and `storyshots` in pipeline simultaneously. But one step in the pipeline only did one thing, how can we achieve that? **Docker**.

Create a docker file for storybook, and use `docker-compose` to create another service to run accessibility tests.

```dockerfile
FROM node:14.16.0-alpine3.12

WORKDIR /opt/app/

COPY package.json ./
COPY yarn.lock ./

# Install app dependencies
RUN yarn install --frozen-lockfile
COPY . .

EXPOSE 6006

# Run storybook
CMD ["yarn", "storybook"]
```

Create a `Dockerfile` and `.dockerignore` file to ignore `node_modules` in your harddisk. This docker file is copying all files from local to docker image and runs the storybook in `6006` at the end.

### docker-compose

Then `docker-compose`, create a file named `docker-compose.yml`:

```yml
version: "3"

services:
  web:
    volumes:
      - app-content:/opt/app/
    build: .
    logging:
      driver: none
    networks:
      defaults:
    ports:
      - "6006:6006"
    tty: false
  accessibility:
    image: "buildkite/puppeteer:7.1.0"
    networks:
      defaults:
    working_dir: /opt/app/
    volumes:
      - app-content:/opt/app/
    depends_on:
      - web
    command: /bin/sh -c "npx wait-on http://web:6006 && yarn test:a11y"
volumes:
  app-content:

networks:
  defaults:
    driver: bridge
```

There are two services here: `web` and `accessibility`. `docker-compose` runs two services simultaneously and `accessibility` wait till `6006` port of `web` is ready.

The takeaway of this:
- `networks` setting is important, both needs to be in the same network, otherwise `wait-on` doesn't work
- use `puppeteer` docker image in the accessibility service to save the time of setting headless Chrome and puppeteer
- use `logging: driver: none` to get rid of noises from the `web` service

Then, change the `storybookUrl` of `storyshots`:

```javascript
initStoryshots({
  suite: 'A11y checks',
  test: axeTest({ storybookUrl: 'http://web:6006' }),
});
```

Finally, the pipeline!

### CircleCI

In your pipeline, add a job called `accessibility`.

```yml
  accessibility:
    docker: 
      - image: cimg/base:2021.03-20.04
    steps:
      - checkout
      - setup_remote_docker:
          version: 19.03.13
      - run:
          name: Accessibility tests
          command: |
            set -x
            docker-compose -f ./docker-compose.yml up --exit-code-from accessibility
```

The important stuff here: `--exit-code-from <YOUR-DOCKER-SERVICE-NAME>`. 

By default, CircleCI gets exit code from the image itself, this means, even you have failed tests in `accessibility`, the pipeline is still passed.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ok4kauhpghn396skj5wk.jpg)

By setting this `exit-code-from`, we can control the behaviour of pipeline to get exit code from docker container `accessibility`, i.e. when `accessibility` tests failed, pipeline throws an error.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ajz353unkk4i6xr036vv.jpg)

See, the pipeline failed after setting `exit-code-from`!

One thing that is for CircleCi only, is the `setup_remote_docker` command (see [official doc](https://circleci.com/docs/2.0/building-docker-images/)), which is connecting to remote `docker` service. Otherwise, `docker-compose` doesn't work without `docker`.

Add that to your workflow, for example:

```yml
workflows:
  tests:
    jobs:
      - test
      - accessibility
```

That's all. Enjoy!

Feel free to check [my repo](https://github.com/snowleo208/design-system-demo/) and [my pull request](https://github.com/snowleo208/design-system-demo/pull/2/files) for the a11y pipeline!

## Beware, manual testing is still important!

Automatic test of accessibility is convenient but it can only catch a small number of accessibility issues. 

Some sophisticated issues, e.g. a form that doesn't work well with VoiceOver, still need manual checks.

If you do care about accessibility, remember to do some manual checks!


## Read more
- [Real-time accessibility testing with Storybook](https://medium.com/storybookjs/instant-accessibility-qa-linting-in-storybook-4a474b0f5347)
- [Learn How We Test for Product Accessibility at Netlify](https://www.netlify.com/blog/2020/12/08/how-we-test-for-accessibility-at-netlify/)
- [a11y pipeline demo](https://github.com/snowleo208/design-system-demo/pull/2/files)