Local Deep Research 无障碍测试指南:WCAG 2.1 AA 与屏幕阅读器兼容性的工程化落地
2026/9/16 14:39:03 网站建设 项目流程

Local Deep Research 无障碍测试指南:WCAG 2.1 AA 与屏幕阅读器兼容性的工程化落地

【免费下载链接】local-deep-research~95% on SimpleQA (e.g. Qwen3.6-27B on a 3090). Supports all local and cloud LLMs (llama.cpp, Ollama, Google, ...). 10+ search engines - arXiv, PubMed, your private documents. Everything Local & Encrypted.项目地址: https://gitcode.com/GitHub_Trending/lo/local-deep-research

本指南以 Local Deep Research(LDR)仓库中的 无障碍测试文档 为骨架,结合 后端测试、Playwright 合规测试、axe-core 辅助工具 等源码证据,系统讲解如何通过自动化手段保障 Web 界面符合 WCAG 2.1 AA 标准、支持屏幕阅读器与纯键盘操作。读者学完后,将能够独立运行整套无障碍测试、定位并修复典型的 ARIA/键盘导航缺陷,并把无障碍检查无缝接入 CI 流水线。

一、测试体系概览:双层自动化结构

LDR 的 Web 界面无障碍测试采用“JavaScript 端到端 + Python 后端结构”双层设计,位于 tests/accessibility_tests/ 目录,目标是确保研究页、设置页、历史页等核心页面满足 WCAG 2.1 AA 与屏幕阅读器兼容性要求。

入口文件技术栈验证维度
前端行为层wcag-compliance.spec.jsPlaywright + @axe-core/playwright真实浏览器中的 DOM 结构、键盘导航、动态内容 ARIA
后端结构层test_accessibility_backend.pypytest + BeautifulSoup服务端渲染 HTML 的语义结构、配置与样式

两层各有分工:Python 层在无浏览器环境下快速校验 HTML 骨架,JavaScript 层则在真实浏览器中验证交互行为。辅助文件 axe-helper.js 封装了 axe-core 的公共配置,auth-setup.js 负责测试前的登录态准备(默认使用test_admin/testpass123测试凭据,可通过环境变量覆盖)。

1.1 JavaScript 测试覆盖范围

Playwright 测试重点覆盖四大类能力:

  • 屏幕阅读器兼容:研究模式选择使用真正的<input type="radio">结构(而非 div 模拟),校验 ARIA 属性与角色、.sr-only辅助元素、<fieldset>/<legend>分组;
  • 键盘导航:Tab 顺序与焦点管理、单选组的方向键(Arrow 键)切换、Enter/Space 激活、快捷键(Enter 提交、Shift+Enter 换行、Ctrl+Enter 备选提交);
  • 表单无障碍:表单控件标签完整、键盘提示可见、纯键盘可完成表单提交、焦点指示器清晰;
  • WCAG 合规:通过 axe-core 自动化扫描 color contrast、焦点可见性与语义标记校验。

1.2 Python 测试覆盖范围

Python 后端测试校验服务端 HTML:

  • HTML 结构:表单标签、语义化标记、标题层级、必填字段标记;
  • ARIA 实现:单选组结构、ARIA 角色与属性、屏幕阅读器支持元素;
  • 配置与样式<html lang>属性、viewport meta 标签、CSS 焦点样式、跳转链接(skip link)、错误提示容器。

二、环境准备与运行命令

2.1 安装依赖

# Python 依赖(后端结构测试) pip install pytest beautifulsoup4 requests # Node.js 依赖(Playwright 前端测试) npm install playwright @playwright/test npx playwright install

测试目录 package.json 声明了@axe-core/playwright ^4.13.0@playwright/test ^1.63.0@lhci/cli ^0.15.1,要求 Node.js>=22.19,并针对yauzltmplodashws等传递依赖配置了安全覆盖(overrides)。

2.2 启动被测应用

