☰
axe-core 的 ARIA Practices(APG)示例页面回归测试:原理、运行与规则豁免机制
2026/9/28 22:18:52 网站建设 项目流程
  • 测试

【免费下载链接】axe-core

Accessibility engine for automated Web UI testing

项目地址:https://gitcode.com/gh_mirrors/ax/axe-core
点击查看免费下载

导读

本文围绕 axe-core 仓库中的 test/aria-practices/README.md 及其测试实现 apg.spec.js,完整讲解这套面向 W3C ARIA Authoring Practices(ARIA 实践指南,简称 APG)示例页面的自动化无障碍回归测试:它如何批量拉取 APG 官方示例、如何用 axe-core 逐页审计、如何通过disabledRules豁免特定规则并追溯豁免原因。读完本文,你将掌握pnpm run test:apg的完整执行链路、测试用例的组织方式,以及为 APG 测试新增规则豁免与页面跳过的正确姿势。

一、这套测试在测什么:为什么用 axe-core 审计 APG 示例

ARIA Authoring Practices(APG)是 W3C 发布的权威无障碍组件交互模式指南,其中包含大量可直接运行的 HTML 示例页面(如 tabs、listbox、dialog、toolbar 等 ARIA 组件的完整实现)。这些示例既是开发者学习 ARIA 用法的教材,也是检验无障碍工具准确性的天然基准语料——因为它们覆盖了真实世界中高频使用的 ARIA 模式组合。

axe-core 仓库将 APG 的示例页面直接引入自身测试体系:用 axe-core 引擎逐页审计这些示例,断言"在给定规则集合下不应发现任何无障碍违规"。这套测试的价值在于:

  • 双向校准:既验证 axe-core 对权威 ARIA 模式的理解与 W3C 实践一致,也反向督促 APG 示例保持高水平的无障碍质量;
  • 回归守护:当 axe-core 新增规则或调整检查逻辑时,任何对 APG 示例误报/漏报的变化都会在这里暴露;
  • 跨规则联动:同一个页面会同时接受 WCAG 2 A/AA 级别相关规则审计,天然形成组合场景测试。

在 doc/developer-guide.md 的"Running Tests"一节中,test:apg被明确列为与test:act(WCAG ACT 规则测试)、test:examples(官方示例测试)并列的非单元测试套件,属于 axe-core 质量保障体系的重要组成部分。

二、一键运行:pnpm run test:apg的完整执行链路

2.1 命令入口

按照 test/aria-practices/README.md 的说明,运行全套 APG 测试只需一条命令:

pnpm run test:apg

这条命令在 package.json 中的定义是:

"test:apg": "start-server-and-test 9876 integration:apg", "integration:apg": "mocha --fail-zero test/aria-practices/*.spec.js", "start": "http-server -a \"\" -p 9876 --silent"

执行链路拆解如下:

  1. start-server-and-test 9876 integration:apg:先启动http-server静态服务器监听9876端口(对应pnpm start脚本),待端口就绪后再执行integration:apg;
  2. integration:apg调用 Mocha 运行test/aria-practices/目录下所有*.spec.js测试文件(当前即 apg.spec.js);
  3. --fail-zero标志确保即便收集到零个测试用例,Mocha 也不会误报失败。

2.2 静态服务器为何必要

测试通过driver.get()让无头浏览器访问http://localhost:9876/node_modules/aria-practices/content/patterns/...形式的本地 URL 来加载 APG 示例页面。因此http-server必须以仓库根目录为服务根目录(-a ""表示监听所有网络接口,--silent关闭访问日志),这样才能把node_modules/aria-practices/下的示例文件直接以 URL 形式暴露给浏览器。这也是start-server-and-test必须"先起服务、再跑测试"的原因。

三、测试实现解剖:apg.spec.js

3.1 示例页面收集:glob 扫描而非手工枚举

测试文件顶部用globSync动态发现所有 APG 示例页面:

const apgPath = path.resolve(__dirname, '../../node_modules/aria-practices/'); const filePaths = globSync( `${apgPath}/content/patterns/*/**/examples/*.html`, { posix: true } ); const testFiles = filePaths.map( fileName => fileName.split('/aria-practices/content/patterns/')[1] );

