Docs
Storybook Docs

可访问性测试

Watch a video tutorial

可访问性是让网站包容所有人的实践。这意味着支持以下要求:键盘导航、屏幕阅读器支持、触摸友好、可用的颜色对比度、减少运动和缩放支持。

¥Accessibility is the practice of making websites inclusive to all. That means supporting requirements such as: keyboard navigation, screen reader support, touch-friendly, usable color contrast, reduced motion, and zoom support.

可访问性测试根据基于 WCAG 规则和其他行业认可的最佳实践的一组启发式方法审核渲染的 DOM。它们充当 QA 的第一行,以捕获明显的可访问性违规行为。

¥Accessibility tests audit the rendered DOM against a set of heuristics based on WCAG rules and other industry-accepted best practices. They act as the first line of QA to catch blatant accessibility violations.

使用 a11y 插件进行可访问性检查

¥Accessibility checks with a11y addon

Storybook 提供官方 a11y 插件。由 Deque 的 axe-core 提供支持,可自动赶上 57% 的 WCAG 问题

¥Storybook provides an official a11y addon. Powered by Deque's axe-core, which automatically catches up to 57% of WCAG issues.

设置 a11y 插件

¥Set up the a11y addon

如果你想使用 addon 检查故事的可访问性,你需要安装它并将其添加到你的 Storybook 中。

¥If you want to check accessibility for your stories using the addon, you'll need to install it and add it to your Storybook.

运行以下命令来安装插件。

¥Run the following command to install the addon.

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

更新你的 Storybook 配置(在 .storybook/main.js|ts 中)以包含辅助功能插件。

¥Update your Storybook configuration (in .storybook/main.js|ts) to include the accessibility addon.

.storybook/main.ts
// 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;

启动你的 Storybook,你将在 UI 中看到一些明显的差异。一个新的工具栏图标和可访问性面板,你可以在其中检查测试结果。

¥Start your Storybook, and you will see some noticeable differences in the UI. A new toolbar icon and the accessibility panel where you can inspect the results of the tests.

Storybook accessibility addon running

工作原理

¥How it works

Storybook 的 a11y 插件在选定的故事上运行 Axe。允许你在开发过程中捕获和修复可访问性问题。例如,如果你正在处理按钮组件并包含以下故事集:

¥Storybook's a11y addon runs Axe on the selected story. Allowing you to catch and fix accessibility issues during development. For example, if you’re working on a button component and included the following set of stories:

Button.stories.ts|tsx
import type { Meta, StoryObj } from '@storybook/react';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
};
 
export default meta;
type Story = StoryObj<typeof Button>;
 
// This is an accessible story
export const Accessible: Story = {
  args: {
    primary: false,
    label: 'Button',
  },
};
 
// This is not
export const Inaccessible: Story = {
  args: {
    ...Accessible.args,
    backgroundColor: 'red',
  },
};

循环浏览两个故事,你将看到 Inaccessible 故事包含一些需要修复的问题。打开可访问性面板中的违规选项卡提供了可访问性问题的清晰描述和解决问题的指南。

¥Cycling through both stories, you will see that the Inaccessible story contains some issues that need fixing. Opening the violations tab in the accessibility panel provides a clear description of the accessibility issue and guidelines for solving it.

Storybook accessibility addon running

配置

¥Configure

开箱即用,Storybook 的可访问性插件包含一组涵盖大多数问题的可访问性规则。你还可以微调 插件配置 或覆盖 Axe 的规则集 以最适合你的需求。

¥Out of the box, Storybook's accessibility addon includes a set of accessibility rules that cover most issues. You can also fine-tune the addon configuration or override Axe's ruleset to best suit your needs.

全局 a11y 配置

¥Global a11y configuration

如果你需要关闭可访问性规则或修改所有故事中的设置,你可以将以下内容添加到你的 storybook/preview.js|ts

¥If you need to dismiss an accessibility rule or modify its settings across all stories, you can add the following to your storybook/preview.js|ts:

.storybook/preview.ts
// Replace your-framework with the framework you are using (e.g., react, vue3)
import { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    a11y: {
      // Optional selector to inspect
      element: '#storybook-root',
      config: {
        rules: [
          {
            // The autocomplete rule will not run based on the CSS selector provided
            id: 'autocomplete-valid',
            selector: '*:not([autocomplete="nope"])',
          },
          {
            // Setting the enabled option to false will disable checks for this particular rule on all stories.
            id: 'image-alt',
            enabled: false,
          },
        ],
      },
      // Axe's options parameter
      options: {},
      // Optional flag to prevent the automatic check
      manual: true,
    },
  },
};
 
