mcpField
A
a11y-playwright-testing

a11y-playwright-testing

github.com/fugazi测试与质量256139 次安装2 天前更新
代码仓库

关于 a11y-playwright-testing

面向使用 Playwright、TypeScript 和 axe-core 的团队的无障碍测试技能,支持 WCAG 2.2 AA 扫描及键盘、焦点和语义检查。自动化检测不能替代完整合规审查,仍需人工测试。

主要功能

  1. 01axe-core WCAG 2.2 AA 扫描
  2. 02键盘导航与焦点管理测试
  3. 03语义结构和 ARIA 验证
  4. 04表单标签及错误提示检查
  5. 05颜色对比度与读屏器测试模式

适用场景

  1. 01检查页面和组件的 WCAG 违规
  2. 02验证对话框的焦点陷阱与返回
  3. 03测试表单键盘操作和标签
  4. 04为无障碍回归测试添加自动检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/fugazi/test-automation-skills-agents --skill a11y-playwright-testing

安装后在 Claude Code 中输入/a11y-playwright-testing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

LICENSE.txt
references/
aria-patterns-mistakes.md
aria-patterns-widgets-1.md
aria-patterns-widgets-2.md
axe-tags-reference.md
snippets-keyboard-focus-semantic.md
snippets-setup-and-scanning.md
snippets-visual-names-checklist.md
wcag21aa-checklist.md

详细介绍

Playwright 无障碍测试(TypeScript)

使用 Playwright、TypeScript 和 axe-core 进行自动化无障碍测试的综合工具包。支持验证是否符合 WCAG 2.2 AA 级标准(包含 WCAG 2.1 的全部要求),测试键盘操作能力、语义有效性,并防止无障碍回归。

激活条件: 在进行无障碍测试、WCAG 合规性检查、axe-core 扫描、键盘导航测试、焦点管理、ARIA 验证或屏幕阅读器兼容性测试时,会触发此技能。

何时使用此技能

  • 使用 axe-core 进行自动化无障碍扫描,以检查是否符合 WCAG 2.2 AA 级标准
  • 测试键盘导航,确保 Tab、Enter、Space、Escape 和方向键均可操作
  • 验证对话框、菜单和动态内容的焦点管理
  • 断言地标、标题和 ARIA 的语义结构
  • 测试表单的无障碍性,包括标签、错误信息和说明
  • 验证颜色对比度和视觉无障碍性
  • 测试屏幕阅读器兼容性模式
请勿用于
  • Selenium/Java 无障碍测试(请使用 accessibility-selenium-testing)。
  • 编写 Playwright 功能/UI E2E 规范(请使用 playwright-e2e-testing)。
  • 完整的符合性认证——自动化 axe 扫描只能发现约 30-40% 的问题;仍需进行人工审查和辅助技术测试。

前提条件

要求详细信息
Node.js建议使用 v18+
Playwright已安装 @playwright/test
axe-core@axe-core/playwright 软件包
TypeScript已在项目中配置
快速设置
# Add axe-core to existing Playwright project
npm install -D @axe-core/playwright axe-core

首先要确认的问题

编写无障碍测试前,请先确认:

  1. 范围:哪些页面/流程在测试范围内?明确排除哪些内容?
  2. 标准:采用 WCAG 2.2 AA(默认)还是特定的组织政策?
  3. 优先级:哪些组件风险最高(表单、模态框、导航、结账)?
  4. 例外情况:是否存在已知限制(旧版标记、第三方小组件)?
  5. 辅助技术:哪些屏幕阅读器/浏览器需要进行人工测试?

核心原则

1. 自动化的局限性

[!] 重要: 自动化工具只能检测约 30-40% 的无障碍问题。使用自动化来防止回归并发现常见故障;要全面符合 WCAG 标准,必须进行人工审查。

2. 优先使用语义化 HTML

优先使用原生 HTML 语义,而不是 ARIA。只有原生元素无法实现所需语义时,才使用 ARIA。

// [ok] Semantic HTML - inherently accessible
await page.getByRole("button", { name: "Submit" }).click();

// [no] ARIA override - requires manual keyboard/focus handling
await page.locator('[role="button"]').click(); // Often a <div>
3. 定位器策略也是无障碍性信号

如果无法通过角色或标签定位元素,这通常意味着存在无障碍缺陷。

定位器成功示例无障碍性信号
getByRole('button', { name: 'Submit' }) [ok]按钮具有无障碍名称
getByLabel('Email') [ok]输入框已正确标注
getByRole('navigation') [ok]地标存在
locator('.submit-btn') [!]可能缺少无障碍名称

关键工作流

自动化 Axe 扫描(WCAG 2.2 AA)
import AxeBuilder from "@axe-core/playwright";
import { test, expect } from "@playwright/test";

