EUI 组件测试实战指南:基于 Cypress Component Testing 的测试、无障碍与调试体系
2026/9/17 21:16:25 网站建设 项目流程

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

【免费下载链接】euiElastic UI Framework 🙌项目地址: https://gitcode.com/GitHub_Trending/eu/eui

本指南围绕 Elastic UI Framework(EUI)仓库中的 wiki/contributing-to-eui/testing/cypress-testing.md 文档展开,系统讲解 EUI 如何以 Cypress Component Testing 作为组件级测试方案:从运行命令、主题切换、React 版本选择,到编写组件测试、无障碍(a11y)测试、真实事件(Real Events)测试,再到失败录像、截图产物与 CI 调试策略。读完本文,你将掌握 EUI 仓库内 Cypress 测试的完整工作流,并能在本地复现、扩展与调试同类测试。

为什么 EUI 选择 Cypress Component Testing

EUI 目前只使用 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:焦点管理与键盘导航行为;
  • PortalsEuiPortalEuiModalEuiFlyout等渲染到 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-core)
yarn 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.tsspecPattern的注释("scripts/cypress.js splits this using the CLI --spec argument")相互印证。

设置主题:light 与 dark

默认情况下测试使用 light(浅色)主题运行;如需深色模式,传入--theme=dark即可:

yarn test-cypress --theme=dark yarn test-cypress-a11y --theme=dark

该选项在 scripts/test-cypress.js 中被限定为choices: ['light', 'dark'],默认light;最终以环境变量THEME=${theme}的形式注入 Cypress 进程。同时,EUI 的挂载命令 cypress/support/setup/mount.tsx 会以<EuiProvider colorMode="LIGHT">包裹被测组件,并允许通过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切换到161718验证多版本兼容性:

yarn test-cypress --react-version=16 yarn test-cypress --react-version=17

在 scripts/test-cypress.js 中该参数被定义为type: 'number'choices: [16, 17, 18],最终以REACT_VERSION环境变量传递。这一变量直接影响挂载方式:mount.tsx 通过比对process.env.REACT_VERSION === '18'来选择@cypress/react18还是@cypress/reactmount实现(注释特别说明必须直接与字符串比较,才能让 tree-shaking 正常工作、避免缺包报错)。

透传 Cypress CLI 参数

test-cypress脚本本身基于 yargs 解析,并将多余参数原样透传给 Cypress(scripts/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 video=true # 覆盖环境变量 yarn test-cypress-dev --env password=foobar

编写组件测试

何时该写 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-subj="someInput"]').type('hello world'); cy.get('[data-test-subj="submitButton"]').click(); cy.get('[data-test-subj="someOutput"]').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下。

Do's and don'ts

  • DO:通读 Cypress 官方 best practices 建议;
  • DO:使用data-test-subj属性标记后续要find的组件部位;
  • DON'T:尽量不依赖 class 名或其他实现细节来定位节点(避免测试与内部实现强耦合);
  • DON'T:不要扩展cy.全局命名空间——优先直接导入辅助函数。

仓库对此的践行体现在 cypress/support/component.tsx:所有自定义命令都通过Cypress.Commands.add以受控方式注册,例如mountrealMountcheckAxerepeatRealPress等,而辅助逻辑(如wait_for_position_to_settle)则作为可导入的 helper 而非全局 API 存在。

在 CI 上记录失败的 Cypress 测试

EUI 支持将失败的 Cypress 测试录制为 Buildkite CI 产物(artifact)。该功能默认关闭,通过修改 cypress.config.ts 中的video: falsevideo: 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 color="subdued"> Any content inside of <strong>EuiAccordion</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(仅报告)模式,整个套件完整运行而不会提前失败false
context扫描范围,可以是 document 或某个选择器(class、id、元素)div[data-cy-root](即 Cypress 挂载容器)
axeConfig修改 axe.run API 配置,可包含/排除元素、单条规则或整个规则集defaultAxeConfig
callback自定义违规回调(violation callback),用于增加副作用或改变报告结构内置的日志/抛错回调