export default preview;

组件级 a11y 配置

¥Component-level a11y configuration

你还可以为组件的所有故事自定义自己的规则集。更新故事的默认导出并添加具有所需配置的参数:

¥You can also customize your own set of rules for all stories of a component. Update your story's default export and add a parameter with the required configuration:

MyComponent.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
 
import { MyComponent } from './MyComponent';
 
const meta: Meta<typeof MyComponent> = {
  component: MyComponent,
  parameters: {
    a11y: {
      // Optional selector to inspect
      element: '#storybook-root',
      config: {
        rules: [
          {
            // The autocomplete rule will not run based on the CSS selector provided
            id: 'autocomplete-valid',
            selector: '*:not([autocomplete="nope"])',
          },
          {
            // Setting the enabled option to false will disable checks for this particular rule on all stories.
            id: 'image-alt',
            enabled: false,
          },
        ],
      },
      options: {},
      manual: true,
    },
  },
};
 
export default meta;

故事级 a11y 配置

¥Story-level a11y configuration

通过更新故事以包含新参数,在故事级别自定义 a11y 规则集:

¥Customize the a11y ruleset at the story level by updating your story to include a new parameter:

MyComponent.stories.ts|tsx
import type { Meta, StoryObj } from '@storybook/react';
 
import { MyComponent } from './MyComponent';
 
const meta: Meta<typeof MyComponent> = {
  component: MyComponent,
};
 
export default meta;
type Story = StoryObj<typeof MyComponent>;
 
export const ExampleStory: Story = {
  parameters: {
    a11y: {
      element: '#storybook-root',
      config: {
        rules: [
          {
            // The autocomplete rule will not run based on the CSS selector provided
            id: 'autocomplete-valid',
            selector: '*:not([autocomplete="nope"])',
          },
          {
            // Setting the enabled option to false will disable checks for this particular rule on all stories.
            id: 'image-alt',
            enabled: false,
          },
        ],
      },
      options: {},
      manual: true,
    },
  },
};

如何禁用 a11y 测试

¥How to disable a11y tests

通过分别将以下参数添加到故事的导出或组件的默认导出来禁用故事或组件的可访问性测试:

¥Disable accessibility testing for stories or components by adding the following parameter to your story’s export or component’s default export respectively:

MyComponent.stories.ts|tsx
import type { Meta, StoryObj } from '@storybook/react';
 
import { MyComponent } from './MyComponent';
 
const meta: Meta<typeof MyComponent> = {
  component: MyComponent,
};
 
export default meta;
type Story = StoryObj<typeof MyComponent>;
 
export const NonA11yStory: Story = {
  parameters: {
    a11y: {
      // This option disables all a11y checks on this story
      disable: true,
    },
  },
};

使用测试运行器自动执行可访问性测试

¥Automate accessibility tests with test runner

检查可访问性的最准确方法是在真实设备上手动检查。但是,你可以使用自动化工具来捕获常见的可访问性问题。例如,Axe 平均会自动向上追赶到 57% 的 WCAG 问题

¥The most accurate way to check accessibility is manually on real devices. However, you can use automated tools to catch common accessibility issues. For example, Axe, on average, catches upwards to 57% of WCAG issues automatically.

这些工具通过根据 WCAG 规则和其他行业认可的最佳实践审核渲染的 DOM 是否符合启发式方法来工作。然后,你可以使用 Storybook 测试运行器axe-playwright 将这些工具集成到你的测试自动化管道中。

¥These tools work by auditing the rendered DOM against heuristics based on WCAG rules and other industry-accepted best practices. You can then integrate these tools into your test automation pipeline using the Storybook test runner and axe-playwright.

设置

¥Setup

要使用测试运行器启用可访问性测试,你需要采取其他步骤来正确设置它。我们建议你在继续进行其余所需配置之前先完成 测试运行器文档

¥To enable accessibility testing with the test runner, you will need to take additional steps to set it up properly. We recommend you go through the test runner documentation before proceeding with the rest of the required configuration.

运行以下命令安装所需的依赖。