test("page has no WCAG 2.2 AA violations", async ({ page }) => {
  await page.goto("/");

  const results = await new AxeBuilder({ page })
    .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22a", "wcag22aa"])
    .analyze();

  expect(results.violations).toEqual([]);
});
限定范围的 Axe 扫描(组件级)
test("form component is accessible", async ({ page }) => {
  await page.goto("/contact");

  const results = await new AxeBuilder({ page })
    .include("#contact-form") // Scope to specific component
    .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22a", "wcag22aa"])
    .analyze();

  expect(results.violations).toEqual([]);
});
键盘导航测试
test("form is keyboard navigable", async ({ page }) => {
  await page.goto("/login");

  // Tab to first field
  await page.keyboard.press("Tab");
  await expect(page.getByLabel("Email")).toBeFocused();

  // Tab to password
  await page.keyboard.press("Tab");
  await expect(page.getByLabel("Password")).toBeFocused();

  // Tab to submit button
  await page.keyboard.press("Tab");
  await expect(page.getByRole("button", { name: "Sign in" })).toBeFocused();

  // Submit with Enter
  await page.keyboard.press("Enter");
  await expect(page).toHaveURL(/dashboard/);
});
对话框焦点管理
test("dialog traps and returns focus", async ({ page }) => {
  await page.goto("/settings");
  const trigger = page.getByRole("button", { name: "Delete account" });

  // Open dialog
  await trigger.click();
  const dialog = page.getByRole("dialog");
  await expect(dialog).toBeVisible();

  // Focus should be inside dialog
  await expect(dialog.getByRole("button", { name: "Cancel" })).toBeFocused();

  // Tab should stay trapped in dialog
  await page.keyboard.press("Tab");
  await expect(dialog.getByRole("button", { name: "Confirm" })).toBeFocused();
  await page.keyboard.press("Tab");
  await expect(dialog.getByRole("button", { name: "Cancel" })).toBeFocused();

  // Escape closes and returns focus to trigger
  await page.keyboard.press("Escape");
  await expect(dialog).toBeHidden();
  await expect(trigger).toBeFocused();
});
跳过链接验证
test("skip link moves focus to main content", async ({ page }) => {
  await page.goto("/");

  // First Tab should focus skip link
  await page.keyboard.press("Tab");
  const skipLink = page.getByRole("link", { name: /skip to (main|content)/i });
  await expect(skipLink).toBeFocused();

  // Activating skip link moves focus to main
  await page.keyboard.press("Enter");
  await expect(page.locator('#main, [role="main"]').first()).toBeFocused();
});

POUR 原则参考

原则重点领域示例测试
可感知替代文本、字幕、对比度、结构图像替代文本、颜色对比度比值
可操作键盘、焦点、时序、导航Tab 顺序、焦点可见性、跳过链接
可理解标签、说明、错误、一致性表单标签、错误消息、可预测的行为
健壮有效的 HTML、ARIA、名称/角色/值语义结构、无障碍名称

Axe-Core 标签

默认值:wcag2a、wcag2aa、wcag21a、wcag21aa、wcag22a、wcag22aa(WCAG 2.2 AA)。使用 best-practice 可进行额外检查。完整标签列表请参阅 references/axe-tags-reference.md。


异常处理

无法避免异常时:

  1. 缩小范围 - 仅限具体组件/路由
  2. 记录影响 - 涉及哪些 WCAG 标准,及对用户的影响
  3. 设置到期时间 - 负责人 + 修复日期
  4. 跟踪工单 - 链接到修复问题
// [no] Avoid: Global rule disable
new AxeBuilder({ page }).disableRules(["color-contrast"]);

// [ok] Better: Scoped exclusion with documentation
new AxeBuilder({ page })
  .exclude("#third-party-widget") // Known issue: JIRA-1234, fix by Q2
  .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22a", "wcag22aa"])
  .analyze();

故障排除

问题原因解决方案
Axe 检测到 0 项违规,但应用未通过人工审计自动化仅覆盖约 30-40%添加人工测试清单
动态内容出现误报内容尚未完全呈现等待状态稳定后再扫描
颜色对比度检测错误地未通过背景图像/渐变对已知误报使用 exclude
无法按角色找到元素缺少语义化 HTML修复标记 - 这是一个真实缺陷
焦点不可见缺少 :focus 样式添加可见的焦点指示器 CSS
对话框焦点未被限制缺少焦点限制逻辑实现焦点限制(参见代码片段)
跳过链接不起作用目标缺少 tabindex="-1"为主要内容添加 tabindex

CLI 快速参考

命令描述
npx playwright test --grep "a11y"仅运行无障碍测试
npx playwright test --headed使用可见浏览器运行以便调试
npx playwright test --debug使用 Inspector 逐步执行
PWDEBUG=1 npx playwright test带暂停功能的调试模式

危险信号

  • 将 axe 扫描干净视为完全符合 WCAG — 自动化仅覆盖约 30-40% 的标准。
  • 全局禁用规则(例如 color-contrast),而不是使用有明确工单记录的限定范围 .exclude()。
  • 在页面状态稳定前扫描 — 异步内容会导致误报“0 项违规”。
  • 因 axe 测试通过而跳过键盘/焦点测试 — 焦点顺序和焦点陷阱需要显式测试。

参考资料

文档内容
代码片段:设置与扫描axe-core 设置、辅助函数和扫描模式
代码片段:键盘、焦点、语义键盘导航、焦点管理、语义结构
代码片段:视觉、名称、清单视觉无障碍、无障碍名称、关键页面
WCAG 2.2 AA 清单按 POUR 原则划分的手动审查清单
ARIA 模式:小组件第 1 部分基础知识、对话框、标签页、菜单小组件
ARIA 模式:小组件第 2 部分手风琴、组合框、实时区域、工具提示
ARIA 模式:错误与参考常见 ARIA 错误和角色速查参考

外部资源

资源URL
WCAG 2.2 规范https://www.w3.org/TR/WCAG22/
WCAG 快速参考https://www.w3.org/WAI/WCAG22/quickref/
WAI-ARIA 编写实践https://www.w3.org/WAI/ARIA/apg/
axe-core 规则https://dequeuniversity.com/rules/axe/

验证

  • axe-core 审计通过 — AxeBuilder.analyze() 返回的严重违规项数量为零
  • 已测试键盘导航 — 所有交互元素都可通过 Tab 访问;焦点顺序符合逻辑
  • 颜色对比度足够 — 符合 WCAG 2.2 AA 最低对比度要求(普通文本为 4.5:1,大号文本为 3:1)
  • 符合 WCAG 2.2 AA — 扫描中包含标签 wcag22a/wcag22aa(焦点不被遮挡、拖动操作、目标尺寸下限)

评论

支持 Markdown