Files
2026-07-13 21:35:45 +08:00

177 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: e2e-test-create
description: >
分析 React 组件源代码以理解 UI 结构,
然后按照 Metabase 惯例生成符合习惯的 Cypress E2E 测试。
仅在代码阅读和截图调试不足以解决问题时,才回退使用 Playwright MCP 浏览器探索。
disable-model-invocation: true
allowed-tools:
- Bash
- Read
- Write
- Grep
- Glob
- Skill
- "mcp__playwright__*"
---
# 代码阅读优先 → 生成 Cypress 测试(Metabase
你正在为 **Metabase** 代码库编写 Cypress E2E 测试。
在生成任何测试代码之前,你**必须**分析 React 组件源代码,
以理解 DOM 结构、选择器和用户流程。
## 阶段 0 — 调研
1. 在编写任何代码之前,先阅读现有辅助函数:
- `e2e/support/helpers/` — 所有共享辅助函数(restore、signInAs、openOrdersTable 等)
- `e2e/support/cypress_sample_database.ts` — 表/字段模式常量(ORDERS、PRODUCTS 等)
- `e2e/support/cypress_sample_instance_data.ts` — 实例特定 IDORDERS_DASHBOARD_ID、NORMAL_USER_ID 等)
2. 使用 Glob 搜索 `e2e/test/scenarios/`,找到与待测试区域最接近的已有 spec 文件。
研究其模式——严格匹配它们。
3. 使用 Glob 搜索 `frontend/src/metabase/`,找到该功能区域对应的 React 组件。
## 阶段 1 — 代码分析
阅读 React 组件源代码以理解 DOM 结构。不需要浏览器——源代码包含所有信息。
1. **查找相关组件**:使用 Glob 和 grep 搜索 `frontend/src/metabase/` 中该功能区域的代码。
2. **提取选择器**:在相关组件中 grep 搜索 `data-testid`
3. **记录可见文本**:阅读组件的 JSX,获取按钮标签、标题、占位符文本。
4. **记录 aria 属性**:在相关组件中 grep 搜索 `aria-label`
5. **理解用户流程**:阅读事件处理函数(onClick、onSubmit、onChange)以理解交互过程。
6. **查找 API 调用**grep 搜索 `Api.use``fetch``useQuery`、端点定义,识别需要拦截的 API 调用。
7. **与已有 spec 交叉对照**:找到同一区域内的已有 spec,重用其经过验证的选择器和 `cy.intercept` 模式。
## 阶段 2 — 启动后端
默认使用 `MB_EDITION=oss`。仅当用户明确要求编写企业版测试时,才使用 `MB_EDITION=ee`
使用 `run_in_background: true`(而不是 `&`)启动后端。
`bin/e2e-backend` 会自动检测后端是否已在运行并复用。
```bash
MB_EDITION=oss bin/e2e-backend
```
不要通过运行不相关的测试 spec 来手动生成快照。
`bun test-cypress` 运行器默认设置 `GENERATE_SNAPSHOTS: true`,并在运行任何 spec 之前
自动生成快照。通过 `/e2e-test` skill 在阶段 4 运行测试时,
如果快照尚不存在,将在首次运行时生成。
恢复干净的测试数据:
```bash
curl -sf -X POST http://localhost:4000/api/testing/restore/default
```
## 阶段 3 — 生成 Cypress Spec
遵循 Metabase Cypress 惯例:
@./../_shared/cypress-conventions.md
在代码分析中识别出 API 调用后,使用上面展示的拦截模式对它们进行桩代码替换或等待。
## 阶段 4 — 验证
生成 spec 后:
1. 检查所有导入的辅助函数是否存在(在 `e2e/support/helpers/` 中 grep 搜索)。
2. **你必须使用 `/e2e-test` skill** 来运行测试——不要直接运行 `bun test-cypress`
`/e2e-test` skill 处理版本选择、快照管理和正确的环境变量设置。
`/e2e-test GREP="should do the thing" --spec e2e/test/scenarios/<path>`
如果你创建了多个 `it()` 块,请逐一运行每个块以隔离失败。
## 阶段 5 — 修复失败(最多尝试 2 次)
测试失败时,**首先尝试根据 Cypress 输出修复**:
1. 查看失败截图(路径打印在 `(Screenshots)` 下方)。
2. 从控制台输出中查看错误消息和代码帧。
3. 修复测试并重新运行(回到阶段 4,步骤 2)。
如果在 2 次尝试后仍无法诊断问题,则进入阶段 6。
## 阶段 6 — Playwright 回退
仅在阶段 5 中经过 2 次修复尝试失败后才能进入此阶段。后端已在运行。
恢复干净的测试数据:
```bash
curl -sf -X POST http://localhost:4000/api/testing/restore/default
```
**绕过 CSP** 头(在导航之前执行——Metabase 提供了严格的 CSP,会阻止开发服务器脚本)。
使用 `browser_run_code` 进行设置:
```js
async (page) => {
// 移除 CSP 头以便页面加载(镜像 Cypress 的 chromeWebSecurity: false
await page.context().route('**/*', async (route) => {
const response = await route.fetch();
const headers = { ...response.headers() };
delete headers['content-security-policy'];
delete headers['content-security-policy-report-only'];
await route.fulfill({ response, headers });
});
// 通过 API 登录
const response = await page.request.post('http://localhost:4000/api/session', {
data: { username: 'admin@metabase.test', password: '12341234' }
});
const session = await response.json();
await page.context().addCookies([{
name: 'metabase.DEVICE',
value: session.id,
domain: 'localhost',
path: '/'
}]);
await page.goto('http://localhost:4000');
await page.waitForLoadState('networkidle');
return 'signed in';
}
```
**逐步维护观察日志。** 每次进行重要的 Playwright 交互后,
立即将观察到的内容追加到临时文件中,然后再进行下一次交互:
```bash
cat >> /tmp/e2e-observations.md << 'OBSERVATION'
## [页面/流程名称]
- URL: /question/notebook#...
- 点击: "Box plot" 按钮 → 可见文本 "Box plot",角色: radio
- 选择器: data-testid="viz-type-button"findByText("Box plot")
- API 调用: POST /api/dataset(在可视化类型变更时触发)
- 关键状态: 选择可视化类型后,摘要侧边栏显示指标选择器
OBSERVATION
```
对于每个页面/流程:
- 获取无障碍快照(`browser_snapshot`)。
- 点击交互式元素、填写表单、触发弹窗。
- **每执行一步后立即追加到观察日志**:URL、可见文本、aria 标签、`data-testid` 属性、API 调用。
- 对关键状态进行截图。
探索完成后:
1. 回读观察日志:`cat /tmp/e2e-observations.md`
2. 使用观察到的选择器和行为修复测试。
3. 重新运行测试(回到阶段 4)。
4. 清理:`rm -f /tmp/e2e-observations.md`
## 阶段 7 — 清理
所有测试通过后(或放弃修复失败后),**始终杀死端口 4000 上的后端**:
```bash
lsof -ti:4000 | xargs kill 2>/dev/null || true
```
不要使用宽泛的 `pkill` 模式——可能还有其他 Metabase 实例运行在不同的端口上。
阶段 2 启动的后端进程在 Claude 会话结束时**不会**被自动终止。
让其持续运行会浪费资源,并可能干扰将来的会话。务必清理。
## 禁止事项(工作流程)
- 不要将 Playwright 作为第一步——始终先分析源代码。
- 不要在阶段之间杀死后端——它应在整个过程中保持运行。
- 不要编造你在源代码中未找到或在浏览器中未观察到的选择器。
关于惯例层面的「禁止事项」(选择器、等待、辅助函数等),请参见阶段 3 中引用的惯例文件。