使用 Cypress 测试可访问性

2025-06-07

使用 Cypress 测试可访问性

在上一篇文章,我们创建了一个可访问的 React 折叠面板组件。现在来测试一下。我觉得为这种组件编写单元测试没什么意义。快照测试也没什么用。我认为端到端 (e2e) 测试是最佳选择(但对于测试钩子,我更喜欢单元测试)。

将尝试使用Cypress进行测试。Cypress 使用 Headless Chrome,它有一个 devtools 协议,据说比以前的类似解决方案具有更好的集成度。

安装 Cypress

一旦了解如何启动,Cypress 就很容易上手。我花了……比我预想的要多得多的时间才搞清楚如何开始。他们的文档非常多,很难浏览(至少对我来说)。

有时,过多的文档与过少的文档一样糟糕

我经过一些实验后找到了答案。安装 Cypress

yarn add cypress --dev

首次运行

yarn cypress open

它会创建很多文件。关闭 Cypress 窗口。删除 中的所有内容cypress/integration

添加cypress.json到项目的根目录。

{
  "baseUrl": "http://localhost:3000/"
}

现在,您可以在一个终端中启动开发服务器yarn start,在第二个终端中启动 Cypressyarn cypress open并开始编写测试。

配置 Cypress

但是如何在 CI 中运行测试呢?为此,你需要另一个 npm 包:

yarn add --dev start-server-and-test

改变package.json

"scripts": {
  "test": "yarn test:e2e && yarn test:unit",
  "test:unit": "react-scripts test",
  "cypress-run": "cypress run",
  "test:e2e": "start-server-and-test start http://localhost:3000 cypress-run"
}

快完成了。再添加一个包

yarn add cypress-plugin-tab --dev

cypress/support/index.js

import "./commands";
import "cypress-plugin-tab";

添加.gitignore

cypress/screenshots
cypress/videos

现在我们完成了。

规划测试

我喜欢这部分。

让我们创建测试文件cypress/integration/Accordion.js

describe("Accordion", () => {
  before(() => {
    cy.visit("/");
  });
  // your tests here
});

它将在测试之前打开服务器的根页面(我们将使用开发服务器)。

我们在上一篇文章中看到了WAI-ARIA 创作实践 1.1。

  • 空格或 Enter
    • 当焦点位于折叠部分的手风琴标题上时,展开该部分。
  • Tab
    • 将焦点移动到下一个可聚焦元素。
    • 手风琴面板中所有可聚焦元素均包含在页面 Tab 序列中。

我们只需将其“按原样”复制粘贴到测试文件中即可

  describe("Space or Enter", () => {
    xit("When focus is on the accordion header of a collapsed section, expands the section", () => {});
  });

  describe("Tab", () => {
    xit("Moves focus to the next focusable element.", () => {});
    xit("All focusable elements in the accordion are included in the page Tab sequence.", () => {});
  });
  • describe- 向层次结构中添加一个级别(可选)。
  • xit- 将被跳过的测试,一旦我们实施实际测试,我们将把它更改为it
  • it- 测试,it("name of the test", <body of the test>)

是不是很漂亮?我们可以直接从 WAI-ARIA 规范中复制粘贴测试定义。

编写测试

让我们编写实际测试

首先,我们需要就测试页面的假设达成一致:

  • 只有一个 accordion 组件
  • 其中有三个部分:“第 1 部分”、“第 2 部分”、“第 3 部分”
  • 第 2 部分已展开,其他部分已折叠
  • 第 2 部分有一个链接
  • 手风琴后面有一个按钮

第一个测试:“空格或回车键,当焦点位于折叠部分的手风琴标题上时,展开该部分”。

让我们找到 accordion 中的第一个面板,并检查它是否已折叠。根据规范,我们知道该面板应该包含role=region一个参数,如果它已折叠,则应该包含hidden一个参数:

cy.get("body")
  .find("[role=region]")
  .first()
  .should("have.attr", "hidden");

我们先找到对应的 header eg。根据规范,我们知道它应该包含role=button一个参数。我们模拟focus事件,因为用户会用它Tab来访问它。

cy.get("body")
  .find("[role=button]")
  .first()
  .focus();

现在让我们在焦点元素中输入空格

cy.focused().type(" ");

让我们检查一下扩展的部分(与第一个动作相反):

cy.get("body")
  .find("[role=region]")
  .first()
  .should("not.have.attr", "hidden");

我想这很简单(如果您熟悉任何 e2e 测试工具,它们都有类似的 API)。

根据鼠标的规格和规格编写所有测试很容易。

不稳定的测试

唯一不太稳定的地方是当我们使用 React 切换焦点时,例如向上箭头、向下箭头、结束、主页。在这种情况下,焦点的改变不是即时的(与浏览器相比Tab)。所以我被迫添加了一个小小的延迟来解决这个问题:

describe("Home", () => {
  it("When focus is on an accordion header, moves focus to the first accordion header.", () => {
    cy.contains("section 2").focus();
    cy.focused().type("{home}");
    cy.wait(100); // we need to wait to make sure React has enough time to switch focus
    cy.focused().contains("section 1");
  });
});

结论

我喜欢规范可以直接转化为端到端测试。这是编写 a11y 组件的好处之一——所有行为都已描述,测试也已规划好。我想尝试用 BDD 风格(测试优先)编写下一个组件。

文章来源:https://dev.to/stereobooster/testing-accessibility-with-cypress-2dfo
PREV
React 项目的 TypeScript monorepo
NEXT
如何安装 Ghost Blog 和 5 美元的 Digital Ocean Droplet 服务 10 万+ 读者