| name | webapp-e2e-testing-v3 |
| description | E2E testing for local web applications — constraint-first, execution-driven structure:
iron rules, 5-stage execution flow with inline principles, artifact specification.
Use this skill whenever the user wants to test a local web app,
validate UI behavior, walk through multi-step user flows, mock API responses for testing,
debug E2E test failures, capture screenshots, or understand E2E testing best practices.
Triggers on: "test this web app", "E2E test", "UI test", "browser test", "验证 UI",
"测试页面", "写个测试", "mock API", "端到端测试", "自动化测试", "帮我测一下",
"test the login", "checkout flow", "registration flow", "dashboard test", or any
mention of localhost testing, form validation, flow verification, or E2E debugging.
|
前端 E2E 测试
按以下顺序阅读:先扫铁律,再按执行流程走,产物规范在输出时查阅。
铁律与约束
不可协商。违反任何一条都意味着测试结果不可信。
全局
- 禁止跳阶段——阶段 N 门控未通过,不能进入阶段 N+1
- 禁止 JSON 格式输出报告,统一使用 report.md
- 截图路径使用相对路径,不使用绝对路径
环境
- HTTPS localhost 是正常配置——使用
ignore_https_errors,禁止降级为 HTTP
- 禁止修改 dev server 的任何配置(协议、端口、环境变量)
- 禁止重启 dev server 来切换配置
分析
- 分析未完成 5 项结论,禁止进入执行阶段
- 禁止在分析阶段执行测试操作或截取测试截图(允许 DOM 快照用于实检)
执行
- 禁止批量预先截图——只在确认 UI 已渲染后才截
- 禁止使用固定时间等待(sleep)替代语义等待
- 禁止修改被测页面的 DOM 结构、替换前端组件、通过
page.evaluate() 注入假数据
- 禁止使用 XPath 或基于绝对位置的选择器
报告
- 禁止在分析或执行阶段提前输出报告
- 报告必须基于最终运行结果,不得使用调试阶段的中间结果
执行流程
按时序走。每个阶段内联了判断原则和常见陷阱——不需要跳转到其他分区。
阶段 1:环境确认
目标:确保 dev server 可达,选择测试技术栈
怎么做:
- 启动 dev server(如尚未运行)
- 轮询检测可访问性(不用固定等待)
- 确认可达
技术栈选择:
仅靠浏览器交互就能完成?(导航、点击、输入、截图、读取文本——人坐在电脑前也能做的事)
- 是 → agent-browser CLI
- 否 → Playwright Python(需要 Mock API、注入全局变量、条件判断、循环、状态传递等代码级控制)
输出:dev server URL + 技术栈选择
门控:成功获得 HTTP/HTTPS 响应
阶段 2:静态分析
目标:搞清楚要测什么、需要什么数据、怎么定位元素——全部想清楚再动手
为什么不分析就直接操作会返工:写选择器前不分析 DOM 结构、写 mock 前不分析 API 契约,会导致选择器匹配错误元素、mock 数据结构不对、路由优先级错误。每次返工都消耗时间和 token。
怎么做:
-
澄清验证目标——怎样算通过?定义具体可验证的断言。操作改变数据时,断言应验证操作后的数据状态(如列表项数量变化),而非仅验证操作本身完成。如果用户输入缺少验证目标,主动要求澄清,不要猜测后直接执行
- 陷阱:用户说「测一下 XX 页面」就直接截图——可能截到空白加载态、错误页面
-
澄清用户流程——达到目标需要哪些操作步骤?每步操作后页面应呈现什么状态?
- 明确后用一句话概括测试计划:「验证[目标]:[步骤A] → [步骤B] → 断言[结果]」
-
追踪数据依赖——读源码,从 API 调用点到 UI 渲染的完整路径
- 为什么:API 响应结构 ≠ 代码期望结构,中间经历了
.map()、.filter() 等转换。凭直觉构造的 mock 数据几乎总是错的
- 陷阱:对照 API 类型定义直接写 mock,忽略组件中
items.map(i => ({ id: i.productId, name: i.title })) 的字段重命名
-
确认状态注入路径——同时检查三条路径:
- API 响应(拦截 HTTP 请求)
- 持久化存储(localStorage / sessionStorage / IndexedDB,页面脚本执行前预填充)
- 服务端注入的全局变量(
window.__INITIAL_STATE__ 等,拦截 HTML 响应替换)
- 陷阱:API mock 全部正确但页面状态不对——遗漏了 localStorage 或全局变量
-
DOM 实检——写选择器之前,必须执行以下之一(不可跳过):
- 源码实检:从路由配置出发,逐层追踪到最终渲染目标元素的叶子组件,确认目标元素的真实属性。包装组件(如
<SceneSwitcher>)不是终点——必须进入条件分支对应的实际渲染组件
- 运行时实检(推荐):通过
snapshot -i 或 DevTools 获取 DOM 结构快照,验证源码分析结论。当源码分析存在不确定(条件分支、多场景组件)时优先使用
- 选择器优先级:语义属性 >
data-testid > CSS 类名 > 永远不用 XPath
- 项目约定优先:如果项目已有选择器风格约定,遵循项目约定
- 陷阱:凭经验写
button:has-text('Confirm') 发现按钮文本为空(i18n 未渲染)
-
预判障碍——遮挡、iframe、受控组件
Mock 边界原则:mock 只能发生在网络请求层。
- ✅ 拦截 API 请求返回 mock JSON;拦截 HTML 替换内联全局变量
- ❌ 修改 DOM、替换组件、注入假数据——这样测试的已经不是真实代码
数据真实性原则:
- 字段类型必须与真实 API 一致——字符串字段不能传 number
- 数组至少 2-3 项——真实用户不会只有 1 条数据
- 条件渲染字段必须设正确值——
isNewUser: true 应触发新用户引导
- 路由注册顺序:具体路由在宽泛路由之前
- 陷阱:
amount: "100.00"(字符串)被 mock 成 amount: 100(number),.toFixed(2) 报错
测试用户可见行为:断言验证页面渲染结果(元素可见性、文本内容),而非内部状态变量(如 window.__userInfo__.isLoggedIn)。选择器优先使用用户可感知属性。
每次测试独立运行:确保环境干净,需要的数据通过 mock 创建,不假设数据库已有。
只控制你能控制的:对不受控的网络依赖(第三方 API、CDN)使用 mock,避免随机失败。
关键路径优先:聚焦核心用户流程,异常路径留给单元测试。
输出:5 项分析结论
- 环境确认:dev server URL 及可达性
- 数据依赖清单:API 端点及响应结构
- 交互路径清单:每个目标元素的可达条件
- 选择器方案:每个元素的定位策略(附 DOM 依据)
- 障碍预判:遮挡、iframe、受控组件等及应对策略
门控:5 项结论全部明确写出
阶段 3:测试执行
目标:按计划执行测试步骤,每步确认 UI 状态后再操作
怎么做:按「侦察-然后-行动」模式:导航 → 等待渲染 → 截图确认 → 操作 → 验证
操作前确认元素可达:DOM 中存在 ≠ 可交互。依次确认:
- ✅ 已在 DOM 中渲染
- ✅ 在视口内可见(必要时先滚动)
- ✅ 未被其他元素遮挡
- ✅ 未处于 disabled 状态
- ✅ 是目标元素本身,而非同类元素(如 readonly combobox vs 真实输入框)
等待机制:使用工具内置的语义等待(waitForSelector、waitForURL、web-first assertions),不用 sleep。3 秒在慢网络下不够,在快网络下浪费 2.5 秒。
受控组件:React/Vue 受控组件依赖合成事件(onChange),直接修改 DOM 属性不会触发状态更新。
- 用
fill() 触发完整事件链;不生效则改用 type() 逐字符输入
- 陷阱:
element.value = 'xxx' 直接设值——React state 完全没更新,按钮永远 disabled
处理遮挡:遇到「元素被拦截」时,按优先级处理:
- 先确认拦截层是否应该存在——预期弹窗先关闭它
- 非预期覆盖层(引导弹窗、通知)先关闭
- iframe 透明遮罩才用合成事件绕过——这是技术限制
- 叠加弹窗残留:
.first 可能匹配到 display:none 的残留实例,用可见性选择器定位活跃弹窗
force=True 使用三步优先级:
- 滚动到视口 + 正常点击(优先尝试)
- 关闭遮挡层 + 正常点击
force=True(最后手段,必须记录原因:技术限制如 iframe 透明遮罩、第三方组件限制)
- 陷阱:每次交互都加
force=True——弹窗不该出现时给出假 PASS
表单 disabled 诊断:按钮 disabled 不要重试或等待,立即诊断:
- 截图确认下拉菜单是否仍展开(最常见原因)
- 确认输入值是否正确(受控组件可能没真正更新)
- 确认 radio/checkbox 是否选中了正确选项
- 确认是否有校验错误提示
截图是证据不是装饰品:
- 截取完整视口
- 在关键状态变化点截图,确认 UI 已渲染后再截
- 失败截图必须包含失败时的实际页面状态
- 每个关键状态变化点只保留一张有效截图
输出:每个关键步骤的截图 + 测试步骤执行记录
门控:所有计划中的测试步骤已执行
阶段 4:结果验证
目标:逐项对照验证目标和实际结果
怎么做:逐项对比验证目标与实际结果。如有失败项,进入诊断流程。
失败不可跳过:每次失败都暴露一个信息缺口(mock 数据不对、选择器错误、状态假设错误),填补缺口就是提高可靠性。
- 陷阱:换方式试了三遍都失败→「这个场景测不了」。正确做法:截图→发现页面没渲染→追查发现 mock 缺字段→补上→通过
分层排查,先近后远:
- 浏览器 Console——有 JS 报错吗?
- 网络请求——API mock 是否生效?响应结构是否正确?
- 数据流——响应经过
.map() 转换后字段是否丢失?
- 条件渲染——
isVisible && <Component /> 中的条件值是否正确?
- 交互状态——元素是否被遮挡、disabled?
- 陷阱:先去调选择器语法,而根因是 Console 里有个 TypeError 导致组件树崩溃
诊断结论必须有证据:截图、网络日志、Console 日志、源码阅读——四者至少有其二。记录修改了什么、为什么这样修改。
一次只修复一个变量:不要同时改动 mock、选择器和交互逻辑。改完立即验证,确认有效再处理下一个。
输出:每项验证目标的通过/失败结论 + 失败项的诊断记录
门控:每项验证目标都有明确的通过/失败结论
阶段 5:报告输出
目标:输出完整可追溯的测试报告
报告回答三个问题:
- 测了什么——覆盖的用户流程和验证点
- 怎么测的——关键操作序列和数据设置
- 结论可信吗——如有任何阻止完整验证的限制,必须显式说明
失败报告必须包含:
- 失败步骤的截图(页面当前实际状态)
- 失败时的网络状态
- 失败前的关键操作序列——「在 XXX 之后,预期 YYY,实际 ZZZ」
- 已尝试的诊断步骤和结论
未覆盖项必须显式说明。
输出:report.md + 截图(格式见产物规范)
门控:report.md 已写入指定路径
产物规范
输出的硬性格式规范。
输出目录
docs/e2e-testing-output/<scenario-name>/
├── report.md
├── screenshots/
│ ├── step01-<description>.png
│ ├── step02-<description>.png
│ └── ...
└── scripts/ # 仅 Playwright 模式时存在
├── test-<scenario>.py
└── template-playwright.py # 标准模板:console/pageerror 监听、API 计数、evaluate 用法
- 根目录固定为
docs/e2e-testing-output/,用户指定其他路径时以用户为准
- 每次测试创建独立子文件夹(
<scenario-name>/),不同测试的产物物理隔离
- 截图统一放入
screenshots/ 子目录,不与报告和脚本混放
- Playwright 模式下,参考
scripts/template-playwright.py 构建测试脚本,内含 console/pageerror 监听、API 调用计数、evaluate 用法等标准模式
截图命名
格式:stepNN-<description>.png
示例:step01-homepage-loaded.png、step02-after-search.png、step03-error-button-disabled.png
- 不含时间戳——每次运行直接覆盖,不堆积
description 用英文短横线连接,描述截图内容和状态
- 每个关键状态变化点只保留一张有效截图
报告格式
统一为 report.md,禁止 JSON。固定 section 结构:
# E2E 测试报告:<scenario-name>
## 测试摘要
- 测试结果:通过 / 部分通过 / 失败
- 测试时间:YYYY-MM-DD HH:mm
- Dev Server:<URL>
## 验证目标
(从分析阶段产出的验证目标直接填入)
## 测试步骤与结果
| 步骤 | 操作 | 预期 | 实际 | 结果 |
|------|------|------|------|------|
| 1 | ... | ... | ... | ✅/❌ |
## 截图证据

(每个关键步骤的截图内联展示)
## Mock 配置
(使用的 Mock 端点和注入方式,便于复现)
## 未覆盖项
(如有任何未验证的场景,显式说明)
- 报告必须基于最终运行结果,不得使用调试阶段的中间结果
- 截图路径使用相对路径(相对于
report.md),不使用绝对路径