实现细节(checkAxe.ts)显示:命令内部先cy.injectAxe(),再以context ?? defaultContextaxeConfig ?? defaultAxeConfig调用cy.checkA11y,并根据skipFailures选择"仅打印违规并抛错"还是"仅打印违规"的处理函数。违规信息会通过cy.task('log' / 'table')输出到控制台,表格包含iddescriptionimpact、违规节点数等字段(对应 cypress.config.ts 中注册的logtable任务)。

默认规则集定义在 cypress/support/a11y/defaultAxeConfig.ts:runOnly覆盖section508wcag2awcag2aawcag21awcag21aa全部标签,以帮助满足欧美无障碍合规要求;同时显式关闭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.clickcy.type等都由 JavaScript 触发,因此事件是"不受信任"的(event.isTrustedfalse),行为可能与真实原生事件略有差异。某些场景根本无法用模拟事件完成,例如填写原生 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-subj="submitButton"]').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-subj="submitButton"]').realPress('Space'); cy.focused().invoke('attr', 'aria-expanded').should('equal', 'true'); }); });

仓库侧,cy.realMount()实现在 cypress/support/setup/realMount.tsx:它在普通cy.mount()的基础上额外渲染一个 1px×1px 的data-test-subj="cypress-real-event-target"目标元素并对其realClick,以此在测试窗口内建立真实焦点起点,随后realPress才能真正落到被测组件上。此外 cypress/support/keyboard/repeatRealPress.ts 还封装了repeatRealPress(keyToPress, count = 2, options),用于连续多次按键(如多次 Tab 导航)的场景。

真实事件的 Do's and don'ts

  • DO:遵循上文 编写 Cypress 测试 的全部建议;
  • DO:选对挂载方式——
    • 组件不会自动接收焦点时用cy.realMount()
    • 组件在渲染时会自动获取焦点则用cy.mount()
  • DO:持续关注 Cypress Real Events 的新特性。

调试测试

本地调试失败时推荐yarn test-cypress-dev:它允许你运行单个测试套件,并在浏览器窗口中运行测试,从而可以使用 DevTools 按需暂停与检查 DOM。

一般建议:测试运行期间不要乱点页面。用户干扰可能引入难以复现的偶发(flaky)行为与超时。

产物(Artifacts)

  • 所有失败测试都会向cypress/screenshots/输出一张截图。调试失败测试时强烈建议先看截图,从中检查错误信息与失败瞬间的 UI 状态;
  • 若静态截图信息不足,可传入--config video=true为全部测试(含成功与失败)录制视频到cypress/videos/,以获取失败点之前更多的上下文。

ℹ️ 仓库配置默认关闭视频以缩短测试耗时(尤其是 CI),但深度调试时建议重新开启。这正对应 cypress.config.ts 中的video: false默认值。

CI 调试

失败截图产物(以及 Cypress 日志)由 Jenkins CI 生成。

TODO:官方文档此处留有 TODO,待补充"在哪里点击查看相关产物/截图"的指引——本地调试请优先使用上文提到的cypress/screenshots/目录。

总结

围绕 cypress-testing.md 这份文档,EUI 的 Cypress 测试体系可以概括为三条主线:

  1. 运行入口统一test-cypress/test-cypress-a11y/test-cypress-dev三个命令背后都是 scripts/test-cypress.js,通过--theme--skip-css--react-version及任意 Cypress CLI 参数完成高度灵活的测试编排;
  2. 三类测试互补*.spec.tsx覆盖交互功能、*.a11y.tsx基于 axe-core 覆盖无障碍合规、Cypress Real Events 覆盖真实键盘/鼠标行为,三者共用data-test-subj定位契约与EuiProvider挂载环境;
  3. 失败可追溯:截图、录像(默认仅保留失败用例)、CI 产物与自定义cy.checkAxe报告共同构成完整的调试闭环。

对 EUI 的贡献者而言,理解这套体系意味着:新增组件时知道该补哪种测试文件、命名放在哪里;排查 CI 失败时知道先看截图、必要时开录像;维护无障碍合规时知道如何扩展defaultAxeConfig规则集。这些能力同样可以迁移到其他基于 Cypress Component Testing 的 React 组件库项目中。

【免费下载链接】euiElastic UI Framework 🙌项目地址: https://gitcode.com/GitHub_Trending/eu/eui

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

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

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

立即咨询