几个关键设计点:

  • 使用path.resolve而非require.resolve:代码注释明确说明,因为 APG 的package.json没有main字段,无法通过require.resolve定位包路径,只能手动基于测试文件位置计算node_modules/aria-practices/绝对路径;
  • 匹配模式:content/patterns/*/**/examples/*.html会命中patterns/下每个组件目录examples/子目录中的所有 HTML 文件,例如tabs/examples/tabs.html、listbox/examples/listbox-actions.html等;
  • 路径归一化:split('/aria-practices/content/patterns/')[1]把绝对路径裁剪成tabs/examples/tabs.html这种仓库内相对形式,既用于拼接访问 URL(${addr}content/patterns/${filePath}),也用于按页面精确匹配豁免规则。

aria-practices依赖本身在 package.json 中被固定为 GitHub 上 W3C 仓库的特定 commit(github:w3c/aria-practices#18c1a2...),并在 pnpm-workspace.yaml 中登记,确保测试基准版本可复现、可追踪。

3.2 单页测试流程:AxeBuilder 审计全流程

对每一个发现的示例页面,测试用例执行如下流程:

await driver.get(`${addr}content/patterns/${filePath}`); const builder = new AxeBuilder(driver, axeSource) // Support table has no title and has duplicate ids .exclude('#at-support') .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']) .disableRules([ ...disabledRules['*'], ...(disabledRules[filePath] || []) ]); const { violations } = await builder.analyze();

逐项解读:

  1. 加载页面:driver.get()访问本地静态服务器上的示例页;addr定义为http://localhost:9876/node_modules/aria-practices/,最终拼接成完整的示例 URL;
  2. 注入 axe 源码:before钩子中通过require.resolve('../../axe.js')定位构建产物,用fs.readFileSync读出源码字符串,再传给AxeBuilder(来自@axe-core/webdriverjs),确保测试使用的是当前仓库构建出的 axe-core 本体;
  3. 排除干扰元素:.exclude('#at-support')跳过页面中的辅助技术(AT)支持信息表格——代码注释解释了原因:该表格没有标题且包含重复 id,属于示例页自身的"教学性"缺陷,不应计入审计结果;
  4. 限定规则标签:.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])只运行与 WCAG 2.0/2.1 A、AA 级别相关的规则。这些标签直接对应 lib/rules/color-contrast.json 等规则定义文件中的tags数组——例如color-contrast规则带有wcag2aa、wcag143、EN-301-549等多个标签,withTags就是按此筛选规则集;
  5. 断言零违规:analyze()返回结果后,把违规信息映射为{ id, issues: nodes.length }列表,用assert.lengthOf(issues, 0, ...)断言没有任何违规;若存在违规,断言失败信息会列出全部违规规则 id 及其节点数量,便于定位问题。

3.3 超时与重试:面向真实浏览器测试的稳健性

测试套件声明了this.timeout(50000)和this.retries(3):单用例最长 50 秒,失败自动重试 3 次。这是针对真实浏览器驱动场景的必要容错——无头 Chrome 启动、页面渲染、axe 注入分析在 CI 环境都可能偶发超时,重试机制可显著降低偶发失败导致的误报。

四、核心机制:disabledRules规则豁免(README 的灵魂内容)

test/aria-practices/README.md 的重点在于规则豁免的治理规范。测试通过 apg.spec.js 中的disabledRules对象控制哪些规则不在哪些页面上运行:

const disabledRules = { '*': [ 'color-contrast', 'target-size', 'heading-order', // w3c/aria-practices#2119 'scrollable-region-focusable' // w3c/aria-practices#2114 ], 'tabs/examples/tabs-actions.html': ['aria-required-children'], 'listbox/examples/listbox-actions.html': ['nested-interactive'] };

4.1 两层豁免结构

  • 全局豁免('*'键):作用于所有示例页面。当前豁免了color-contrast(颜色对比度)、target-size(目标尺寸)、heading-order(标题顺序)、scrollable-region-focusable(可滚动区域可聚焦)四条规则;
  • 页面级豁免(具体文件路径键):精确到单个示例页面,例如tabs-actions.html豁免aria-required-children(必需子元素),listbox-actions.html豁免nested-interactive(嵌套可交互元素)。

两者在测试用例中合并使用:[...disabledRules['*'], ...(disabledRules[filePath] || [])]——先应用全局豁免,再叠加该页面特有的豁免。

4.2 豁免必须附带"为什么"——注释与 Issue 追踪规范

README 明确了硬性要求:每次豁免规则都必须添加注释说明原因。从 apg.spec.js 的注释可以看到三种典型豁免理由:

  • 上游已知缺陷:heading-order豁免指向w3c/aria-practices#2119,scrollable-region-focusable指向w3c/aria-practices#2114——APG 示例页面本身存在这些问题,axe-core 如实报告,属于正确的检测结果,故在示例修复前豁免;
  • axe-core 尚未支持的新兴模式:tabs-actions.html的豁免注释说明,axe-core 虽已识别aria-actions属性(对应 dequelabs/axe-core#5199),但尚未认可"通过aria-actions关联操作控件"这种作者模式,相关结构性规则因此误报,已在 dequelabs/axe-core#5215 追踪;
  • 示例页的教学性结构:#at-support表格的排除同理。

README 进一步规定:如果豁免原因适用,应在 axe-core 或 aria-practices 仓库提交 Issue 并把 Issue 链接写进代码。这样做有两个目的:一是让后续维护者随时可查"为什么这条规则还不跑";二是当上游 Issue 关闭(例如 APG 修复了示例、axe-core 支持了新模式)时,可以据此及时移回豁免规则,恢复完整审计。

4.3 豁免的最终落地

豁免通过builder.disableRules([...])传入AxeBuilder,运行时不再执行被禁规则,因此最终violations中不会出现这些规则的报告。这套"白名单式豁免 + 注释 + Issue 链接"的治理模式,避免了无理由、永久性的规则关闭——每一次豁免都是可审计、可回溯、可收敛的技术债务。

五、页面跳过:skippedPages机制

除规则豁免外,测试还维护了一个页面级跳过清单:

const skippedPages = [ 'toolbar/examples/help.html' // Embedded into another page ];

skippedPages中的页面会从测试用例列表中直接过滤(testFiles.filter(filePath => !skippedPages.includes(filePath))),完全不对其运行审计。当前唯一的例子是toolbar/examples/help.html——它被嵌入到了另一个页面中作为子页面,单独审计没有意义。测试还包含一个独立的it('finds examples')用例,用assert.isTrue(testFiles.length > 0)确保示例页面收集非空,防止 glob 匹配失效导致"零用例通过"的假绿。

六、运行环境与浏览器驱动

测试基于 Selenium WebDriver 驱动真实浏览器,驱动创建逻辑位于 test/get-webdriver.js:

const chromedriverPath = process.env.CHROMEDRIVER_BIN ?? require('chromedriver').path; const options = new chrome.Options().addArguments( '--headless', '--no-sandbox', '--disable-dev-shm-usage', '--disable-gpu' );

关键点:

  • 无头 Chrome:默认以 headless 模式运行,适合 CI;
  • 环境变量覆盖:CHROMEDRIVER_BIN可指定 chromedriver 二进制路径,CHROME_BIN可指定 Chrome 可执行文件路径(例如在容器内安装自定义 Chrome 时使用);
  • 容器友好参数:--no-sandbox、--disable-dev-shm-usage、--disable-gpu是针对 CI/容器环境的常规配置。

另外,eslint.config.js 中对test/aria-practices/**/*.js有专门的 lint 配置段,与test/act-rules一并处理,说明该目录的测试代码遵循项目统一的 ESLint 规范,贡献新用例时需保持代码风格一致。

