Langflow 前端 a11y 可访问性扫描实践:双层扫描架构、路由清单与 CI 基线断言
2026/9/7 17:15:19 网站建设 项目流程

Langflow 前端 a11y 可访问性扫描实践:双层扫描架构、路由清单与 CI 基线断言

【免费下载链接】langflowLangflow is a powerful tool for building and deploying AI-powered agents and workflows.项目地址: https://gitcode.com/GitHub_Trending/la/langflow

Langflow 的前端可访问性(accessibility,简称 a11y)体系由「Playwright 回归宿主 + 独立路由扫描脚本」两层构成。本文基于 a11y 测试目录的 README,完整覆盖静态路由覆盖、状态化覆盖、本地命令与基线断言的用法,并结合 CI 工作流 a11y-scan.yml、路由清单 a11y_routes.json 和扫描器 a11y_scan.py 的源码实现,说明每个环节背后「为什么这样做」以及如何在本地复现。读完本文,你可以独立在本地运行完整 a11y 扫描、生成按路由/规则分组的 HTML 报告,并理解 CI 是如何自动发现并断言扫描基线的。

两层扫描架构概览

Langflow 对 a11y 检查明确分为两个层次:

  1. Playwright 回归宿主:src/frontend/tests/a11y/ 目录下的 spec 文件是「回归宿主(regression hosts)」。CI 工作流 a11y-scan.yml 会运行所有调用了page.runA11yScan(...)的 spec——不是硬编码文件列表,而是通过grep -rl "runA11yScan(" tests --include="*.spec.ts"动态发现,因此新增的扫描宿主无需修改工作流即可被纳入。
  2. 临时路由/报告工具:scripts/a11y/a11y_scan.py 是一个独立的路由感知扫描器,当你需要针对自定义路由批次、模态框状态(modal state)做 Markdown/HTML 报告或本地排查(local triage)时使用它。

这种分工对应了两种截然不同的诉求:CI 层要求稳定、便宜、可断言,所以只扫「便宜的、可预测的」静态路由;临时工具则要求灵活,支持任意路由组合和 UI 状态操作序列。

静态路由覆盖(Static Route Coverage)

静态路由的「唯一事实来源」是 scripts/a11y/a11y_routes.json。Playwright spec static-routes.a11y.spec.ts 在启动时会读取该清单(依次尝试../../scripts/a11y/a11y_routes.jsonscripts/a11y/a11y_routes.json两个候选路径),并为static组中的每一个条目生成一条scans ${route.path}测试,调用page.runA11yScan(\route-${route.id}`)`。

清单文件将路由分为四组,这一划分在 a11y_scan_routes.md 中有明确说明:

分组含义示例
static常规扫描与 CI 默认覆盖的已认证路由页面/flows/components/settings/api-keys等 13 条
dynamic需要真实 ID 数据才能扫描的路由,不属于默认批次/flow/:id//playground/:id/
gated需要特定认证/角色/环境状态的页面/login/signup/login/admin
excluded重定向、别名或渲染相同组件的路由,附排除理由/(重定向到/flows)、/all*/(兜底重定向链)

每条静态路由条目包含以下字段(可对照 a11y_routes.json 原文):

  • id:稳定标识符,会转化为 IBM 报告标签,例如route-settings-api-keys
  • path:路由路径(清单声明BASENAME为空,路径从/开始);
  • surface:人类可读的页面描述,如 "Files page";
  • ready:就绪检查数组,用于确认页面真正渲染完成而非重定向;
  • requiresMainContent(可选):为true时(如/assets/files/assets/knowledge-bases),spec 会先执行awaitBootstrapTest(page, { skipModal: true })等待主内容引导完成,再跳转路由。

就绪检查(ready check)语法

README 强调:新增路由时必须带上稳定的ready检查,这样如果路由发生重定向或停止渲染,CI 会直接失败。从 static-routes.a11y.spec.ts 的locatorForReadyCheck/expectReadyCheck实现看,ready数组支持以下检查项:

{ "testId": "settings_menu_header" } // getByTestId 可见 { "testId": "mainpage_title", "containsText": "Files" } // 可见且包含文本 { "role": "heading", "name": "Langflow MCP Client" } // 按 role+name 匹配 { "oneOf": [ { "role": "button", "name": "add knowledge" }, { "testId": "search-kb-input" } ] } // 任一候选可见即可

name匹配是大小写不敏感的(实现为new RegExp(name, "i")),oneOf用 Playwright 的or组合定位器后断言first()可见。清单中还附带了assumptions数组,记录当前功能开关假设(如ENABLE_FILE_MANAGEMENT为 true 使/assets路由生效、ENABLE_KNOWLEDGE_BASES为 true 使知识库路由生效),提示维护者开关变更时需要同步更新清单。

静态路由的扫描流程细节

每条路由测试在waitForRouteToSettle中执行(static-routes.a11y.spec.ts):

  1. 若路由标记requiresMainContent,先执行引导等待;
  2. page.goto(route.path)后注入一段 CSS,将所有animation/transition-duration强制置 0——消除动画导致的截图/DOM 抖动;
  3. 断言最终 URL 仍匹配route.path(允许末尾斜杠),这是防重定向的核心防线
  4. 依次执行所有ready检查;
  5. 尝试等待networkidle(失败静默忽略),最后执行runA11yScan

README 对静态路由还有三条约束,与清单excluded组的排除理由一一对应:每个独立页面表面只做一次扫描;不添加重定向别名、文件夹过滤路由,或仅数据不同的同组件路由。例如/components/folder/:folderId被排除,理由就是 "Same components list page, folder-filtered data"。

状态化覆盖(Stateful Coverage)

需要 UI 操作才能到达的状态——流程画布(flow canvas)、配置面板、认证校验、toast、对话框、playground——应写在聚焦的独立 spec中,而不是塞进static-routes.a11y.spec.ts。README 给出的原则是:静态路由必须保持「便宜且可预测」。从目录实际内容看,这层覆盖由约 15 个聚焦 spec 承担,包括 files.a11y.spec.ts、auth-pages.a11y.spec.ts、mcp-servers.a11y.spec.ts、shared-playground.a11y.spec.ts 等;dynamic组中/playground/:id/条目也显式标注了coveredBy指向对应 spec。

目录下的baselines/存放着按「浏览器__检查名」命名的已提交基线 JSON(如chromium__assets-files-actions-menu.json),供断言模式比对扫描结果是否回归。

本地命令

以下命令均在src/frontend目录下执行:

cd src/frontend # 只跑静态路由扫描 RUN_A11Y=true npx playwright test tests/a11y/static-routes.a11y.spec.ts --project=chromium --workers=5 # 跑整个 a11y 目录 RUN_A11Y=true npx playwright test tests/a11y --project=chromium --workers=5 # 汇总所有 JSON 报告并生成 HTML npm run a11y:html-report --silent # 生成本轮作业的文本摘要 npm run a11y:job-summary --silent

RUN_A11Y=true是扫描的开关,只有开启后runA11yScan调用才会真正产出报告。三个 npm 脚本定义在 src/frontend/package.json:a11y:reporttests/utils/aggregate-a11y-reports.mjs,聚合 JSON)、a11y:html-reportbuild-a11y-html-report.mjs)、a11y:job-summarybuild-a11y-job-summary.mjs)。