测试前必须先启动 Web 服务(默认地址http://localhost:5000):

# 方式一:仓库根目录启动 python app.py # 方式二:以模块方式启动 python -m src.local_deep_research.web.app

TEST_BASE_URL环境变量可覆盖被测地址(默认http://localhost:5000),例如:

export TEST_BASE_URL=http://localhost:8080

Playwright 测试则读取BASE_URL环境变量(wcag-compliance.spec.js 默认同样为http://localhost:5000)。

2.3 运行 JavaScript 测试

# 运行全部无障碍测试 npx playwright test test_accessibility.js # UI 模式调试(可视化查看每一步) npx playwright test test_accessibility.js --ui # 按用例名称过滤运行 npx playwright test test_accessibility.js -g "mode selection should have proper radio button structure"

注意:README 中示例文件名为test_accessibility.js,仓库实际实现为 wcag-compliance.spec.js,运行时可替换为实际文件名。

2.4 运行 Python 测试

# 从 tests 目录运行 cd tests python -m pytest ui_tests/test_accessibility_backend.py -v # 带覆盖率运行 python -m pytest ui_tests/test_accessibility_backend.py --cov=src # 运行单个测试用例 python -m pytest ui_tests/test_accessibility_backend.py::TestHTMLAccessibility::test_radio_button_structure -v

README 中路径为ui_tests/test_accessibility_backend.py,仓库实际文件位于 tests/accessibility_tests/test_accessibility_backend.py,按实际路径调整即可。

2.5 CI 快速命令

# 快速无障碍检查 npm run test:accessibility:quick # 完整无障碍套件 npm run test:accessibility:full # Python 无障碍测试 python -m pytest tests/ui_tests/test_accessibility_backend.py

三、axe-core 扫描与 WCAG 合规测试(源码级剖析)

3.1 axe 配置封装

axe-helper.js 提供了统一入口:

import AxeBuilder from '@axe-core/playwright'; // 默认启用 WCAG 2.1/2.2 的 A/AA 标签 const WCAG_TAGS = ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa']; export function createAxeBuilder(page, options = {}) { const { tags = WCAG_TAGS, exclude = [], disableRules = [] } = options; let builder = new AxeBuilder({ page }).withTags(tags); exclude.forEach(selector => { builder = builder.exclude(selector); }); if (disableRules.length > 0) { builder = builder.disableRules(disableRules); } return builder; }

配套提供两个工具函数:

  • getCriticalViolations(violations):只筛选criticalserious两个等级的问题(axe-helper.js),避免被 minor 问题淹没;
  • formatViolations(violations):把违规详情(影响等级、规则 ID、帮助链接、受影响元素选择器)格式化为可读的控制台输出。

3.2 全页面扫描用例

wcag-compliance.spec.js 对研究页/、历史页/history、设置页/settings三个页面做全量 WCAG 2.1 AA 扫描,以串行模式运行:

const results = await createAxeBuilder(page, { disableRules: AXE_DISABLE_RULES }).analyze(); const criticalViolations = getCriticalViolations(results.violations); expect(criticalViolations).toHaveLength(0);

扫描结果通过test.info().annotationsaccessibility-summary类型写入测试输出(violations 数量与 passes 数量),便于 CI 报表归档。

3.3 color-contrast 规则的工程取舍

源码中有一条值得注意的工程决策:全局禁用了color-contrast规则(wcag-compliance.spec.js):

// Color-contrast is excluded because the default theme (sepia/solarized-light) // has text colors that don't meet WCAG AA 4.5:1 contrast ratios. // This is a systemic theme design issue tracked separately. const AXE_DISABLE_RULES = ['color-contrast'];

注释明确说明:默认主题(sepia/solarized-light)的文字颜色未达到 WCAG AA 4.5:1 对比度,属于系统性的主题设计问题,需单独跟踪,而非页面级的局部缺陷。因此测试策略是:先在自动化扫描中排除该规则以保证流水线可运行,同时将其作为独立议题跟踪修复。这一做法体现了"自动化兜底 + 人工跟进系统性问题"的务实分层。

3.4 单页面重点断言

除全量扫描外,研究页还有 7 个细粒度用例(wcag-compliance.spec.js):

  • 标题结构main内恰好 1 个h1,标题层级不允许向上跳级(如h1后直接h3);
  • 页面地标(landmark):至少 1 个mainrole="main",至少 1 个navrole="navigation"
  • 表单标签:每个非 hidden 的 input/select/textarea 必须有label[for]、包裹型<label>aria-labelaria-labelledby之一;
  • 图片 alt:所有<img>必须有altaria-label
  • 按钮可访问名称:必须有aria-labelaria-labelledby、文本内容或title
  • 链接可辨别文本:文本、ARIA 名称或内嵌带 alt 的图片至少占其一;
  • 设置页表单分组:每个.form-group/.ldr-form-group内必须包含labellegend

四、键盘导航与动态内容测试

4.1 Tab 顺序与焦点可见性

导航测试(wcag-compliance.spec.js)连续按下 10 次 Tab,记录每次聚焦元素的 tag/id/class,断言焦点至少移动过 2 个不同元素(验证 Tab 顺序存在且推进)。随后检查聚焦元素的outlineoutlineWidthboxShadow计算样式,确认存在可见焦点指示器。

这一断言与后端 CSS 相印证:仓库的 custom_dropdown.css 为.ldr-mode-option定义了:focus:focus-visibleoutline: 2px solid var(--accent-primary, #6e4ff6); outline-offset: 2px;,保证键盘聚焦有清晰可见的视觉反馈。

4.2 动态内容的 ARIA 规范

  • 警报容器(wcag-compliance.spec.js):[role="alert"].alert-container.ldr-settings-alert-container必须具有role="alert"role="status"aria-live之一,且aria-live属性可来自祖先元素。这与研究页模板 research.html 中的<div id="research-alert" class="ldr-settings-alert-container" role="alert" aria-atomic="true">一一对应;
  • 进度条[role="progressbar"]必须同时具备aria-valuenowaria-valueminaria-valuemax三个属性(无进度条时用例自动 skip)。

4.3 自定义下拉框 combobox 结构

组件测试(wcag-compliance.spec.js)验证:所有[role="combobox"]必须通过aria-controls指向一个role="listbox"的元素。若目标元素存在则校验其角色;不存在时虽不失败,但缺失aria-controls本身即视为违规。这正是 ARIA 组合组件(combobox → listbox → option)的推荐实践。

五、Python 后端测试:HTML 结构与 ARIA 实现(逐用例解析)

5.1 通用测试夹具

test_accessibility_backend.py 通过authenticated_clientfixture 请求/,将响应 HTML 交给 BeautifulSoup 解析后供各用例复用。

5.2 表单标签与单选组结构

  • test_form_has_proper_labels:遍历所有非 hidden/submit/button、非csrf_token的 input/textarea/select,凡带id的必须存在对应的label[for]
  • test_radio_button_structure:若页面存在type="radio",断言其必有name属性,且带id时必须有对应<label>
  • test_fieldset_and_legend:若存在<fieldset>,每个都必须含<legend>aria-label/aria-labelledby;若没有 fieldset,则要求存在 form-group 类分组或<form>,作为现代 UI 的替代分组方案。

这些断言与前端模板 research.html 的实现严格吻合——研究模式选择使用<fieldset>+<legend>+ 三个带sr-only类的原生 radio:

<fieldset> <legend>Research Mode</legend> <input type="radio" id="mode-quick" name="research_mode" value="quick" checked class="sr-only"> ... <input type="radio" id="mode-detailed" name="research_mode" value="detailed" class="sr-only"> ... <input type="radio" id="mode-chat" name="research_mode" value="chat" class="sr-only"> ... </fieldset>

这正是 README 中 Issue #75(屏幕阅读器兼容)的核心修复:用原生 radio 取代 div 模拟的选择器,使屏幕阅读器能正确播报选中状态,同时保留自定义视觉样式(原生控件用.sr-only视觉隐藏)。

5.3 ARIA 属性与必填字段

  • test_aria_attributes:所有 button/a 必须有文本、aria-labeltitle(保证可访问名称);
  • test_required_fields_marked:带required属性的输入,其标签文本须含*或 "required" 字样(或具备 required 类),或元素本身带aria-required="true"。研究页文本域 research.html 使用aria-required="true"即为此规范。

5.4 语义化与标题层级

test_semantic_markup要求页面至少出现 header/nav/main/footer/section/article 中的一个;test_heading_hierarchy要求至少有 1 个h1且标题级别不得跳级(h1 → h2 → h3…)。后者与前端用例中"main 内恰好一个 h1、向上不跳级"的规则形成前后端双重校验。

5.5 配置与样式类测试

TestAccessibilityConfiguration(test_accessibility_backend.py)验证:

  • CSS 焦点样式:请求/static/css/style.css,若返回 200 则必须包含:focusfocus-visibleoutline(无法访问时自动 skip);
  • 响应式 viewport<meta name="viewport">的 content 必须含width=device-width
  • lang 属性<html lang>必须为en/en-US/en-GB之一——仓库 base.html 使用<html lang="en">queryInput.addEventListener('keydown', function(event) { if (event.key === 'Enter') { if (event.shiftKey) { // Shift+Enter:允许默认行为(插入换行),不做拦截 } else if (event.ctrlKey || event.metaKey) { // Ctrl+Enter / Cmd+Enter:备选提交方式(通用习惯) event.preventDefault(); handleResearchSubmit(new Event('submit')); } else { // 单独 Enter:直接提交表单(既有行为) event.preventDefault(); handleResearchSubmit(new Event('submit')); } } });

    对应的键盘提示以.sr-only文本形式渲染在 research.html,屏幕阅读器可读出提示,视觉用户不受干扰。

    模式切换的方向键导航(README 中"Arrow key navigation for radio button groups")同样在 research.js 实现:监听keydownArrowLeft/ArrowUp选中上一个模式,ArrowRight/ArrowDown选中下一个模式,并调用selectMode()同步视觉状态与 ARIA 状态。

    七、浏览器兼容矩阵与测试前置

    JavaScript 测试在Chromium(Chrome/Edge)、Firefox、WebKit(Safari)三种浏览器引擎上运行。不同浏览器对屏幕阅读器 API 的行为存在差异(尤其涉及 ARIA 动态播报),因此多引擎矩阵是必要的验证手段。

    wcag-compliance.spec.js 还配置了:

    test.use({ baseURL: BASE_URL, trace: 'on-first-retry', // 首次失败重试时记录 trace screenshot: 'only-on-failure', // 仅在失败时截图 });

    便于 CI 排障。

    登录态处理(auth-setup.js)

    由于多数页面需要登录,auth-setup.js 在测试前通过 project setup 任务完成一次登录并把storageState保存到.auth/user.json,后续用例直接复用登录态,避免重复登录拖慢套件。脚本中值得注意的细节:

    • 登录超时放宽到 180 秒(setup.setTimeout(180_000)),注释解释原因:冷启动 Docker 时首次登录会创建加密 SQLCipher 数据库、从密码派生密钥并导入 500+ 默认设置,实际等待由内部page.waitForURL('/', { timeout: 120_000 })兜底;
    • 登录后断言.ldr-user-info可见,确保真正进入已登录状态再保存会话。

    八、常见测试失败与修复方案

    8.1 单选组结构问题

    症状:找不到 radio 或标签不正确。修复:确保模板使用原生<input type="radio">并配对应<label for>。参考 research.html 的 fieldset/legend 结构,避免用 div 模拟选择器。

    8.2 ARIA 属性不同步

    症状aria-checked与实际选中状态不一致。修复:检查 research.js 的selectMode()函数——它必须同时更新视觉样式(active class)与 ARIA 状态(aria-checked/checked 属性)。

    8.3 键盘导航失效

    症状:方向键无法切换模式。修复:确认事件监听器正确绑定在 research.js 的 mode 元素上,且preventDefault()未被遗漏导致页面滚动干扰焦点。

    8.4 焦点可见性缺失

    症状:Tab 聚焦后看不到焦点指示。修复:确认 custom_dropdown.css 中:focus/:focus-visibleoutline样式存在且未被outline: none覆盖(注意.ldr-custom-dropdown-input:focus { outline: none; }这类局部重置,需在组件级别单独补充可见焦点样式)。

    九、手动测试补充建议

    自动化无法完全替代真实用户感知,README 建议至少覆盖以下场景:

    1. 屏幕阅读器实测:NVDA(Windows)、JAWS(Windows)、VoiceOver(macOS)各跑一遍核心流程;
    2. 纯键盘导航:断开鼠标,仅用 Tab、方向键、Enter/Space 完成一次完整的"输入问题 → 选择模式 → 提交研究"流程;
    3. 高对比模式:Windows 高对比模式下验证焦点指示器仍然可见;
    4. 200% 缩放下验证:所有内容在 200% 缩放下仍可访问、不丢功能。

    十、为新增 UI 功能贡献无障碍测试

    在添加新 UI 功能时,应遵循以下验收清单:

    1. 为功能补充对应的无障碍测试用例;
    2. 正确实现 ARIA 属性(角色、状态、labelledby 关联);
    3. 测试键盘导航路径(Tab 顺序、方向键、快捷键);
    4. 验证屏幕阅读器兼容性(可访问名称、播报内容);
    5. 提交 PR 前完整运行整套无障碍测试套件。

    后端断言(如 test_accessibility_backend.py 中的语义化、标题层级、必填标记)可作为新页面模板的"结构红线";前端 axe 扫描与键盘用例则是"行为红线"。两层同时通过,才能保障新功能不回归 WCAG 2.1 AA 合规状态。

    十一、最佳实践总结

    • 原生优先:能用<button><input type="radio">等原生控件就不用 div 模拟,原生控件自带键盘与屏幕阅读器语义;
    • 自动化分两级:后端结构断言负责快速反馈,axe-core 扫描负责行为级兜底,二者互补;
    • 系统性问题单独跟踪:像 color-contrast 这类主题级的合规缺口,应在排除规则的同时登记独立议题,避免掩盖问题;
    • 登录态复用:用 storageState 复用会话,显著缩短多页面测试耗时;
    • 多浏览器矩阵:屏幕阅读器与键盘行为存在引擎差异,Chromium/Firefox/WebKit 三端都必须跑;
    • 失败可观测:开启 trace 与失败截图,让 CI 中的无障碍失败可快速定位。

    通过上述双层测试体系,Local Deep Research 得以在持续迭代中守住 WCAG 2.1 AA 与屏幕阅读器兼容性的底线——这正是 无障碍测试文档 及其配套 前端合规测试、后端结构测试 为项目提供的核心价值。

    【免费下载链接】local-deep-research~95% on SimpleQA (e.g. Qwen3.6-27B on a 3090). Supports all local and cloud LLMs (llama.cpp, Ollama, Google, ...). 10+ search engines - arXiv, PubMed, your private documents. Everything Local & Encrypted.项目地址: https://gitcode.com/GitHub_Trending/lo/local-deep-research

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

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

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

立即咨询