七、如何为这套测试做贡献

结合 test/aria-practices/README.md 的治理规则与 apg.spec.js 的实现,常见的扩展场景与标准操作如下:

场景操作
某示例页触发某规则的误报在disabledRules的'*'或页面级键中追加规则 id,并必须添加注释说明原因,必要时附上 axe-core 或 aria-practices 的 Issue 链接
上游 Issue 已修复,可恢复审计从disabledRules中移除对应条目,同时更新或删除注释
某个页面不应单独审计加入skippedPages数组并注释原因
新增 APG 示例无需改动测试代码——globSync会自动发现examples/*.html下的新文件并生成对应用例
调整审计规则范围修改withTags的标签数组(如追加wcag21aa之外的标签)

运行验证:pnpm run test:apg(完整链路)或直接pnpm run integration:apg(需自行先保证 9876 端口服务已启动)。测试输出中若出现assert.lengthOf失败,错误信息会列出所有违规规则 id,可据此判断是真实缺陷还是需要豁免。

结语

axe-core 的 APG 测试套件是一个"以权威示例为基准、动态发现用例、严格治理豁免"的典型回归测试范式:globSync让用例集合随上游示例自动扩展,withTags让规则范围随 WCAG 版本精确收敛,而disabledRules+ 注释 + Issue 链接的三件套则把"暂时不能跑的规则"变成显式、可追踪、可收敛的技术债务。理解这套机制,不仅能读懂 axe-core 的测试体系,也能为其他基于 WebDriver 与 axe 的无障碍测试项目提供可复制的设计参考。

  • 测试

【免费下载链接】axe-core

Accessibility engine for automated Web UI testing

项目地址:https://gitcode.com/gh_mirrors/ax/axe-core
点击查看免费下载
上一篇:如何快速掌握 Opulence PHP 框架:面向新手的完整指南
下一篇:Scoop 项目使用教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询