# QMTV E2E 测试约定

LLMS 索引： [llms.txt](/llms.txt)

---

> 二十六期（2026-10-19）立册。对齐 pms e2e 约定骨架，按 QMTV 单库场景裁剪。
> 场景覆盖地图见 [e2e-qmtv-场景清单.md](/qmtv/test/e2e-qmtv-%E5%9C%BA%E6%99%AF%E6%B8%85%E5%8D%95/)。

## §1 初始世界

- **环境**：`bash scripts/dev/e2e/e2e-up.sh` 一键起——独立库 `qmtv_e2e`（DROP/CREATE 即全量 reset）+ 端口 `:3301`（与 dev :3300 三隔离：库/端口/产物文件名，可共存）+ 托管 `frontend/dist`（每次 up 强制重建，永测当前 src）。停环境 `e2e-down.sh`；快恢复（不重建库）`e2e-seed.sh`。
- **种子自检**：`e2e-verify.sh` 六项（healthz / admin·demo·producer 三账号登录 / models-active / style-presets）——绿才能跑用例。
- **账号矩阵**：admin@qmtv.com（管理员，credits 100000）/ demo@qmtv.com / producer@xingmang.cn——密码=seed 公开演示值，不入新密。API 造数的辅助账号一律 `e2e-` 前缀 email；storageState 册（`.auth/{admin,demo}.json`）由 setup 项目真实 UI 登录一次产出，用例零登录（对抗 login 20/min 限流）。
- **确定性规则**：未配置 `model_endpoints` → 生成类全走内置 Mock **同步 done**（无轮询等待、无外部超时）；Mock 图像/视频字节含随机源——**只断结构确定（卡片存在/URL 可达/台账行），绝不断素材内容**。
- **预置种子**：SQL seed（admin/demo+分销配置+计费 KV）+ Go seed（producer+示例任务链）。业务单据不进种子，一律 API 造数。
- **隔离与清理**：用例各自造数各自收尾（集成侧 integCleanup 同纪律）；失败不自愈——重跑前 `e2e-up`；**连跑两轮必须间隔 reset**（credits 水位会漂移）。

### 明令禁止

1. **禁 sleep 等待 UI 条件**——唯一合法等待面是 Playwright 自动等待/`expect.poll`/`waitForEvent`/`pollUntil`。豁免两处（登记）：①巡检类用例的路由间静置窗口（nav/admin 的 `waitForTimeout(400/1500)`、snapshots 截图前 `waitForTimeout(800)`——错误收集窗/渲染落定窗，非断言等待）；②截图前 `networkidle`。
2. **禁 retry**——config `retries: 0`（pms flaky 规则：禁 retry 掩盖；修复优先序 改等待>改定位>改断言>最后才是 retry）。
3. **禁断全局计数/绝对余额**——流水断言用 total 相对面 + 最新行身份；余额用 `assertBal` 恒等式（后端集成侧）。
4. **禁触 qmtv_main / pms_* / sw_main**——e2e 只操作 qmtv_e2e 单库（远程 139 共享实例纪律平移）。
5. **SSE 面禁 networkidle**——EventSource 长连接使 networkidle 永不触发；巡检用 load+双 rAF+静置窗口。

## §2 可测性硬规则

- **断业务结果，不断文案细节**：toast 只断关键短语（「拆解完成」「去字幕完成」）；选择器优先 role/placeholder/既有稳定 class，**不批量加 testid**（触碰面爆炸）；高频锚点按需单点补并在此登记。
- **失败留证**：trace `retain-on-failure` + screenshot `only-on-failure` → `e2e/.artifacts/`（gitignore）。
- **断言/截图分文件一行不共用**（pms 硬规则）：断言册 `playwright.e2e.config.ts`（testDir e2e，smoke 项目收 `assertion/*.spec.ts`）；截图册 `playwright.config.ts`（只收 `snapshots.spec.ts`）。
- **API 侧错误信封直接抛**（`e2e/lib/client.ts` apiReq）：非 2xx 抛响应体——静默吞错=测试说谎。
- **对话框断言用 `waitForEvent('dialog')`**（once+变体捕获有竞态——auth.spec 负样本抓获）；LoginView 错误走原生 alert，业务 confirm 各页分散（WorkbenchView 仅 repaint 空描述分支）——**用例写前先读该页 submit 源码，勿凭笔记推 dialogs**。

## §3 运行手册

```bash
bash scripts/dev/e2e/e2e-up.sh          # 起环境+种子自检（首次/失败后/连跑第二轮前必跑）
cd frontend
npm run test:e2e                        # 断言册（smoke，:3301）
npm run test:e2e:headed                 # 有头观察
npm run test:snapshots                  # 截图管线（二十七期手册出图）
npx playwright show-trace <trace.zip>   # 失败取证
bash scripts/dev/e2e/e2e-down.sh        # 停环境
bash scripts/ci.sh --e2e                # 全栈验收档（=--with-env 全部 + e2e 册）
```

## §4 flaky 处理规则

- 72h 内同一用例红 ≥2 次 → 摘出开单，不得带红合入；
- 7 天未修 → 删用例（场景清单「自动化?」列同步回落「待实现」）；
- **关键用例落地必配负样本演练**（涉及金额/回归锁/A1 教训面——改断言键名/路径必红才转正；A1 fixture 死键教训：键名错→锚死力失效）。巡检类（nav/admin 只读遍历）可豁免——harness 判死力由其它用例的有机红佐证（二十六期如实报告：admin.spec 无单独负样本，nav/auth/credits/dashboard/generation 五处有机红已证）。

## §5 场景登记与回归锁

- 场景 ID 唯一（`E2E-<模块>-<序号>`），ID 标注于 **spec 文件头注释**（用例名可不带前缀）——清单与 spec **双侧 grep 对账按场景 ID 级**（「自动化?=是」的行必须有对应 ID 的 spec 文件，清单「现状」列登记 ✅ 文件名）。〔二十七期勘误：原「用例同名对账」与实际不符——ID 在头注释非用例名，对账实为场景 ID 级 9=9+文件级 ✅ 列。〕
- 缺陷修复优先「并入现有场景加断言」（回归锁），不新开用例——本仓 `/review-plan` A 类「验收锚点」判死力机制直接挂此。
- 写链场景（导出 ZIP、充值审核、分销提现等）先登清单「待实现」行，后续期次升格。

---

反链：

- [QMTV 使用手册 · 文档索引](/qmtv/manual/readme/)
- [人工测试清单(QMTV QA 走查底稿)](/qmtv/manual/%E4%BA%BA%E5%B7%A5%E6%B5%8B%E8%AF%95%E6%B8%85%E5%8D%95/)
- [测试](/qmtv/test/)