HTML 报告写入:

coverage/accessibility-reports/index.html

它按「先路由、后规则」两级分组展示问题,每个问题条目包含:IBM 消息、目标元素、DOM 路径、ARIA 路径、元素边界(element bounds)、代码片段(snippet)以及对应 IBM 规则链接。HTML 构建脚本还会读取 a11y_routes.json 把报告标签(如route-settings-api-keys)映射回路由路径与 surface 名称(见 a11y_scan_routes.md)。

对基线断言

要针对 checker 基线做断言(即扫描结果偏离已提交基线时让测试失败),加上RUN_A11Y_ASSERT=true

RUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y --project=chromium --workers=5

CI 集成:a11y-scan.yml 工作流

a11y-scan.yml 的触发方式有三种:pull_request(每次 PR)、每日 02:00 UTC 定时(cron 只从默认分支触发,因此定时运行会先解析出最新的release-*分支再扫描,与 nightly_build 同模式)、以及workflow_dispatch手动触发(可传入refassert参数)。

关键步骤值得注意:

  • 环境:Node 22、Playwright 1.60.0(只安装 chromium),Python 3.13 + uv;
  • 动态发现扫描宿主
SCAN_SPECS=$(grep -rl "runA11yScan(" tests --include="*.spec.ts" | sort) test -n "$SCAN_SPECS" npx playwright test $SCAN_SPECS --project=chromium --workers=1 --retries=2

注意 CI 与本地命令的差异:CI 用--workers=1 --retries=2保证确定性和重试容错,而本地 README 推荐--workers=5提速;RUN_A11Y_ASSERT仅在手动触发且勾选assert输入时置为true

  • 报告与产物:无论扫描成败都会执行Build IBM Scan Summaryif: always()),只要coverage/accessibility-reports/下存在 JSON 报告,就生成 HTML 报告并把npm run a11y:job-summary的输出写入 GitHub Step Summary;随后上传ibm-a11y-reports-${run_attempt}工件(30 天保留期),在 run 页面下载后打开index.html即可查看完整的路由级报告。
  • 扫描前关闭追踪:设置LANGFLOW_DEACTIVATE_TRACING=true,避免遥测干扰被测页面。

临时扫描器:a11y_scan.py 的参数与能力

scripts/a11y/a11y_scan.py 是一个「路由感知的 IBM ACE 扫描 + API 请求跟踪」工具。它的典型调用方式(摘自 a11y_scan_routes.md)直接消费路由清单:

uv run python scripts/a11y/a11y_scan.py \ --url http://localhost:3000 \ --routes-file scripts/a11y/a11y_routes.json \ --route-group static \ --out /tmp/langflow-a11y-static-canonical.json \ --markdown /tmp/langflow-a11y-static-canonical.md \ --html /tmp/langflow-a11y-static-canonical.html \ --timeout-ms 45000

完整命令行参数(对应 parse_args):

参数默认值说明
--url(必填)基础 URL 或完整页面 URL
--routes/--route逗号分隔/可重复的路由,显式指定时覆盖清单
--routes-file路由清单 JSON(即a11y_routes.json
--route-groupstatic清单中的路由分组
--states-file每个路由加载后要执行的模态/状态动作 JSON
--levelsviolation逗号分隔:violation,potentialviolation,recommendation,manual
--timeout-ms/--quiet-ms30000/1000导航超时 / 网络静默窗口
--outa11y-scan-report.jsonJSON 报告输出路径
--markdown/--html可选的 Markdown / 自包含 HTML 报告路径
--ace-urlunpkg 上的accessibility-checker-engine@latest/ace.jsIBM ACE 脚本来源,可指向本地文件
--browser-executable环境变量PLAYWRIGHT_CHROMIUM_EXECUTABLE指定 Chrome/Chromium 可执行文件,也可自动探测系统 Chrome/Chromium/Edge
--headedoff有头模式运行

扫描机制的源码细节

理解该脚本的三处实现,能解释它报告里那些字段从何而来:

  1. 网络静默判定(settled network):wait_for_settled_network 维护一个「在途请求」集合,只有当集合为空且经过--quiet-ms的静默窗口后才认为页面就绪,而不是简单等待load事件。
  2. API 请求跟踪:脚本通过page.on("request"/"response"/"requestfailed")钩子,把同源于/api//health/config的请求记入每个结果的apiRequests(含 method、url、status),失败请求记入requestFailures。这样每条路由的 a11y 报告同时回答「页面加载过程中后端接口是否正常」,扫描结果里若某非closed阶段没有任何 API 请求还会打印warn: no same-origin API/config/health requests observed
  3. 状态动作序列--states-file中每个状态可声明open/close动作列表,支持clickclickTextclickRolefillpresswaitForwaitForHiddenwaitForTextwait九种原子动作(run_action)。每个状态在open阶段执行 ACE 检查并附加模态诊断(可见 dialog 数量、焦点是否落在 dialog 内、打开前的焦点元素),close阶段再执行关闭动作并生成closed阶段记录。这使工具能覆盖「点击按钮弹出对话框后」这类静态扫描触及不到的表面。

报告产物

  • JSON--out):包含generatedAturlroutesreportLevelstotalIssues与逐路由/状态的results(每条含routestatephasefinalUrldurationMsapiRequestsrequestFailuresvisibleTextdiagnosticsissues);
  • Markdown--markdown):路由汇总表 + Top Rules 规则计数表 + 逐路由 Findings(每条 issue 附 ruleId、message、path、source、snippet);
  • HTML--html):自包含单文件,按「route → state → issue」三级<details>折叠展开,摘要区显示总 issue 数、路由数、levels、base URL,支持深浅色主题自动切换。

检查标准参考

a11y 目录下另有两份检查标准文档:ibm-a11y-level1-criteria.md 与 ibm-able-level-1-requirements.md,描述项目对齐的 Level 1 要求,可作为修复 issue 时对照的规范基线。

小结:新增一个路由的完整流程

综合 README 与各工具源码,为 Langflow 新增 a11y 覆盖的标准路径是:

  1. 判断该路由属于「独立页面表面」还是「同组件数据变体」——后者应加入 a11y_routes.json 的excluded组并写明理由;
  2. 静态路由:在static组新增条目,附idpathsurface和稳定的ready检查(testId/role+name/oneOf均可),无需改动 spec——static-routes.a11y.spec.ts 会自动为清单中的每条路由生成测试;
  3. 状态化表面:写一个新的聚焦 spec 并调用page.runA11yScan(...),CI 的 grep 发现机制会自动把它纳入工作流;
  4. 本地验证:RUN_A11Y=true npx playwright test tests/a11y --project=chromium,再用npm run a11y:html-report检查coverage/accessibility-reports/index.html;需要断言基线时加RUN_A11Y_ASSERT=true
  5. 需要临时批次或模态状态排查时,用 a11y_scan.py 配--states-file输出独立报告。

整个体系的不变式是:路由目标只维护在a11y_routes.json一份清单里,Python 扫描器、Playwright spec、HTML 报告构建器三方共用同一来源,任何一侧的路线变更都会立刻在其他侧暴露为失败,而不是静默漂移。

【免费下载链接】langflowLangflow is a powerful tool for building and deploying AI-powered agents and workflows.项目地址: https://gitcode.com/GitHub_Trending/la/langflow

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

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

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

立即咨询