¥Run the following command to install the required dependencies.

npm install axe-playwright --save-dev

在你的 Storybook 目录中添加一个新的 配置文件,其中包含以下内容:

¥Add a new configuration file inside your Storybook directory with the following inside:

.storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
import { injectAxe, checkA11y } from 'axe-playwright';
 
/*
 * See https://storybook.js.org/docs/writing-tests/test-runner#test-hook-api
 * to learn more about the test-runner hooks API.
 */
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;

preVisitpostVisit 是方便的钩子,可让你扩展测试运行器的默认配置。阅读有关它们 此处 的更多信息。

¥preVisit and postVisit are convenient hooks that allow you to extend the test runner's default configuration. Read more about them here.

当你执行测试运行器(例如,使用 yarn test-storybook)时,它将运行可访问性审核以及你可能为每个组件故事配置的任何 组件测试

¥When you execute the test runner (for example, with yarn test-storybook), it will run the accessibility audit and any component tests you might have configured for each component story.

它通过从故事的根元素开始遍历 DOM 树来开始检查问题,并根据遇到的问题生成详细报告。

¥It starts checking for issues by traversing the DOM tree starting from the story's root element and generates a detailed report based on the issues it encountered.

Accessibility testing with the test runner

使用测试运行器的 A11y 配置

¥A11y config with the test runner

测试运行器提供 辅助方法,允许访问故事的信息。你可以使用它们来扩展测试运行器的配置并提供你可能拥有的特定故事的其他选项。例如:

¥The test runner provides helper methods, allowing access to the story's information. You can use them to extend the test runner's configuration and provide additional options you may have for a specific story. For example:

.storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
import { getStoryContext } from '@storybook/test-runner';
 
import { injectAxe, checkA11y, configureAxe } from 'axe-playwright';
 
/*
 * See https://storybook.js.org/docs/writing-tests/test-runner#test-hook-api
 * to learn more about the test-runner hooks API.
 */
const config: TestRunnerConfig = {
  async preVisit(page) {
    await injectAxe(page);
  },
  async postVisit(page, context) {
    // Get the entire context of a story, including parameters, args, argTypes, etc.
    const storyContext = await getStoryContext(page, context);
 
    // Apply story-level a11y rules
    await configureAxe(page, {
      rules: storyContext.parameters?.a11y?.config?.rules,
    });
 
    await checkA11y(page, '#storybook-root', {
      detailedReport: true,
      detailedReportOptions: {
        html: true,
      },
    });
  },
};
 
export default config;

使用测试运行器禁用 a11y 测试

¥Disable a11y tests with the test runner

此外,如果你已经对任何特定故事进行了 禁用可访问性 测试,你还可以配置测试运行器以避免对其进行测试。例如:

¥Additionally, if you have already disabled accessibility tests for any particular story, you can also configure the test runner to avoid testing it as well. For example:

.storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
import { getStoryContext } from '@storybook/test-runner';
 
import { injectAxe, checkA11y } from 'axe-playwright';
 
/*
 * See https://storybook.js.org/docs/writing-tests/test-runner#test-hook-api
 * to learn more about the test-runner hooks API.
 */
const config: TestRunnerConfig = {
  async preVisit(page) {
    await injectAxe(page);
  },
  async postVisit(page, context) {
    // Get the entire context of a story, including parameters, args, argTypes, etc.
    const storyContext = await getStoryContext(page, context);
 
    // Do not run a11y tests on disabled stories.
    if (storyContext.parameters?.a11y?.disable) {
      return;
    }
    await checkA11y(page, '#storybook-root', {
      detailedReport: true,
      detailedReportOptions: {
        html: true,
      },
    });
  },
};
 
export default config;

基于浏览器和基于 linter 的可访问性测试有什么区别?

¥What’s the difference between browser-based and linter-based accessibility tests?

基于浏览器的可访问性测试(如 Storybook 中的测试)会评估渲染的 DOM,因为这可为你提供最高的准确性。审核尚未编译的代码与真实代码相差一步,因此你无法捕捉到用户可能遇到的所有内容。

¥Browser-based accessibility tests, like those found in Storybook, evaluate the rendered DOM because that gives you the highest accuracy. Auditing code that hasn't been compiled yet is one step removed from the real thing, so you won't catch everything the user might experience.

了解其他 UI 测试

¥Learn about other UI tests