| name | page-to-e2e |
| description | 根据用户提供的页面地址(URL 或路由路径)为该页面生成完整的 Playwright e2e 测试,覆盖页面上所有可见功能(筛选、表格、按钮、弹窗、跳转、导出等)。先询问使用真实 API 还是 Mock 数据两种模式,再读取组件源码推断功能点,最后生成并运行测试。适用于用户说“给这个页面生成 e2e”“测一下 /xxx 页的功能”“写 playwright 测试”“为 xxx 页补 e2e”等场景。 |
Page → E2E 测试生成
根据用户提供的页面地址(URL、路由路径或组件文件路径),生成一套覆盖页面全部可见功能的 Playwright e2e 测试。
工作流
必须严格按顺序执行:
- 定位页面源码 → 根据用户给的地址找到组件文件
- 询问 API 模式 → 真实 API 还是 Mock 数据(必须问,不要自作主张)
- 勘探页面功能 → 读组件源码列出所有可测点
- 勘探项目约定 → 读现有 e2e 配置/用例,沿用项目既有模式
- 生成 spec 文件 → 按功能分组,每个功能一个 test
- 运行并修复 → 跑一次,失败就修,直到全绿
- 输出测试报告 → 按模板给出通过率、失败项、产物路径
跳过任何一步都会产生低质量测试。
Step 1:定位页面源码
用户输入可能是:
- URL:
http://localhost:5175/customer/list
- 路由路径:
/customer/list
- 组件文件:
frontend/src/views/customer/List.vue
- 页面描述:
客户列表页
用工具定位:
- URL/路径 → 搜路由配置(
src/router/**)找到对应组件
- 描述 → 按语义在
src/views/** 或 pages/** 搜索
禁止在没读组件源码的情况下凭空编写测试。
Step 2:询问 API 模式(关键)
必须用 AskUserQuestion 向用户提问,不要自己决定:
问题:这次 e2e 用什么数据源?
选项 A:真实 API(推荐)
- 贴近生产行为,能抓到后端 bug(如筛选失效、权限漏网)
- 依赖本地后端 + 数据库/Redis 就绪,需要预先登录态
选项 B:Mock 数据
- 用 page.route() 拦截 /api/** 返回固定 JSON
- 不依赖后端,跑得快,CI 稳定
- 只能测前端渲染和交互,测不到后端筛选/权限语义
选项 C:混合模式
- 查询类接口 Mock 固定返回,写操作(新增/删除)仅点到 UI 不真实提交
记录用户选择,后续 spec 生成按此分支走。两种模式的具体做法见 references/mock-strategies.md。
Step 3:勘探页面功能
读组件源码(template + script),列出所有可测点。输出一份功能清单让用户确认(可简化为一条消息列出,不必专门 ask),至少覆盖:
| 类别 | 具体点 |
|---|
| 渲染 | 页面标题、核心按钮、表单项(label 是否齐全)、表格列头 |
| 筛选 | 每个筛选项的入参下发 + 后端过滤语义(真实 API 模式下用不匹配值验证 total=0) |
| 分页 | 切页、切页大小 |
| 行操作 | 详情、编辑、删除、分配等(副作用操作只打开弹窗不提交) |
| 弹窗/抽屉 | 打开、字段齐全、关闭 |
| 跳转 | 新增/编辑按钮跳转目标路由 |
| 导出/下载 | 用 waitForResponse 验证接口调用,不要用 waitForEvent('download') |
| 权限/空态 | 无数据时的空态、无权限时的隐藏按钮 |
Step 4:勘探项目约定(产出 project-profile)
先检查 references/project-profile.md 是否已存在:
- 存在 → 直接读取并复用,跳过本步
- 不存在 → 按下表勘探,把结果写入
references/project-profile.md 作为本项目的配置缓存
| 维度 | 勘探来源 | 典型取值 |
|---|
| 测试框架 | package.json devDependencies、playwright.config.* | @playwright/test / Cypress / 其他 |
| baseURL 与后端地址 | playwright.config.*、前端构建配置(vite/webpack/next) | 前端 :5173-5175、后端 :8080/:3000/:8188 |
| API 代理前缀 | 前端构建配置里的 proxy 或环境变量 | /api → 后端 |
| 认证方式 | globalSetup / fixture / 登录相关源码 | Authorization: Bearer ${localStorage.token} / cookie / httpOnly |
| Token 存储键 | 前端 axios 拦截器或登录回调 | localStorage.token / access_token / sessionStorage.jwt |
| UI 库 | package.json dependencies | Element Plus / Ant Design Vue / Naive UI / Vant |
| UI 语言包 | main.ts/main.js 里的 locale 配置 | 中文 50 条/页 / 英文 50/page |
| 选择器风格 | 现有 spec 任选一个 | data-testid / getByRole / UI 库 class |
| API 路径前缀 | 现有接口调用 | /api/admin/** / /api/v1/** |
| 导出接口约定 | 业务代码搜 export | 响应 blob / 返回下载链接 |
| 过滤语义校验基线 | 无需勘探,固定策略 | 用不匹配值(2000-01-01、__nonexistent__)断言 total === 0 |
勘探完成后,在 references/project-profile.md 以 key-value 形式记录,后续 step 5、6 生成 spec 时直接引用这份 profile,不要再猜。
沿用项目既有模式,不要新造一套。
Step 5:生成 spec 文件
- 文件路径:与现有 spec 同目录,命名
{page-name}.spec.ts
- 按
test.describe 分组:渲染 / 筛选 / 行操作 / 弹窗 / 过滤语义
- 每个 test 只测一件事
- 副作用操作(新增/删除/分配/移入公海/提交表单)默认只验证"能打开弹窗"和"取消按钮能关闭",不实际提交,避免污染数据库
- 如果用户明确要求测写操作,改用 Mock 模式或在用例末尾手动清理数据
代码模板见 references/spec-templates.md。
Step 6:运行并修复
npx playwright test e2e/your.spec.ts --reporter=line
常见失败与修法:
| 失败现象 | 原因 | 修法 |
|---|
| 下拉选项找不到 | .el-select-dropdown 被 teleport 出多个实例 | 改用 getByRole('option', { name, exact: true }) |
| 分页 "50/页" 找不到 | 项目未设中文 locale | 改用英文 '50/page' 或先在项目里配 locale |
| 下载超时 | 前端 blob+a.click 不触发 download 事件 | 改用 waitForResponse(/\/api\/export/) |
| 筛选看起来通过但没真过滤 | 只验证了入参 URL,没验证返回 | 加一组"用不匹配值验证 total=0"的语义用例 |
| 点击后元素消失 | 动画未结束 | locator.waitFor({ state: 'visible' }) + waitForLoadState('networkidle') |
跑到全绿为止。测试全绿不代表功能真的对,见 references/semantic-pitfalls.md。
Step 7:输出测试报告
按以下模板在最终消息里回给用户:
## E2E 报告:{page-name}
**Spec 文件**:`e2e/xxx.spec.ts`
**数据源模式**:真实 API / Mock / 混合
**结果**:X passed / Y skipped / Z failed(耗时 Ns)
### 覆盖的功能点
- [x] 页面渲染(标题、按钮、表格列头)
- [x] 关键词搜索(入参 + 语义)
- [x] 标签筛选(入参 + 语义)
- [ ] 导出下载(已跳过,原因:…)
### 已知限制
- 副作用操作(新增/删除)只点到弹窗未提交
- xxx 功能依赖 yyy 数据,当前库为空已 skip
反模式
- ❌ 不读组件就写测试 → 必然漏功能或选择器错
- ❌ 不问用户就选真实 API / Mock → 每种都有代价,必须问
- ❌ 把其他项目的端口、认证方式、UI 库硬编码进来 → 每次换项目都要改,先勘探 project-profile
- ❌ 只验证入参下发不验证返回数据 → 假阳性,bug 照样漏(典型:MyBatis-Plus 顶层
.or() 打断 AND 链导致筛选失效,UI 看起来有下发参数但过滤无效)
- ❌ 副作用操作真实提交 → 污染数据库,跑完测试库里多出 N 条脏数据
- ❌ 用
waitForTimeout(5000) → 直接改 waitForResponse 或 locator auto-wait
- ❌ 选择器用 CSS class(如
.el-select-dropdown)→ teleport 多实例会歧义,优先 ARIA role
参考资料