ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

EUI 组件测试实战指南:基于 Cypress Component Testing 的测试、无障碍与调试体系

2026/9/17 21:16:45 拓冰建站 浏览量
EUI 组件测试实战指南:基于 Cypress Component Testing 的测试、无障碍与调试体系 EUI 组件测试实战指南基于 Cypress Component Testing 的测试、无障碍与调试体系【免费下载链接】euiElastic UI Framework 项目地址: https://gitcode.com/GitHub_Trending/eu/eui本指南围绕 Elastic UI FrameworkEUI仓库中的 wiki/contributing-to-eui/testing/cypress-testing.md 文档展开系统讲解 EUI 如何以 Cypress Component Testing 作为组件级测试方案从运行命令、主题切换、React 版本选择到编写组件测试、无障碍a11y测试、真实事件Real Events测试再到失败录像、截图产物与 CI 调试策略。读完本文你将掌握 EUI 仓库内 Cypress 测试的完整工作流并能在本地复现、扩展与调试同类测试。为什么 EUI 选择 Cypress Component TestingEUI 目前只使用 Cypress 组件测试Component Testing而非其完整版 E2E 测试运行器。二者最大的区别在于组件测试只挂载mount单个组件并围绕它做交互断言不需要启动整个应用或真实后端因此非常适合隔离、聚焦特定组件的行为验证同时大幅缩短传统 E2E 测试的启动时间。这一设计决策在仓库的 cypress.config.ts 中也有直接体现component配置块指定了specPattern: [./src/**/*.spec.tsx, ./src/**/*.a11y.tsx]即测试文件直接散落在源码目录中、紧邻被测组件而不是集中放在独立的 E2E 目录下同时测试框架被声明为framework: react、bundler: webpack并复用 cypress/webpack.config 作为构建配置。与 Jest/JSDom 相比Cypress 运行在真实浏览器环境中因此擅长覆盖 Jest 难以模拟的场景文档明确列举了四类典型用例Focus state焦点管理与键盘导航行为PortalsEuiPortal、EuiModal、EuiFlyout等渲染到 DOM 其他位置的组件3rd party dependencies依赖第三方库能力如复制到剪贴板的功能Native DOM behaviors原生 DOM 行为例如滚动、label 与 input 的联动点击。运行测试三个命令入口EUI 在 packages/eui/package.json 中定义了三个 Cypress 测试命令它们全部汇聚到 scripts/test-cypress.js 这个统一入口test-cypress: node ./scripts/test-cypress, test-cypress-dev: yarn test-cypress --dev, test-cypress-a11y: yarn test-cypress --a11y命令行为yarn test-cypress无头模式headless运行组件测试不弹出窗口适用于 CI 与日常回归yarn test-cypress-a11y无头模式运行组件无障碍测试基于 axe-coreyarn test-cypress-dev启动由 Cypress 控制的 Chrome 窗口列出已发现的测试可在窗口内逐条执行与交互从 scripts/test-cypress.js 的实现可以看到--dev与--a11y标志会组合出不同的底层 Cypress 命令开发模式dev走open --component打开交互式运行器无头模式headless走run --component --browser chrome并分别用--spec./src/**/*.spec.tsx或--spec./src/**/*.a11y.tsx精确限定运行哪类测试——这与cypress.config.ts中specPattern的注释scripts/cypress.js splits this using the CLI --spec argument相互印证。设置主题light 与 dark默认情况下测试使用 light浅色主题运行如需深色模式传入--themedark即可yarn test-cypress --themedark yarn test-cypress-a11y --themedark该选项在 scripts/test-cypress.js 中被限定为choices: [light, dark]默认light最终以环境变量THEME${theme}的形式注入 Cypress 进程。同时EUI 的挂载命令 cypress/support/setup/mount.tsx 会以EuiProvider colorModeLIGHT包裹被测组件并允许通过providerProps覆盖colorMode等 EuiProviderProps 配置。跳过 CSS 编译为确保测试使用最新的样式运行器会在启动 Cypress 前将仓库的 SCSS 编译为 CSS。这一步会占用额外的处理时间而本地往往已存在仍然有效的构建产物因此可传入--skip-css跳过编译加速本地迭代yarn test-cypress --skip-css yarn test-cypress-dev --skip-css yarn test-cypress-a11y --skip-css指定 React 版本默认情况下EUI 的 Cypress 测试使用当前支持的最新版 React仓库内默认18。可以通过--react-version切换到16、17或18验证多版本兼容性yarn test-cypress --react-version16 yarn test-cypress --react-version17在 scripts/test-cypress.js 中该参数被定义为type: number且choices: [16, 17, 18]最终以REACT_VERSION环境变量传递。这一变量直接影响挂载方式mount.tsx 通过比对process.env.REACT_VERSION 18来选择cypress/react18还是cypress/react的mount实现注释特别说明必须直接与字符串比较才能让 tree-shaking 正常工作、避免缺包报错。透传 Cypress CLI 参数test-cypress脚本本身基于 yargs 解析并将多余参数原样透传给 Cypressscripts/test-cypress.js 中unknown-options-as-args: true即为此服务。因此 Cypress 官方 CLI 参数 都可直接使用文档给出的典型示例# 只运行单个测试文件例如 onBoarding.js yarn test-cypress --spec **/{file}.spec.tsx # 选择 Chrome 无头运行 yarn test-cypress --headless # 覆盖配置项例如开启视频录制 yarn test-cypress-dev --config videotrue # 覆盖环境变量 yarn test-cypress-dev --env passwordfoobar编写组件测试何时该写 Cypress 测试判断标准很清晰优先为 Jest/JSDom 无法真实复现的功能编写 Cypress 测试例如焦点状态、Portals、第三方依赖、原生 DOM 行为滚动、label 与 input 点击联动。换句话说常规渲染与纯逻辑断言仍归 Jest浏览器交互类行为才交给 Cypress。基本写法Cypress 拥有自己的一套cy.API/命令日常最常用的是cy.get()、cy.find()配合cy.click()或cy.type()与 DOM 交互。文档给出的最小示例import { mount } from cypress/react; import TestComponent from ./test_component; describe(TestComponent, () { it(takes user input, submits it, and displays the resulting output, () { mount(TestComponent /); cy.get([data-test-subjsomeInput]).type(hello world); cy.get([data-test-subjsubmitButton]).click(); cy.get([data-test-subjsomeOutput]).contains(HELLO WORLD); }); });注意两点细节示例中的mount直接来自cypress/react而 EUI 内部实际使用自定义的cy.mount()命令其实现见 cypress/support/setup/mount.tsx会自动用EuiProvider包裹被测组件保证主题、全局样式等上下文就绪交互与断言全部围绕data-test-subj属性定位节点这是 EUI 全仓库统一的测试契约。测试文件命名规范测试文件放在与被测组件相同的目录下与{component_name}.tsx同目录按后缀区分用途{component name}.spec.tsx完整的组件测试随每次构建运行{component name}.a11y.tsx无障碍测试使用 Cypress Axe 规则执行。在仓库中可看到大量实例例如 accordion.a11y.tsx、basic_table.a11y.tsx、breadcrumbs.a11y.tsx 等均与对应组件源码同目录存放总计 47 个.a11y.tsx文件分布于src/components下。Dos and dontsDO通读 Cypress 官方 best practices 建议DO使用data-test-subj属性标记后续要find的组件部位DONT尽量不依赖 class 名或其他实现细节来定位节点避免测试与内部实现强耦合DONT不要扩展cy.全局命名空间——优先直接导入辅助函数。仓库对此的践行体现在 cypress/support/component.tsx所有自定义命令都通过Cypress.Commands.add以受控方式注册例如mount、realMount、checkAxe、repeatRealPress等而辅助逻辑如wait_for_position_to_settle则作为可导入的 helper 而非全局 API 存在。在 CI 上记录失败的 Cypress 测试EUI 支持将失败的 Cypress 测试录制为 Buildkite CI 产物artifact。该功能默认关闭通过修改 cypress.config.ts 中的video: false为video: true即可开启。验证方式故意让一个测试失败然后本地运行yarn test-cypress视频文件会存放在cypress/videos/目录。仓库配置还包含两个与录像相关的细节retries: { runMode: 2, openMode: 2 }Cypress 运行/交互模式下各最多重试 2 次videoCompression: 32压缩级别 32处理时间更长但上传的产物文件更小after:spec钩子cypress.config.ts当config.video开启时只有失败的 spec 保留录像通过的测试其视频会被unlinkSync删除——这正是文档所说EUI 团队配置 Cypress 只为失败测试保留视频的实现来源。Cypress Axe自动化无障碍测试EUI 组件以定时任务scheduled task的方式执行无障碍测试借此更全面地覆盖 DOM 变化场景——例如手风琴Accordion展开、模态框Modal触发等。底层使用 cypress-axe 访问 axe-core 的 API 方法与规则集。如何编写 cypress-axe 测试文件名必须符合{component name}.a11y.tsx模式才会被正确纳入 a11y 测试运行// accordion.a11y.tsx describe(Automated accessibility check, () { it(has zero violations when expanded, () { cy.mount( EuiAccordion {...noArrowProps} EuiPanel colorsubdued Any content inside of strongEuiAccordion/strong will appear here. We will include a href#a link/a to confirm focus. /EuiPanel /EuiAccordion ); cy.get(button.euiAccordion__button).click(); cy.checkAxe(); }); });仓库中真实的实现与文档示例高度一致accordion.a11y.tsx 先cy.mount(EuiAccordion ...)点击展开按钮后调用cy.checkAxe()断言零违规。配置cy.checkAxe()EUI 的自定义cy.checkAxe()命令实现在 cypress/support/a11y/checkAxe.ts其签名接收四个可选参数参数说明默认值skipFailures设为true进入 report-only仅报告模式整个套件完整运行而不会提前失败falsecontext扫描范围可以是 document 或某个选择器class、id、元素div[data-cy-root]即 Cypress 挂载容器axeConfig修改 axe.run API 配置可包含/排除元素、单条规则或整个规则集defaultAxeConfigcallback自定义违规回调violation callback用于增加副作用或改变报告结构内置的日志/抛错回调实现细节checkAxe.ts显示命令内部先cy.injectAxe()再以context ?? defaultContext、axeConfig ?? defaultAxeConfig调用cy.checkA11y并根据skipFailures选择仅打印违规并抛错还是仅打印违规的处理函数。违规信息会通过cy.task(log / table)输出到控制台表格包含id、description、impact、违规节点数等字段对应 cypress.config.ts 中注册的log与table任务。默认规则集定义在 cypress/support/a11y/defaultAxeConfig.tsrunOnly覆盖section508、wcag2a、wcag2aa、wcag21a、wcag21aa全部标签以帮助满足欧美无障碍合规要求同时显式关闭color-contrast规则——因为 EUI 拥有经过充分测试的调色板在 Cypress 中该规则容易产生误报详见 cypress-axe 的 issue #98。基于这些默认值可以按文档示例扩展规则集例如追加best-practices标签// 基于 EUI 默认规则集创建自定义规则集 import { defaultAxeConfig } from ../../cypress/support/a11y/axeCheck; const customAxeConfig { ...defaultAxeConfig, runOnly: { type: tag, // 在既有规则集基础上增加 best-practices values: [...defaultAxeConfig.runOnly.values, best-practices], }, }; // 违规将使用自定义规则集并使测试失败 cy.checkAxe(false, customAxeConfig);需要说明文档中的导入路径../../cypress/support/a11y/axeCheck与当前仓库实际结构略有出入——真实文件为 cypress/support/a11y/checkAxe.ts 与 cypress/support/a11y/defaultAxeConfig.ts实际使用时请按此路径导入defaultAxeConfig。Cypress Real Events真实浏览器事件Cypress 默认事件是模拟的cy.click、cy.type等都由 JavaScript 触发因此事件是不受信任的event.isTrusted为false行为可能与真实原生事件略有差异。某些场景根本无法用模拟事件完成例如填写原生 alert 弹窗或复制到剪贴板。Cypress Real Events 插件正是为此而生。为什么用真实事件Cypress Real Events 通过 Chrome Devtools Protocol 以真实浏览器的方式处理行为从而能更可靠地测试复杂事件如鼠标悬停、键盘焦点。EUI 用真实事件 断言的方式验证键盘与读屏器可访问性观察用户改变本地状态时的表现。如何编写真实事件测试该插件的 API 与现有cy()方法无缝协作想用真实事件按按钮可用realPress(Tab)替代合成的cy.tab()要按多个键即 chord 组合键向辅助方法传入数组如[Shift, Tab]。所有方法都以real前缀命名。文档示例import TestComponent from ./test_component; describe(TestComponent, () { it(presses a button using the Enter key, () { /* 使用 realMount() 在测试窗口中设置焦点 */ cy.realMount(TestComponent /); /* 用真实键盘事件激活按钮 */ cy.get([data-test-subjsubmitButton]).realPress(Enter); /* 断言按钮获得焦点且 aria-expanded 属性已更新 */ cy.focused().invoke(attr, aria-expanded).should(equal, true); }); it(presses a button using the Space key, () { /* 断言按钮同样接受空格键键击 */ cy.realMount(TestComponent /); cy.get([data-test-subjsubmitButton]).realPress(Space); cy.focused().invoke(attr, aria-expanded).should(equal, true); }); });仓库侧cy.realMount()实现在 cypress/support/setup/realMount.tsx它在普通cy.mount()的基础上额外渲染一个 1px×1px 的data-test-subjcypress-real-event-target目标元素并对其realClick以此在测试窗口内建立真实焦点起点随后realPress才能真正落到被测组件上。此外 cypress/support/keyboard/repeatRealPress.ts 还封装了repeatRealPress(keyToPress, count 2, options)用于连续多次按键如多次 Tab 导航的场景。真实事件的 Dos and dontsDO遵循上文 编写 Cypress 测试 的全部建议DO选对挂载方式——组件不会自动接收焦点时用cy.realMount()组件在渲染时会自动获取焦点则用cy.mount()DO持续关注 Cypress Real Events 的新特性。调试测试本地调试失败时推荐yarn test-cypress-dev它允许你运行单个测试套件并在浏览器窗口中运行测试从而可以使用 DevTools 按需暂停与检查 DOM。一般建议测试运行期间不要乱点页面。用户干扰可能引入难以复现的偶发flaky行为与超时。产物Artifacts所有失败测试都会向cypress/screenshots/输出一张截图。调试失败测试时强烈建议先看截图从中检查错误信息与失败瞬间的 UI 状态若静态截图信息不足可传入--config videotrue为全部测试含成功与失败录制视频到cypress/videos/以获取失败点之前更多的上下文。ℹ️ 仓库配置默认关闭视频以缩短测试耗时尤其是 CI但深度调试时建议重新开启。这正对应 cypress.config.ts 中的video: false默认值。CI 调试失败截图产物以及 Cypress 日志由 Jenkins CI 生成。TODO官方文档此处留有 TODO待补充在哪里点击查看相关产物/截图的指引——本地调试请优先使用上文提到的cypress/screenshots/目录。总结围绕 cypress-testing.md 这份文档EUI 的 Cypress 测试体系可以概括为三条主线运行入口统一test-cypress/test-cypress-a11y/test-cypress-dev三个命令背后都是 scripts/test-cypress.js通过--theme、--skip-css、--react-version及任意 Cypress CLI 参数完成高度灵活的测试编排三类测试互补*.spec.tsx覆盖交互功能、*.a11y.tsx基于 axe-core 覆盖无障碍合规、Cypress Real Events 覆盖真实键盘/鼠标行为三者共用data-test-subj定位契约与EuiProvider挂载环境失败可追溯截图、录像默认仅保留失败用例、CI 产物与自定义cy.checkAxe报告共同构成完整的调试闭环。对 EUI 的贡献者而言理解这套体系意味着新增组件时知道该补哪种测试文件、命名放在哪里排查 CI 失败时知道先看截图、必要时开录像维护无障碍合规时知道如何扩展defaultAxeConfig规则集。这些能力同样可以迁移到其他基于 Cypress Component Testing 的 React 组件库项目中。【免费下载链接】euiElastic UI Framework 项目地址: https://gitcode.com/GitHub_Trending/eu/eui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考