Post Detail Page

tech

Storybook - 접근성 테스트 자동화하기(addon, test-runner)

테스트 도입 배경

stortybook-ui-test-image
GGF | 접근성 문제(Lighthouse)

프로젝트 성능 최적화 기간에 Accessibility(접근성) 개선을 담당하게 되었는데, 개선 과정에서 페이지별로 contrast ratio(명도 대비), label 태그 및 aria-label 누락 등 다양한 접근성 문제가 발견되었다. 한두 개의 문제가 아니라 여러 페이지에서 수정하고 재적용하는 과정을 거치면서 접근성 테스트 자동화의 필요성을 느꼈다.

이를 개선하기 위해 구현된 컴포넌트에 접근성 테스트를 진행하고, 팀 내부에서 이를 공유할 수 있도록 Storybook의 addontest runner를 활용한 accessibility test를 도입해 보려고 한다.

addon-a11y

💡 addon-a11y는 컴포넌트의 접근성 문제를 자동으로 감지하고, 실시간으로 피드백을 제공한다.

1️⃣ Setup

npm install @storybook/addon-a11y --save-dev

2️⃣ .storybook/main.ts

  • main.js 파일에 @storybook/addon-a11y를 추가하여 Storybook에 애드온을 등록한다.
// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
import type { StorybookConfig } from '@storybook/your-framework';

const config: StorybookConfig = {
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  addons: [
    // Other Storybook addons
    '@storybook/addon-a11y', //👈 The a11y addon goes here
  ],
};

export default config;

3️⃣ Storybook

stortybook-ui-test-image
mingle storybook | Accessibility 오류

Storybook을 실행한 후, 각 컴포넌트 스토리의 Accessibility 패널에서 접근성 문제를 확인할 수 있다.
접근성 검사를 통과하지 못한 경우, Violations 탭에서 오류 내용과 함께 어떤 부분이 잘못됐는지 안내해 준다.

☹︎ 1 Violations
Serious 섹션을 확인해 보면 ErrorMessage 컴포넌트의 텍스트 컬러인 #dc2626의 색상 대비4.094.5:1 요건이 충족되지 않았음을 안내해 준다.(contrast ratio)

test ruuner로 접근성 테스트 자동화하기

1️⃣ Setup

npm install axe-playwright --save-dev
npm install @storybook/test-runner --save-dev

2️⃣ .storybook/test-runner.ts

import type { TestRunnerConfig } from '@storybook/test-runner';
import { injectAxe, checkA11y } from 'axe-playwright';

const config: TestRunnerConfig = {
  async preVisit(page) {
    await injectAxe(page);
  },
  async postVisit(page) {
    await checkA11y(page, '#storybook-root', {
      detailedReport: true,
      detailedReportOptions: {
        html: true,
      },
    });
  },
};

export default config;

3️⃣ package.json

{
  "scripts": {
    "test-storybook": "test-storybook"
  }
}

4️⃣ 테스트 실행

npm run test-storybook
stortybook-ui-test-image

모든 컴포넌트의 테스트가 약 7.6초에 걸쳐 진행되었음을 볼 수 있다. 실행할 때마다 매번 명령어를 입력하는 대신, 테스트를 자동화하여 프로세스를 효율적으로 개선해 보자.

5️⃣ Set up CI to run tests

💡 CI 환경에서 테스트를 실행하도록 테스트 러너를 구성하는 두 가지 방법이 있다

  • Run against deployed Storybooks via Github Actions deployment
  • Run against non-deployed Storybooks

두 방법의 차이는 배포된 Storybook을 대상으로 테스트를 진행하는지, 아니면 배포되지 않은 Storybook을 대상으로 실행하는지에 따라 다르다.

코드가 배포되기 전에 테스트를 실행하면 배포 이전에 문제를 미리 발견할 수 있고, 코드가 푸시(push) 될 때마다 테스트를 실행하여 빠른 피드백과 함께 수정을 할 수 있다는 장점을 고려하여 두 번째 방법으로 진행하기로 했다.

- Run against non-deployed Storybooks(공식문서 제공)

# .github/workflows/storybook-tests.yml

name: 'Storybook Tests'
on: push
jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'
      - name: Install dependencies
        run: yarn
      - name: Install Playwright
        run: npx playwright install --with-deps
      - name: Build Storybook
        run: yarn build-storybook --quiet
      - name: Serve Storybook and run tests
        run: |
          npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
            "npx http-server storybook-static --port 6006 --silent" \
            "npx wait-on tcp:6006 && yarn test-storybook"

테스트가 성공한 후에 chromatic을 배포해야 하므로, chromatic 배포 전에 테스트 작업이 먼저 실행되어야 한다. 이를 위해 아래와 같이 needs 항목을 추가하여 chromatic 작업이 accessiblity-test 작업이 완료된 후에 실행될 수 있도록 수정하여 적용했다.

# .github/workflows/chromatic.yml

name: 'UI TEST'
on: push

jobs:
  accessiblity-test:
  # ...

chromatic:
  needs: accessiblity-test # 테스트 성공 후 실행
  name: Run Chromatic
  runs-on: ubuntu-latest
  steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0
    - uses: actions/setup-node@v4
      with:
        node-version: 20

    - name: Cache dependencies
      id: cache-node
      uses: actions/cache@v4
      with:
        path: '**/node_modules'
        key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
        restore-keys: |
          ${{ runner.os }}-node-

    - name: Install dependencies
      if: steps.cache-node.outputs.cache-hit != 'true'
      run: npm ci
    - name: Run Chromatic
      uses: chromaui/action@latest
      with:
        projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
stortybook-ui-test-image
mingle-ui | accessiblity-test 실패

위와 같이 Accessibility 테스트가 실패할 경우, Chromatic 배포는 실행되지 않는다.
해당 작업이 의존하는 다른 작업이 완료된 후에만 실행될 수 있도록 보장하기 때문에 Accessibility 테스트가 실패하면 전체 워크플로우가 중단된다.

stortybook-ui-test-image
mingle-ui | accessiblity-test, chromatic 배포 성공

위와 같이 모든 테스트가 성공하면, Chromatic 배포가 정상적으로 실행되고 완료된다. 이는 코드 변경 사항이 예상대로 작동하며, 모든 접근성 요구사항을 충족함을 보장한다.