用途
Cypress Flake Fixer 用于审计现有 Cypress 测试套件中导致间歇性失败的代码模式,并在不借助重试、延长超时或 force: true 掩盖问题的前提下进行修复。它适合以下场景:
- 某个 Cypress 规格在本地通过但在 CI 中失败,或每隔几次运行就失败一次
- 套件中包含 cy.wait(<数字>)、{ force: true } 或仅在特定顺序下通过的测试
- 错误信息提及 "detached from the DOM"、"timed out retrying" 或 "is no longer attached"
- 项目正在跨大版本升级 Cypress(12 至 16)
- 需要对 Cypress 测试代码进行审查
若需从零编写新的 Cypress 测试,编码规范即可满足;本技能专注于诊断和修复现有套件。
主要能力
- 零依赖扫描器:通过 Node.js 18 或更高版本运行,无需安装 Cypress。扫描器从最近的 package.json 读取 Cypress 版本,对已移除的 API 根据当前主版本判定为 error 或 warn。
- 规则分级:扫描结果按规则编号(如 CY001、CY002 等)分级,并给出修复优先级顺序。
- 修复指导:针对每条规则提供前后代码对比(references/anti-patterns.md),以及已移除 API 的迁移代码(references/removed-apis.md)。
- 验证流程:强调在修改前记录基线失败率,修改后重复运行足够次数以确认修复有效。
使用方式
-
扫描:
node ./scripts/scan-cypress.mjs <project-root> node ./scripts/scan-cypress.mjs <project-root> --json node ./scripts/scan-cypress.mjs <project-root> --strict node ./scripts/scan-cypress.mjs cypress/e2e/checkout.cy.ts使用 --list-rules 查看规则目录。扫描结果为启发式,需逐条确认后再修改。
-
修改前测量:记录目标规格的失败频率作为基线。例如:
for i in $(seq 1 20); do npx cypress run --spec "cypress/e2e/checkout.cy.ts" --quiet || echo "FAIL $i"; done若规格每 10 次失败 1 次,修复后需约 30 次干净运行才能确认修复可信。
-
按顺序修复:
- 优先级 1:CY002、CY003、CY007(error 级)——测试本身错误或已损坏
- 优先级 2:CY001、intercept 顺序——基于时间的等待是 flake 最大来源
- 优先级 3:CY005、CY006——重试能力损坏导致 detached-DOM 和一次性断言失败
- 优先级 4:CY004——force: true 掩盖真实的重叠、动画或禁用状态
- 优先级 5:CY008、CY010、CY012——状态泄漏和顺序依赖
- 优先级 6:CY013、CY101、CY102——选择器和断言卫生
-
将每个固定等待替换为其所代表的真实条件:
- 网络请求 → 在触发前注册 cy.intercept(...).as('x'),然后 cy.wait('@x')
- 请求或状态变化后的渲染 → 对结果 DOM 使用 .should()
- 防抖、节流、轮询间隔或 toast 超时 → 在 cy.visit() 前使用 cy.clock(),然后 cy.tick(ms)
- CSS 动画或过渡 → 对最终状态断言
- 服务端渲染 HTML 的客户端水合 → 应用在水合完成时设置的信号
- 未知 → 在 npx cypress open 中运行,选中等待,查看期间发生的请求和 DOM 变化
-
验证:单独运行修改后的规格,重复至少与基线相同的次数;重新运行扫描器比较各规则计数;运行完整套件以捕获顺序依赖。
修复原则
- 仅修改测试代码。若测试暴露了应用缺陷(重复提交、缺少加载状态、UI 竞态),应报告而非绕过。
- 不要提高 defaultCommandTimeout、添加重试或添加 { force: true } 来让测试通过。
- 不要将 cy.wait(2000) 替换为 cy.wait(5000),或对已存在的元素使用 .should('exist')。
- 每次提交只包含一个规则类别,便于二分定位回归。
- 优先修复 cypress/support 中的共享自定义命令。
适用场景与限制
- 适用于诊断和修复现有 Cypress 套件中的 flake 问题,以及跨大版本升级时的 API 迁移。
- 扫描器结果为启发式,需人工确认,可能存在误报;可通过 // cy-scan-ignore CY0xx 注释忽略。
- 需要 Node.js 18 或更高版本运行扫描器,但扫描器本身不依赖 Cypress。
- 修复效果需通过足够次数的重复运行验证,单次通过不足以证明修复有效。
- 若配置中 testIsolation: false,顺序依赖失败属于预期行为而非 flake。
- 重试会隐藏 flake,除非配置为报告模式(如 experimentalStrategy: 'detect-flake-but-always-fail')。
- 部分规则(如 CY005)需根据具体场景判断,并非所有链式调用都需要拆分。
报告格式
技能提供标准报告模板,包含 Cypress 版本、扫描文件数、基线失败率、各规则修复前后计数、未修复项及原因、发现的应用问题、验证结果等。
故障排除
- 扫描器报告 "No Cypress files found":传入包含 cypress/ 或 .cy. 文件的文件夹,或显式传入文件路径。
- 误报:在同行或上一行添加 // cy-scan-ignore CY0xx 并注明原因。
- 版本显示 "not detected":传入 --cypress-major <n>;在 monorepo 中从依赖 Cypress 的包运行扫描器。
- 修复后仅在 CI 失败:比较视口、浏览器和 CPU,使用 CI 浏览器本地运行并限制 CPU。
- cy.wait('@alias') 超时:检查 intercept 是否在请求后注册、URL 模式是否遗漏查询字符串(添加 *)、或请求是否来自缓存。
参考资料
- references/anti-patterns.md:每条扫描规则的前后代码对比
- references/removed-apis.md:按 Cypress 主版本列出的已移除 API 及迁移代码
- Cypress retry-ability 文档
- Cypress 迁移指南