Langflow 前端 a11y 可访问性扫描实践双层扫描架构、路由清单与 CI 基线断言【免费下载链接】langflowLangflow is a powerful tool for building and deploying AI-powered agents and workflows.项目地址: https://gitcode.com/GitHub_Trending/la/langflowLangflow 的前端可访问性accessibility简称 a11y体系由「Playwright 回归宿主 独立路由扫描脚本」两层构成。本文基于 a11y 测试目录的 README完整覆盖静态路由覆盖、状态化覆盖、本地命令与基线断言的用法并结合 CI 工作流 a11y-scan.yml、路由清单 a11y_routes.json 和扫描器 a11y_scan.py 的源码实现说明每个环节背后「为什么这样做」以及如何在本地复现。读完本文你可以独立在本地运行完整 a11y 扫描、生成按路由/规则分组的 HTML 报告并理解 CI 是如何自动发现并断言扫描基线的。两层扫描架构概览Langflow 对 a11y 检查明确分为两个层次Playwright 回归宿主src/frontend/tests/a11y/ 目录下的 spec 文件是「回归宿主regression hosts」。CI 工作流 a11y-scan.yml 会运行所有调用了page.runA11yScan(...)的 spec——不是硬编码文件列表而是通过grep -rl runA11yScan( tests --include*.spec.ts动态发现因此新增的扫描宿主无需修改工作流即可被纳入。临时路由/报告工具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.json和scripts/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/adminexcluded重定向、别名或渲染相同组件的路由附排除理由/重定向到/flows、/all、*/兜底重定向链每条静态路由条目包含以下字段可对照 a11y_routes.json 原文id稳定标识符会转化为 IBM 报告标签例如route-settings-api-keyspath路由路径清单声明BASENAME为空路径从/开始;surface人类可读的页面描述如 Files pageready就绪检查数组用于确认页面真正渲染完成而非重定向requiresMainContent可选为true时如/assets/files、/assets/knowledge-basesspec 会先执行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 } // 按 rolename 匹配 { 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若路由标记requiresMainContent先执行引导等待page.goto(route.path)后注入一段 CSS将所有animation/transition-duration强制置 0——消除动画导致的截图/DOM 抖动断言最终 URL 仍匹配route.path允许末尾斜杠这是防重定向的核心防线依次执行所有ready检查尝试等待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_A11Ytrue npx playwright test tests/a11y/static-routes.a11y.spec.ts --projectchromium --workers5 # 跑整个 a11y 目录 RUN_A11Ytrue npx playwright test tests/a11y --projectchromium --workers5 # 汇总所有 JSON 报告并生成 HTML npm run a11y:html-report --silent # 生成本轮作业的文本摘要 npm run a11y:job-summary --silentRUN_A11Ytrue是扫描的开关只有开启后runA11yScan调用才会真正产出报告。三个 npm 脚本定义在 src/frontend/package.jsona11y: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_ASSERTtrueRUN_A11Ytrue RUN_A11Y_ASSERTtrue npx playwright test tests/a11y --projectchromium --workers5CI 集成a11y-scan.yml 工作流a11y-scan.yml 的触发方式有三种pull_request每次 PR、每日 02:00 UTC 定时cron 只从默认分支触发因此定时运行会先解析出最新的release-*分支再扫描与 nightly_build 同模式、以及workflow_dispatch手动触发可传入ref和assert参数。关键步骤值得注意环境Node 22、Playwright 1.60.0只安装 chromiumPython 3.13 uv动态发现扫描宿主SCAN_SPECS$(grep -rl runA11yScan( tests --include*.spec.ts | sort) test -n $SCAN_SPECS npx playwright test $SCAN_SPECS --projectchromium --workers1 --retries2注意 CI 与本地命令的差异CI 用--workers1 --retries2保证确定性和重试容错而本地 README 推荐--workers5提速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_TRACINGtrue避免遥测干扰被测页面。临时扫描器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-enginelatest/ace.jsIBM ACE 脚本来源可指向本地文件--browser-executable环境变量PLAYWRIGHT_CHROMIUM_EXECUTABLE指定 Chrome/Chromium 可执行文件也可自动探测系统 Chrome/Chromium/Edge--headedoff有头模式运行扫描机制的源码细节理解该脚本的三处实现能解释它报告里那些字段从何而来网络静默判定settled networkwait_for_settled_network 维护一个「在途请求」集合只有当集合为空且经过--quiet-ms的静默窗口后才认为页面就绪而不是简单等待load事件。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。状态动作序列--states-file中每个状态可声明open/close动作列表支持click、clickText、clickRole、fill、press、waitFor、waitForHidden、waitForText、wait九种原子动作run_action。每个状态在open阶段执行 ACE 检查并附加模态诊断可见 dialog 数量、焦点是否落在 dialog 内、打开前的焦点元素close阶段再执行关闭动作并生成closed阶段记录。这使工具能覆盖「点击按钮弹出对话框后」这类静态扫描触及不到的表面。报告产物JSON--out包含generatedAt、url、routes、reportLevels、totalIssues与逐路由/状态的results每条含route、state、phase、finalUrl、durationMs、apiRequests、requestFailures、visibleText、diagnostics、issuesMarkdown--markdown路由汇总表 Top Rules 规则计数表 逐路由 Findings每条 issue 附 ruleId、message、path、source、snippetHTML--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 覆盖的标准路径是判断该路由属于「独立页面表面」还是「同组件数据变体」——后者应加入 a11y_routes.json 的excluded组并写明理由静态路由在static组新增条目附id、path、surface和稳定的ready检查testId/rolename/oneOf均可无需改动 spec——static-routes.a11y.spec.ts 会自动为清单中的每条路由生成测试状态化表面写一个新的聚焦 spec 并调用page.runA11yScan(...)CI 的 grep 发现机制会自动把它纳入工作流本地验证RUN_A11Ytrue npx playwright test tests/a11y --projectchromium再用npm run a11y:html-report检查coverage/accessibility-reports/index.html需要断言基线时加RUN_A11Y_ASSERTtrue需要临时批次或模态状态排查时用 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),仅供参考
