返回UI 测试手册
React
章节
  • 简介
  • 视觉
  • 组合
  • 交互
  • 可访问性
  • 用户流程
  • 自动化
  • 工作流
  • 结论

使用 Storybook 进行可访问性测试

通过集成工具快速反馈
此社区翻译尚未更新到最新的 Storybook 版本。请将英文指南中的更改应用到此翻译中,帮助我们更新它。 欢迎提交 Pull Request.

26% 的美国成年人至少有一项残疾。当您改善可访问性时,它会对您当前和未来的客户产生重大影响。它也是法律要求。

检查可访问性的最准确方法是在真实设备上手动检查。但这需要专业知识和大量时间,而这两者在前台团队中都很稀缺。

这就是为什么许多公司现在结合使用自动化和手动测试。自动化以较低的开发人员精力捕捉常见的可访问性问题。手动 QA 保留给需要人工关注的棘手问题。

有很多资源深入探讨了可访问性原则,因此我们在此不赘述。相反,我们将专注于如何使用 Storybook 自动化可访问性测试。这是一种务实的解决方案,可以查找和修复您可能会遇到的大部分问题。

为什么自动化?

在开始之前,让我们检查一下常见的残疾类型:视觉、听觉、行动不便、认知、言语和神经系统。这些用户残疾会产生应用程序需求,例如

  • ⌨ 键盘导航
  • 🗣 屏幕阅读器支持
  • 👆 触摸友好
  • 🎨 足够高的颜色对比度
  • ⚡️ 减少运动
  • 🔍 缩放

过去,您会通过在各种浏览器、设备和屏幕阅读器上检查每个组件来验证这些要求。但这手工操作是不切实际的,因为应用程序有几十个组件并且 UI 正在不断更新。

自动化加快您的工作流程

自动化工具根据 WCAG 规则和其他行业公认的最佳实践,对照一组启发式规则审计渲染的 DOM。它们充当第一道 QA 来捕获明显的无障碍违规。

例如,Axe 平均 自动查找 57% 的 WCAG 问题。这使得团队能够将专家资源集中在需要人工审查的更复杂问题上。

许多团队使用 Axe 库,因为它与大多数现有测试环境集成。例如,Twilio Paste 团队使用 jest-axe 集成。而 Shopify Polaris 和 Adobe Spectrum 团队则使用 Storybook 插件版本。

Storybook 插件在浏览器中运行检查(而不是像 Jest 那样在 jsdom 中),因此可以捕获诸如低对比度之类的问题。但是,这需要您手动验证每个故事。

可访问性测试工作流程

通过在整个开发过程中运行这些检查,您可以缩短反馈周期并更快地修复问题。工作流程看起来是这样的:

  1. 👨🏽‍💻 开发期间:使用 Storybook 一次专注于一个组件。使用 A11y 插件模拟视力缺陷,并在组件级别运行可访问性审计。
  2. QA:将 Axe 审计集成到您的功能测试管道中。对所有组件运行检查以捕获回归。

让我们看看这个工作流程的实际应用。

安装可访问性插件

Storybook 的可访问性插件在活动故事上运行 Axe。它会在面板中可视化测试结果,并突出显示所有存在违规的 DOM 节点。

要安装插件,请运行:yarn add --dev @storybook/addon-a11y。然后,在您的 .storybook/main.js 的插件数组中添加 '@storybook/addon-a11y'

