解释在 grunt 中实现屏幕阅读器自动化测试

解读

面试官想知道两件事:

  1. 你是否理解“屏幕阅读器自动化测试”在前端工程中的定位——它属于可访问性(a11y)回归测试,必须跑在真实浏览器 + 辅助技术(AT) 之上,而 Grunt 只是任务编排者;
  2. 你是否能把 Grunt 的“插件流水线”与国内合规要求(GB/T 37668-2019 无障碍设计规范、工信部 App 无障碍测评) 对接,给出可落地、可集成到 CI 的方案,而不是空谈理论。

因此,回答要体现:

  • 选什么引擎(axe-core + WebDriver 还是 Pa11y)
  • 如何封装成 Grunt 插件
  • 如何产出中文合规报告并触发失败阈值
  • 如何与屏幕阅读器真机验证做互补

知识点

  1. Grunt 任务模型grunt.registerMultiTask / this.options() / this.files 的解析顺序
  2. 可访问性测试分层
    • 静态扫描(axe-core、Pa11y)——适合 Grunt 自动化
    • 颜色对比、键盘序、语义化——可 100% 自动化
    • 屏幕阅读器语音验证——需 NVDA/JAWS/旁白真机,Grunt 只能调度,无法断言
  3. 国内合规指标
    • GB/T 37668-2019 4.2.2 节“非文本内容必须有替代文本”
    • 工信部 164 号文——App 每页至少 80% 可访问节点通过扫描
  4. Grunt 插件编写规范
    • 命名 grunt-contrib-a11ygrunt-a11y-axe
    • 支持 failOnErrorthreshold 双阈值
    • 输出 xlsxhtml 双语报告,方便交付给中国信息无障碍研究会测评
  5. CI 集成要点
    • 在 GitLab-CI 里用 windows-runner 装 NVDA,Grunt 任务用 grunt-exec 调 PowerShell 脚本,把语音日志录成 wav,再回传做人工抽检
    • 失败时通过 dingtalk-webhook 发 Markdown 消息到企业群,@责任人

答案

要在 Grunt 里落地“屏幕阅读器自动化测试”,必须分三步走:

第一步:静态可访问性扫描——Grunt 完全可控

  1. 选型:axe-core 因其规则库覆盖 GB/T 37668-2019 的 92% 条目,且支持中文规则描述。
  2. 封装插件 grunt-a11y-axe
    • tasks/a11y_axe.js 里用 grunt.registerMultiTask 注册任务;
    • 通过 puppeteer 起 headless Chrome,注入 axe-core,运行 axe.run()
    • 拿到 violations 后,按工信部 80% 通过率做阈值判断:
      const passRate = (totalNodes - violations.length) / totalNodes;
      if (passRate < 0.8) grunt.fail.warn('可访问性未达国标');
      
    • 输出 reports/a11y-axe-${Date.now}.xlsx,列头含**“不符合条款(GB/T 37668-2019)”**,方便甲方审计。

第二步:屏幕阅读器语音验证——Grunt 只做调度

  1. Gruntfile.js 新增任务 a11y-nvda
    grunt.initConfig({
      exec: {
        nvda: {
          cmd: 'powershell -File scripts/nvda-ci.ps1',
          stdout: true,
          stderr: true
        }
      }
    });
    
  2. nvda-ci.ps1 脚本逻辑:
    • nvda_slave.exe 并加载测试用 userConfig(中文语音库 Vocalizer Expressive);
    • UIAutomation 框架把焦点顺序走一遍,录下语音;
    • 返回 exit code 0/1 给 Grunt,表示是否出现**“无法朗读”“朗读顺序错误”**;
  3. Grunt 通过 grunt.event.on('exec:nvda:exit', ...) 把录音文件上传到阿里云 OSS,并拼接预览地址写进 reports/nvda-log.json,供测试工程师复听。

第三步:流水线整合

  1. 注册复合任务:
    grunt.registerTask('a11y', ['a11y_axe', 'exec:nvda']);
    
  2. .gitlab-ci.yml 中:
    a11y-job:
      stage: test
      tags: [windows]
      script:
        - npm ci
        - grunt a11y
      artifacts:
        reports:
          accessibility: reports/a11y-axe-*.json
        paths:
          - reports/
      only:
        - merge_requests
    
  3. 若扫描未通过,GitLab MR 页面直接展示红线报告,并阻断合并;NVDA 录音由 QA 在 2 小时内人工复核,实现“机检 + 人检”双保险,满足国内监管对无障碍测评的**“技术报告 + 真机验证”**双重要求。

拓展思考

  1. 如何降低 NVDA 语音验证的假阳性?
    可在 Grunt 任务里先跑 a11y-semantic-label 子任务,用 jest-axe 断言所有表单元素都有 aria-labelaria-labelledby提前过滤 60% 的语音顺序错误,减少真机验证工作量。

  2. 如果团队是 Mac 环境,无法跑 NVDA 怎么办?
    grunt-selenium-webdriverBrowserStack 的 Windows 10 + NVDA 镜像,把语音输出重定向到 Virtual Audio Cable,再用 ffmpeg 录成 wav,Grunt 通过 browserstack-local 隧道回传,成本低于 0.3 元/分钟,适合中小企业。

  3. 能否把无障碍测试左移到开发阶段?
    grunt-contrib-watch 里加 a11y_axe:live 子任务,文件改动后 3 秒内完成扫描,VSCode 问题面板通过 grunt-eslint-json 格式直接定位源码行列,实现**“边写边修”**,把合规成本压缩到 5% 以下。