复制
.storybook/main.js
/** @type { import('@storybook/react-vite').StorybookConfig } */
const config = {
  stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
  staticDirs: ['../public'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
+   '@storybook/addon-a11y',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
};
export default config;

边写代码边测试可访问性

我们已经隔离了 Task 组件,并将其所有用例捕获为故事。在开发阶段,您可以循环浏览这些故事以发现可访问性问题。

复制
src/components/Task.stories.jsx
import Task from './Task';

export default {
  component: Task,
  title: 'Task',
  argTypes: {
    onArchiveTask: { action: 'onArchiveTask' },
    onTogglePinTask: { action: 'onTogglePinTask' },
    onEditTitle: { action: 'onEditTitle' },
  },
};

export const Default = {
  args: {
    task: {
      id: '1',
      title: 'Buy milk',
      state: 'TASK_INBOX',
    },
  },
};

export const Pinned = {
  args: {
    task: {
      id: '2',
      title: 'QA dropdown',
      state: 'TASK_PINNED',
    },
  },
};

export const Archived = {
  args: {
    task: {
      id: '3',
      title: 'Write schema for account menu',
      state: 'TASK_ARCHIVED',
    },
  },
};

const longTitleString = `This task's name is absurdly large. In fact, I think if I keep going I might end up with content overflow. What will happen? The star that represents a pinned task could have text overlapping. The text could cut-off abruptly when it reaches the star. I hope not!`;

export const LongTitle = {
  args: {
    task: {
      id: '4',
      title: longTitleString,
      state: 'TASK_INBOX',
    },
  },
};

请注意,该插件发现了两个违规。第一个是“元素必须满足最低颜色对比度阈值”,这特定于 archived 状态。基本上,这意味着任务标题和背景之间的对比度不够。我们可以通过在应用程序的 CSS(位于 src/index.css)中将文本颜色更改为更深的灰色来快速修复它。

复制
src/index.css
.list-item.TASK_ARCHIVED input[type="text"] {
- color: #a0aec0;
+ color: #4a5568;
  text-decoration: line-through;
}

第二个违规“某些 ARIA 角色必须包含在特定的父级中”表示 DOM 结构不正确。Task 组件仅渲染一个 <li> 元素。因此,我们需要更新我们的故事,将组件包装在 <ul> 元素中。

复制
src/components/Task.stories.jsx
import Task from './Task';

export default {
  component: Task,
  title: 'Task',
  argTypes: {
    onArchiveTask: { action: 'onArchiveTask' },
    onTogglePinTask: { action: 'onTogglePinTask' },
    onEditTitle: { action: 'onEditTitle' },
  },
};

/*
*👇 Wraps the component with a custom render function.
* See https://storybook.org.cn/docs/api/csf
* to learn how to use render functions.
*/
export const Default = {
  render: (args) => (
    <ul>
      <Task {...args} />
    </ul>
  ),
  args: {
    task: {
      id: '1',
      title: 'Buy milk',
      state: 'TASK_INBOX',
    },
  },
};

export const Pinned = {
  render: (args) => (
    <ul>
      <Task {...args} />
    </ul>
  ),
  args: {
    task: {
      id: '2',
      title: 'QA dropdown',
      state: 'TASK_PINNED',
    },
  },
};

export const Archived = {
  render: (args) => (
    <ul>
      <Task {...args} />
    </ul>
  ),
  args: {
    task: {
      id: '3',
      title: 'Write schema for account menu',
      state: 'TASK_ARCHIVED',
    },
  },
};

const longTitleString = `This task's name is absurdly large. In fact, I think if I keep going I might end up with content overflow. What will happen? The star that represents a pinned task could have text overlapping. The text could cut-off abruptly when it reaches the star. I hope not!`;

export const LongTitle = {
  render: (args) => (
    <ul>
      <Task {...args} />
    </ul>
  ),
  args: {
    task: {
      id: '4',
      title: longTitleString,
      state: 'TASK_INBOX',
    },
  },
};

您现在可以对所有其他组件重复此过程。

将可访问性测试集成到 Storybook 中可以简化您的开发工作流程。在处理组件时,您不必在不同工具之间切换。您所需的一切都在浏览器中。您甚至可以模拟视觉障碍,例如二色视觉、红绿色盲或蓝黄视觉。

使用测试运行器自动捕获回归

通常,对组件的更改可能会无意中引入新的可访问性问题。为了捕获此类回归,您需要在打开拉取请求之前测试所有故事。但是,可访问性插件仅在您查看故事时运行检查。要一次性测试所有故事,我们可以使用 Storybook 测试运行器。它是一个独立的实用工具(由 JestPlaywright 提供支持),用于检查故事中的渲染错误。

让我们开始配置测试运行器来运行 Axe。我们将从安装 axe-playwright 开始。

复制
yarn add --dev axe-playwright

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

复制
.storybook/test-runner.js
const { injectAxe, checkA11y } = require('axe-playwright');

module.exports = {
  async preVisit(page) {
    await injectAxe(page);
  },
  async postVisit(page) {
    await checkA11y(page, "#storybook-root", {
      detailedReport: true,
      detailedReportOptions: {
        html: true,
      },
    });
  },
};

preVisitpostVisit 是方便的钩子,允许您配置测试运行器以执行其他任务。我们使用这些钩子将 Axe 注入到故事中,然后在渲染完成后运行可访问性测试。

您会注意到传递给 checkA11y 函数的一些选项。我们已将 Axe 设置为从故事的根元素开始,然后向下遍历 DOM 树以检查问题。它还将根据遇到的问题生成详细报告,并输出违反可访问性规则的 HTML 元素列表。

要运行测试,请在一个终端窗口中运行 yarn storybook 启动您的 Storybook,在另一个终端窗口中运行 yarn test-storybook 启动测试运行器。

捕获集成问题

UI 是通过组合组件并将它们与数据和 API 连接起来而构建的。这是潜在的故障点。接下来,我们将使用 Cypress 来测试系统的所有层,以捕获集成问题。

保持你的代码与本章同步。在 GitHub 上查看 d16be74。
这个免费指南对你有帮助吗?发推文表示赞赏,并帮助其他开发者找到它。
下一章
用户流程
验证您的 UI 端到端正常工作
✍️ 在 GitHub 上编辑 – PR 欢迎!
加入社区
7,424开发者及更多
原因为什么选择 Storybook组件驱动的 UI
文档指南教程更新日志遥测
社区插件参与进来博客
展示探索关于
开源软件
Storybook - Storybook 中文

特别感谢 Netlify CircleCI