test(08-07-e2e-fixture-repair): 修复交互回归夹具与附件定位

这个提交包含在:
2026-08-07 09:05:28 +08:00
父节点 ab90bc1ce4
当前提交 88b97b40af
共修改 14 个文件,包含 303 行新增和 20 行删除
@@ -7,14 +7,17 @@
| 子任务 | 交付物 | 主要风险 |
| --- | --- | --- |
| `08-05-interaction-pattern-unification` | 错误/状态/文案/反馈/移动动作一致性,删除差旅自动选中 | 跨页面文案回归、共享组件影响 |
| `08-07-interaction-pattern-browser-regression` | 补齐低风险统一的本地 Chromium 与企业微信 UA 模拟证据 | 证据边界、历史 spec 漂移 |
| `08-07-e2e-fixture-repair` | 修复 R6/R111 committed E2E fixture 与断言,勘误历史诊断 | 测试口径被误删、fixture 权限漂移 |
| `08-05-business-detail-routing` | 采购、资产已有详情态迁到独立路由 | URL 兼容、返回、dirty guard |
| `08-05-contract-detail-workspace` | 新建合同详情、终止状态、迁移和事务审计 | 状态机、权限、迁移、审计 |
## 2. 执行顺序
1. 先实施低风险统一,形成后续页面可复用的错误消息、状态兜底和移动动作模式。
2. 再迁移采购/资产路由;该子任务只消费共享模式,不修改合同、差旅、付款或请假。
3. 合同详情作为独立能力最后实施,必须单独批准其 schema、状态机与迁移计划。
2. 补齐低风险统一的浏览器证据,并修复补跑暴露的 committed E2E fixture/断言红灯。
3. 再迁移采购/资产路由;该子任务只消费共享模式,不修改合同、差旅、付款或请假。
4. 合同详情作为独立能力最后实施,必须单独批准其 schema、状态机与迁移计划。
父子关系不隐含自动授权。每个子任务都要独立执行 `task.py start`、验证、提交与归档。
@@ -28,7 +31,7 @@
## 4. 集成与回滚
- 三个子任务独立提交;任何后续任务失败时可回滚该子任务,不回滚已经验收的前序任务。
- 三个产品交付子任务和质量支撑任务均独立提交;任何后续任务失败时可回滚该子任务,不回滚已经验收的前序任务。
- 路由兼容保留旧链接过渡,避免低风险任务和路由任务必须同时上线。
- 合同 migration 与前端详情同批兼容发布;生产应用、迁移和数据写入另需授权。
- 最终集成复核只检查跨任务一致性、链接、共享组件和文档事实,不重做每个子任务的全部测试。
@@ -2,8 +2,8 @@
## 1. 父任务职责
- [ ] 审核三个子任务的 PRD、design 和 implement,确认范围无重叠。
- [ ] 按“低风险统一 → 采购/资产路由化 → 合同详情”顺序分别请求实施批准。
- [ ] 审核三个产品交付子任务及质量支撑子任务的 PRD、design 和 implement,确认范围无重叠。
- [ ] 按“低风险统一 → 浏览器补跑 → E2E fixture 修复 → 采购/资产路由化 → 合同详情”顺序分别请求实施批准。
- [ ] 不启动父任务作为业务代码实现目标;启动拥有下一交付物的子任务。
## 2. 子任务交付顺序
@@ -15,6 +15,8 @@
### 2.2 采购与资产详情独立路由化
进入本节前,必须完成 `08-07-interaction-pattern-browser-regression` 与 `08-07-e2e-fixture-repair`,确认 committed 交互回归 spec 无已知红灯。
- [ ] 在低风险任务完成后迁移采购/资产详情路由与旧链兼容。
- [ ] 验证所有仓库内入口、dirty guard 和浏览器返回,独立提交和归档。
@@ -26,7 +28,7 @@
## 3. 父任务最终集成门禁
- [ ] 三个子任务验收结果与父 PRD 逐项对账。
- [ ] 三个产品交付子任务和质量支撑任务的验收结果与父 PRD 逐项对账。
- [ ] 确认普通用户错误无技术前缀,采购/资产新链接稳定,合同状态/证据边界未漂移。
- [ ] 确认差旅未恢复、付款/请假未路由化、非涉密合同附件未提前实现。
- [ ] 运行父任务 validate、process contract、docs registry、Trellis governance 和 `git diff --check`。
@@ -14,10 +14,11 @@
## Scope
- 父任务只管理三个子任务的边界、顺序、验收和最终集成复核。
- 父任务管理三个产品交付子任务及必要的质量支撑子任务,维护边界、顺序、验收和最终集成复核;质量支撑任务不扩张产品范围。
- 低风险子任务负责跨页面文案、错误反馈、状态兜底、首页优先级、移动动作容器和差旅自动选中缺陷。
- 采购/资产子任务负责两个已有详情态台账的独立路由迁移与旧链接兼容。
- 合同子任务负责从零合同详情工作区、`terminated` 状态和事务审计设计/实施。
- `08-07-interaction-pattern-browser-regression` 已补齐本地 Chromium/UA 模拟证据;`08-07-e2e-fixture-repair` 负责关闭补跑时暴露的 committed 测试红灯,二者均为质量支撑子任务。
- 付款批次、请假和完整差旅不做详情路由化;非涉密合同附件能力按后续规约任务处理。
## Requirements
@@ -29,13 +30,15 @@
- R5:视觉与交互分析优先遵循项目指定的 `frontend-design`;本会话该 skill 不可用时,只以 `ui-ux-pro-max`、现有共享组件和产品事实源做保守补充,不创建新的视觉体系。
- R6:不得借一致性治理扩张业务模块、重做既有状态机、改变企业微信/OA 边界、放宽权限或触碰真实数据。
- R7:完整差旅属于冻结模块,不做详情路由化;仅在低风险子任务中删除自动选择第一条记录的缺陷,不恢复或扩展差旅能力。
- R8:三个子任务独立实施与批准;低风险统一先行,采购/资产路由化随后,合同详情在其独立规划获批后另行实施。
- R8:三个产品交付子任务独立实施与批准;低风险统一先行,浏览器证据和 E2E fixture 红灯清零后再做采购/资产路由化,合同详情在其独立规划获批后最后实施。
- R9:每个子任务独立完成本地验证、可访问性/交互回归和 Trellis 门禁;只有部署与公网关键路径检查通过后,才能告知用户线上可测。
- R10:浏览器补跑与 E2E fixture 修复只维护验证可信度和 committed 测试绿色,不新增页面、权限、API、状态机或数据库能力。
## Acceptance Criteria
- [ ] 三个子任务均具有独立、可观察且不互相重复的验收标准。
- [ ] 低风险统一先于详情路由化完成,路由化复用已经收敛的文案、反馈和动作组件。
- [ ] 浏览器补跑与 committed E2E fixture 修复在采购/资产路由化开工前完成,不把已知红灯带入后续子任务。
- [ ] 当前仍存在的问题被纳入范围;错误码前缀按覆盖变量名的完整搜索结果治理,不因错误的窄模式搜索被误判为已修复。
- [ ] 付款批次继续使用现有显式选择详情形态,不新增独立详情路由。
- [ ] 完整差旅继续冻结;只删除首屏自动选中第一条记录的缺陷。
@@ -57,6 +60,7 @@
- 每个子任务先运行自身聚焦测试、typecheck/build 和任务 validate,再运行父任务集成门禁。
- 父任务最终运行 `node scripts/qa/check-process-contract.mjs --baseline --json`、`node scripts/qa/audit-docs.mjs --check-registry`、`node scripts/qa/check-trellis-governance.mjs` 和 `git diff --check`。
- 路由子任务覆盖采购/资产深链、旧 `?id=` 兼容、返回与未保存保护;合同子任务额外覆盖 Prisma 校验、迁移状态、状态不变量、事务审计失败回滚和只读 reviewer。
- E2E fixture 修复直接运行 R6 七视口 spec 与 R111 双端 spec,不使用临时副本冒充 committed suite 通过。
## Open Questions
@@ -2,7 +2,7 @@
"id": "product-interaction-consistency",
"name": "product-interaction-consistency",
"title": "产品交互与页面操作一致性治理",
"description": "统一小团队 OA 的页面操作模型,按低风险交互、采购资产路由、合同详情三项独立交付并完成集成验收。",
"description": "统一小团队 OA 的页面操作模型,按低风险交互、采购资产路由、合同详情三项产品交付独立实施;浏览器回归与 E2E fixture 子任务作为质量支撑,完成后再进入路由和迁移阶段。",
"status": "planning",
"dev_type": null,
"scope": "frontend-governance-and-three-child-integration",
@@ -22,12 +22,13 @@
"08-05-interaction-pattern-unification",
"08-05-business-detail-routing",
"08-05-contract-detail-workspace",
"08-07-interaction-pattern-browser-regression"
"08-07-interaction-pattern-browser-regression",
"08-07-e2e-fixture-repair"
],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {
"description": "统一小团队 OA 的页面操作模型,按低风险交互、采购资产路由、合同详情三项独立交付并完成集成验收。"
"description": "统一小团队 OA 的页面操作模型,按低风险交互、采购资产路由、合同详情三项产品交付独立实施;浏览器回归与 E2E fixture 子任务作为质量支撑,完成后再进入路由和迁移阶段。"
}
}
@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
@@ -0,0 +1,55 @@
# 技术设计
## 1. 修改边界
本任务只修改两份 Playwright spec 与已归档浏览器回归记录:
- `client/tests/e2e/r6-mobile-responsive-system-hotfix.spec.ts`
- `client/tests/e2e/r111-invoice-reimbursement-guard.spec.ts`
- `.trellis/tasks/archive/2026-08/08-07-interaction-pattern-browser-regression/implement.md`
- `.trellis/tasks/archive/2026-08/08-07-interaction-pattern-browser-regression/openclaw-test-prompt.md`
- `.trellis/tasks/08-05-product-interaction-consistency/prd.md`
- `.trellis/tasks/08-05-product-interaction-consistency/design.md`
- `.trellis/tasks/08-05-product-interaction-consistency/implement.md`
`client/src`、`server/`、权限映射、路由和产品行为只作为只读事实源,不在修改范围。
## 2. R6 fixture 权限链
R6 登录夹具通过 `/api/me` 返回合成用户,但 `AuthContext` 不消费响应中的任意 `permissions` 字段,而是在 `client/src/auth/AuthContext.tsx:260` 使用 `resolvePermissionsForRoles(me.roleCodes)` 生成权限。
当前合成用户角色为 `manager`、`employee`,不能获得 `leave.manage`。`MobileLeavePage` 因此把 `?view=form` 回落为 `records`,导致仍存在的 `mobile-leave-form` 不渲染。
设计选择:给该 spec 的合成用户增加 `hr` 角色。`hr` 是当前角色表中最小的非通配选择,可通过真实角色解析链产生 `leave.manage`;不使用 `admin` 的 `*`,也不添加不会被消费的 mock 字段。
兼容检查:R6 的七个设备上下文共享该 fixture,必须完整运行 7/7,确认新增合成角色没有改变其他导航、审批、报销或移动布局断言。
## 3. R111 语义定位
R111 上传附件后,页面可能同时出现待上传摘要和已上传附件行。页面级 `getByText("付款截图.png")` 对 DOM 数量敏感,不能证明用户可打开已上传附件。
设计选择:PC 与移动两处都改为按可访问名称定位已上传附件按钮:
```ts
getByRole("button", { name: "查看补充附件:付款截图.png" })
```
该断言唯一、面向用户动作,并与 PC/移动运行时已有 `aria-label` 一致;不使用 `.first()` 或索引规避 strict mode。
## 4. 归档记录勘误
归档记录不删除当时的执行事实:原始 committed R6 spec 失败,临时 runner 跳过该断言后其余移动矩阵通过。只纠正因果诊断:失败来自 fixture 角色不能解析出 `leave.manage`,不是表单取消或企微/OA 边界要求删除覆盖。
证据文件名 `r6-stale-leave-readonly-320.png` 作为历史产物不改名;文字记录注明文件名不代表最终诊断。
## 5. 回滚
本任务没有数据迁移或运行时代码回滚。若测试修复引入意外回归,回滚范围仅为:
- 移除 R6 fixture 新增的 `hr` 角色;
- 恢复 R111 两处原定位器;
- 恢复归档文字勘误。
父任务规划只增加质量支撑任务登记和顺序,不改变三个业务交付物;若登记需回滚,只撤销该任务行与顺序说明。
回滚后会重新暴露两条已知红灯,因此优先修正断言/fixture 并 roll-forward,不以删除覆盖作为回滚手段。
@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
@@ -0,0 +1,53 @@
# 执行计划
## 1. 实施前
- [x] 读取 `.trellis/workflow.md`、本 task PRD/design、前端 spec 与测试规约。
- [x] 记录 `git status --short`,保护其他任务变更。
- [x] 用户批准最新规划摘要后运行 `task.py start 08-07-e2e-fixture-repair`。
- [x] 启动 loopback Vite;只使用合成 mock,不访问公网或真实账号。
## 2. 测试修复
- [x] 在 R6 fixture 的 `employee.roleCodes` 增加 `hr`;不修改产品角色映射或添加伪权限字段。
- [x] 保留 `/mobile/leave?view=form` 与 `mobile-leave-form` 断言。
- [x] 将 R111 PC、移动两处附件上传后断言改为语义化“查看补充附件”按钮定位。
- [x] 搜索确认不存在本任务遗漏的同类 `getByText("付款截图.png")`。
## 3. 归档勘误
- [x] 修正归档 `implement.md` 中三处 R6 原因描述,保留临时 runner 历史事实。
- [x] 修正归档 `openclaw-test-prompt.md` 中两处错误诊断。
- [x] 搜索确认归档目录不再把该失败描述为错误的产品/断言诊断。
- [x] 更新父任务 PRD/design/implement:登记两个质量支撑子任务,并固定 fixture 修复先于采购/资产路由化。
## 4. 验证
- [x] committed R6 spec 聚焦运行:7 passed,退出码 0。
- [x] committed R111 spec 完整运行:2 passed,退出码 0。
- [x] `cd client && npm run typecheck && npm run build`。
- [x] task validate、process contract、Trellis governance、`git diff --check`。
- [x] `git diff --name-only -- client/src server` 为空。
## 5. 收尾
- [x] 只读复核确认没有降低测试口径、删除断言或扩大真实权限。
- [x] 使用中文 commit message 并引用 `08-07-e2e-fixture-repair`。
- [ ] 归档任务;不宣称部署、公网或企业微信真机已验证。
## 验证记录
- R6:7 个视口全部通过;`mobile-leave-form` 断言在 committed spec 中执行并通过。
- R111:PC 与企业微信 UA 用例均通过;两处附件断言均使用唯一语义按钮定位。
- 静态门禁:typecheck、build、task validate、process contract、Trellis governance、`git diff --check` 均通过;docs audit 0 errors,保留既有 22 个 warning。
- 边界:本地 Chromium/UA 模拟和合成 mock;未执行部署、公网、真实账号、企业微信真机或生产数据验证。
## 只读复核
- 未改产品运行时代码;没有删除或跳过 `mobile-leave-form`,没有使用 `.first()` 掩盖 R111 的重复定位。
- 当前 task 与父任务规划仍保留,提交和归档在最终门禁后执行。
## 回滚点
- 测试文件改动均为单行/小范围 fixture 与定位器调整,可独立回退。
- 归档勘误属于事实修正;除非新证据推翻权限链,不应回退为“表单已取消”的错误诊断。
@@ -0,0 +1,124 @@
# 交互回归 E2E 夹具修复
## Goal
修复 `08-07-interaction-pattern-browser-regression` 已确认但未落入仓库测试文件的两条 Playwright 红灯,使 R6 移动响应式矩阵和 R111 报销附件双端用例可以直接从 committed spec 运行通过;同时勘误已归档任务对 R6 失败原因的错误描述,防止后续人员误删仍有效的 OA 应急请假表单覆盖。
本任务只修测试夹具、断言和审计记录,不改变产品权限模型、请假行为、报销附件行为或运行时代码。
## Background
### R6:权限门控导致表单不可见
- `client/src/pages/MobileLeavePage.tsx:238` 仅在当前权限包含 `leave.manage` 时令 `canUseOaEmergencyLeave=true`;`:253` 会把无该权限用户的 `?view=form` 强制回落到 `records`。
- `mobile-leave-form` 仍存在于 `client/src/pages/MobileLeavePage.tsx:498`,不是已取消能力,原断言覆盖的是仍有效的 OA 应急请假表单。
- `client/tests/e2e/r6-mobile-responsive-system-hotfix.spec.ts:25-47` 的 fixture 用户只有 `manager`、`employee` 两个角色。`client/src/auth/AuthContext.tsx` 通过 `resolvePermissionsForRoles(me.roleCodes)` 生成前端权限,不读取 fixture 自造的 `permissions` 字段;上述两角色均不产生 `leave.manage`。
- 当前角色表中最小可产生 `leave.manage` 的非通配角色是 `hr`。本任务应给 R6 合成用户增加 `hr` 角色,从角色解析链真实获得该权限,而不是删除 `mobile-leave-form` 断言或修改产品门控。
### R111:宽泛文本定位触发 strict mode
- `client/tests/e2e/r111-invoice-reimbursement-guard.spec.ts:680` 与 `:812` 上传 `付款截图.png` 后都使用页面级 `getByText("付款截图.png")`。
- 移动端上传后,待上传摘要会渲染 `付款截图.png · 1 KB`,已上传附件操作入口又渲染 `付款截图.png`,页面级文本定位命中两个元素并触发 Playwright strict mode;产品 DOM 和上传行为本身正确。
- PC、移动附件行均有稳定的语义化按钮名 `查看补充附件:付款截图.png`。本任务统一改用该 role/name 断言,证明附件已成为可操作材料,同时避免 `.first()` 的顺序依赖。
### 归档勘误
- 已归档任务的 `implement.md` 与 `openclaw-test-prompt.md` 把 R6 失败描述为“过期请假表单断言”或与“企微主办、OA 只读”边界冲突,这一诊断不正确。
- 勘误必须保留当时“临时 runner 跳过该断言后其余矩阵通过”的历史事实,同时明确后续复核结论:fixture 缺少能解析出 `leave.manage` 的角色,导致 `effectiveView` 回落到 `records`。
## Scope
- 修复 R6 spec 的合成用户角色,使既有 `mobile-leave-form` 断言在七个视口继续执行并通过。
- 将 R111 PC 与移动端两处附件上传后断言收窄到语义化“查看补充附件”按钮。
- 勘误归档浏览器回归任务中所有错误的 R6 原因描述,保留历史执行事实与证据边界。
- 在父任务 PRD/design/implement 中登记浏览器回归补跑与本任务为质量支撑子任务,明确本任务先于采购/资产路由化执行,不改变三项产品交付范围。
- 直接运行仓库 committed spec 验证,不再依赖 `/tmp` 临时副本或删除断言的 runner。
## Requirements
- R1:R6 fixture 用户的 `roleCodes` 增加 `hr`,通过现有 `resolvePermissionsForRoles` 获得 `leave.manage`;不得向 `/api/me` mock 添加不会被消费的伪 `permissions` 字段。
- R2:保留 `/mobile/leave?view=form`、`mobile-leave-form` 和移动稳定性断言;不得删除、跳过或降级该页面覆盖。
- R3:R111 的 PC 与移动附件断言均改为 `getByRole("button", { name: "查看补充附件:付款截图.png" })` 或等价的唯一语义定位;不得使用页面级宽泛 `getByText` 或依赖 DOM 顺序的 `.first()`。
- R4:归档记录明确写出 `leave.manage` 权限门控、`effectiveView` 回落和 fixture 角色缺口;删除“表单已取消”“过期断言”“企微主办所以不应覆盖该表单”等误导性结论。
- R5:不修改 `client/src`、`server/`、角色权限映射、路由、API、数据库、依赖或产品功能;只允许改两份 E2E spec、归档勘误、父任务的质量子任务登记和当前 Trellis task。
- R6:截图仍写入各自 spec 的既有证据目录,不覆盖其他任务归档证据;测试数据继续使用合成内容。
- R7:父任务继续以低风险统一、采购/资产路由化、合同详情为三个产品交付物;浏览器补跑与 fixture 修复只作为质量支撑和执行前置,不增加产品能力。
## Acceptance Criteria
- [x] A1:直接运行 committed `r6-mobile-responsive-system-hotfix.spec.ts` 聚焦用例,七个视口全部通过,报告为 `7 passed`、退出码 0,且每个视口实际执行 `mobile-leave-form` 可见性与移动稳定性断言。
- [x] A2:直接运行 committed `r111-invoice-reimbursement-guard.spec.ts`,PC 与企业微信 UA 两个用例全部通过,报告为 `2 passed`、退出码 0,不再出现 `付款截图.png` strict mode violation。
- [x] A3:归档 `implement.md` 与 `openclaw-test-prompt.md` 的 R6 描述已改为权限 fixture 诊断,并保留临时 runner 历史事实;目标错误诊断搜索无命中。
- [x] A4:变更文件仅包含两份 E2E spec、两份归档记录、父任务四份规划文件和当前 task 文件;`client/src`、`server/` 无改动。
- [x] A5:`cd client && npm run typecheck && npm run build`、任务 validate、流程合同、Trellis governance 与 `git diff --check` 全部通过。
- [x] A6:父任务明确记录本质量修复先于 `08-05-business-detail-routing`,合同迁移任务仍最后实施;父任务的三个产品交付边界不变。
## Out of Scope
- 不删除或隐藏 OA 应急请假表单,不调整 `leave.manage` 产品门控,不扩大真实角色权限。
- 不修改报销材料 DOM、附件上传/预览逻辑或可访问名称。
- 不重构全仓 E2E fixture/角色体系,不清理其他历史测试。
- 不承诺本任务运行全仓全部 Playwright spec;验收对象是两份已知红灯的 committed spec 及其完整用例。
- 不执行部署、公网、真实账号、企业微信真机或生产数据验证。
## Test Plan
### 1. 基线与修复验证
在本地 loopback Vite 与合成 API mock 下单 worker 执行:
```bash
cd client
E2E_MODE=local E2E_BASE_URL=http://127.0.0.1:5178 npx playwright test \
tests/e2e/r6-mobile-responsive-system-hotfix.spec.ts \
--grep '关键移动页面跨视口自适应且首页宫格不挤压' \
--workers=1 --reporter=list
E2E_MODE=local E2E_BASE_URL=http://127.0.0.1:5178 npx playwright test \
tests/e2e/r111-invoice-reimbursement-guard.spec.ts \
--workers=1 --reporter=list
```
R6 必须报告 7/7;R111 必须报告 2/2。不得使用临时副本、临时删除断言或重定向到当前任务之外的证据目录来替代 committed spec 结果。
### 2. 静态与流程门禁
```bash
cd client && npm run typecheck && npm run build
python3 ./.trellis/scripts/task.py validate 08-07-e2e-fixture-repair
node scripts/qa/check-process-contract.mjs --baseline --json
node scripts/qa/check-trellis-governance.mjs --json
git diff --check
```
收尾时额外确认:
- `rg -n '过期请假|企微主办.*OA 只读' .trellis/tasks/archive/2026-08/08-07-interaction-pattern-browser-regression` 不再命中本次错误诊断。
- `git diff --name-only -- client/src server` 为空。
## 验证记录
- `task.py start 08-07-e2e-fixture-repair`:退出码 0,任务状态为 `in_progress`。
- R6 committed spec:`E2E_MODE=local E2E_BASE_URL=http://127.0.0.1:5178 npx playwright test tests/e2e/r6-mobile-responsive-system-hotfix.spec.ts --grep '关键移动页面跨视口自适应且首页宫格不挤压' --workers=1 --reporter=list`,退出码 0,`7 passed`;七个视口均执行 `mobile-leave-form` 可见性断言。
- R111 committed spec:`E2E_MODE=local E2E_BASE_URL=http://127.0.0.1:5178 npx playwright test tests/e2e/r111-invoice-reimbursement-guard.spec.ts --workers=1 --reporter=list`,退出码 0,`2 passed`。
- `cd client && npm run typecheck`:退出码 0;`npm run build`:退出码 0(仅有既有 Browserslist 数据过期提示)。
- `python3 ./.trellis/scripts/task.py validate 08-07-e2e-fixture-repair`:退出码 0。
- `node scripts/qa/check-process-contract.mjs --baseline --json`:`PASS`,0 errors / 0 warnings。
- `node scripts/qa/audit-docs.mjs --check-registry`:0 errors,保留既有 22 个未注册 docs warning。
- `node scripts/qa/check-trellis-governance.mjs --json`:`PASS`,0 errors / 0 warnings。
- `git diff --check`:退出码 0;`git diff --name-only -- client/src server` 为空;归档错误诊断搜索无命中。
## 只读复核
- 仅修改两份 committed E2E spec、两份归档记录、父任务规划登记和当前 Trellis task;未修改 `client/src`、`server/`、权限映射、API、数据库、依赖或生产配置。
- `mobile-leave-form` 断言仍在 committed spec 中,未删除、跳过或降级;R111 未使用宽泛文本定位或 `.first()`。
- `hr` 只加入 R6 合成 fixture,不改变真实角色权限;测试使用 loopback 与合成 API mock,未执行公网、部署或真机验证。
## Risks and Deferred Items
- `hr` 角色还会带来其他 HR 权限,但只存在于该 spec 的合成用户中;必须以 R6 全部七个视口回归确认没有改变页面可达性断言。相比 `admin` 通配角色,`hr` 是当前角色表中更小的有效选择。
- R111 的语义化按钮名来自当前产品可访问名称;若未来文案调整,应同步按用户可感知动作更新测试,不应退回宽泛文本或 `.first()`。
- 本任务只消除两条已知 committed spec 红灯;其他全仓测试结果仍以各自任务和门禁为准。
## Open Questions
无。失败原因、最小修复、验收命令和不做范围均已由代码与用户复核确认。
@@ -0,0 +1,39 @@
{
"id": "e2e-fixture-repair",
"name": "e2e-fixture-repair",
"title": "交互回归 E2E 夹具修复",
"description": "修复交互回归中两条已知 Playwright 红灯:为 R6 合成用户补足可解析为 leave.manage 的角色,并将 R111 附件断言收窄到语义化操作入口;同步勘误已归档诊断,不改业务代码。",
"status": "in_progress",
"dev_type": null,
"scope": "repair-r6-r111-committed-e2e-fixtures-and-archive-diagnosis",
"package": "client",
"priority": "P2",
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-08-07",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "08-05-product-interaction-consistency",
"relatedFiles": [
"client/tests/e2e/r6-mobile-responsive-system-hotfix.spec.ts",
"client/tests/e2e/r111-invoice-reimbursement-guard.spec.ts",
".trellis/tasks/archive/2026-08/08-07-interaction-pattern-browser-regression/implement.md",
".trellis/tasks/archive/2026-08/08-07-interaction-pattern-browser-regression/openclaw-test-prompt.md",
".trellis/tasks/08-05-product-interaction-consistency/prd.md",
".trellis/tasks/08-05-product-interaction-consistency/design.md",
".trellis/tasks/08-05-product-interaction-consistency/implement.md",
"client/src/pages/MobileLeavePage.tsx",
"client/src/auth/permissions.ts",
"docs/测试规约与回归门禁.md"
],
"notes": "只修测试夹具、测试定位器、父任务质量子任务登记和历史诊断;禁止删除仍有效的 mobile-leave-form 覆盖,禁止修改产品权限或运行时代码。",
"meta": {
"description": "修复交互回归中两条已知 Playwright 红灯:为 R6 合成用户补足可解析为 leave.manage 的角色,并将 R111 附件断言收窄到语义化操作入口;同步勘误已归档诊断,不改业务代码。"
}
}
@@ -17,7 +17,7 @@
- [x] Portal 聚焦场景通过:首屏顺序、WelcomeDialog 权限过滤、区域错误重试、登录/Portal Tab 焦点(3/3)。
- [x] PC 审批详情场景通过:后果说明、二次确认取消、转审 10 字门槛、Tab 顺序(1/1)。
- [x] R6 七视口移动矩阵通过:使用临时运行副本仅移除过期请假表单断言,七个视口均通过;未改仓库测试或业务代码。
- [x] R6 七视口移动矩阵通过:使用临时运行副本暂时跳过 `mobile-leave-form` 断言,七个视口均通过;后续复核确认表单仍存在,原失败原因是 fixture 角色不能解析出 `leave.manage`,导致 `effectiveView` 回落到 `records`;未改仓库测试或业务代码。
- [x] R111 企业微信 UA 报销详情通过:原始 spec 的重复文本 strict locator 先分类为测试夹具问题,临时副本仅加 `.first()` 后 1/1 通过;未改仓库测试或业务代码。
- [x] Wave3 PC/移动代表场景通过:审批/报销详情、返回链路、断点双栏和截图(2/2)。
- [x] R1/R6 状态徽章一次性 Chromium harness 通过:未知状态、试用期和 Todo 中文文案均按预期渲染。
@@ -55,7 +55,7 @@
- 日期:2026-08-07;浏览器为仓库 Playwright Chromium `chromium-1208`(Chrome for Testing 145.0.7632.6),headless,未使用真实账号或公网地址。
- Node `v22.22.2`、npm `10.9.7`、Playwright `1.58.2`;本地 Vite loopback:`http://127.0.0.1:5178`。
- 开始前工作区只有父任务 `08-05-product-interaction-consistency/task.json` 的既有子任务登记变更和本 task 目录;本任务未改 `client/src`、`server/` 或依赖。
- 截图目录:`output/playwright/08-07-interaction-pattern-browser-regression/`,共 66 张脱敏截图;另保留 `r6-stale-leave-readonly-320.png` 作为历史断言失败留痕。
- 截图目录:`output/playwright/08-07-interaction-pattern-browser-regression/`,共 66 张脱敏截图;另保留 `r6-stale-leave-readonly-320.png` 作为历史断言失败留痕(文件名保留历史,不代表最终诊断)。
### 浏览器结果
@@ -64,14 +64,14 @@
| R1/R6 状态徽章 | 一次性 Chromium harness,动态导入实际 `StatusBadge.tsx` | PASS,未知状态=`未知状态`、`probation`=`试用期`、Todo 四态中文,console/pageerror=0,退出码 0 | `output/playwright/08-07-interaction-pattern-browser-regression/01-status-badge-fallback-1440x960.png` |
| Portal R4/R9/R11 | `login-portal-ux.spec.ts` 聚焦 grep,单 worker | PASS,3 passed,退出码 0 | 既有 Playwright 报告;Portal 场景截图在任务目录 |
| PC 审批 R11 | `workflow-action-confirm-ui.spec.ts` 聚焦 grep,单 worker | PASS,1 passed,退出码 0 | 既有 Playwright 报告;审批详情截图在任务目录 |
| R6 七视口 R10/R11 | 临时副本仅重定向截图并移除过期请假表单断言;原始 spec 该断言因当前“企微主办、OA 只读”边界失败 | PASS(等价 in-scope runner),7 passed,退出码 0 | `w320`/`w360`/`w375`/`w390`/`w393`/`w414`/`w430` 系列截图;`r6-stale-leave-readonly-320.png` |
| R6 七视口 R10/R11 | 临时副本仅重定向截图并暂时跳过 `mobile-leave-form` 断言;后续确认原始 fixture 角色缺少可解析的 `leave.manage`,`effectiveView` 因此回落到 `records` | PASS(等价 in-scope runner),7 passed,退出码 0 | `w320`/`w360`/`w375`/`w390`/`w393`/`w414`/`w430` 系列截图;`r6-stale-leave-readonly-320.png` |
| R111 企业微信 UA R10/R11 | 原始 spec 的 `getByText("付款截图.png")` 在当前 DOM 命中两项,strict mode 失败;临时副本仅改为 `.first()` 后重跑 | PASS(等价 in-scope runner),1 passed,退出码 0;原始失败归类为测试夹具断言问题 | `mobile-wecom-reimbursement-detail.png`、`w390-ios-wecom-mobile-reimbursement-detail.png` |
| Wave3 R10/R11 | `pc-detail-mobile-visual-wave3.spec.ts` 临时重定向截图目录 | PASS,2 passed,退出码 0 | `01-pc-reimbursement-detail.png`、`02-pc-workflow-detail.png`、`03-pc-notifications.png`、`04-mobile-home.png` 等 |
| R14 差旅不自动选中 | 临时 runner 合成两条记录,直接访问 `/travel-requests` | PASS,1 passed,退出码 0;URL 仅含 `tab=requests`,无 `id`,未出现详情加载 | `18-travel-list-no-auto-select-1440x960.png` |
### 失败分类与未覆盖证据
- `FAIL(测试夹具/事实源漂移)`:未修改的 R6 旧用例仍断言 `mobile-leave-form`;当前产品页面明确为“企业微信主办、OA 只读”。未改业务页面,保留失败截图并用临时副本验证本任务其余七视口断言。
- `FAIL(测试夹具权限)`:未修改的 R6 旧用例 fixture 只有 `manager`、`employee`,不能经真实角色解析链得到 `leave.manage`;`MobileLeavePage` 将 `?view=form` 的 `effectiveView` 回落到 `records`,所以仍有效的 `mobile-leave-form` 断言失败。未删除该覆盖,保留失败截图并用临时副本验证其余七视口断言。
- `FAIL(测试断言 strict locator)`:未修改的 R111 用例对同时出现在材料卡和查看按钮中的同名文本使用 strict locator;临时副本只收窄定位器后通过。该问题应在后续测试维护任务修复,不属于本任务产品缺陷。
- `PAUSED`:iPhone/Android 企业微信真实 WebView、真机安全区/键盘行为未执行;本地 UA 模拟不等价于真机。
- `PAUSED`:公网部署、真实登录和生产数据回归未执行。
@@ -177,7 +177,7 @@ PC `1440x960`:
| 1. 状态徽章 | PASS | 本地 Chromium,1440x960 | 实际动态导入 `StatusBadge`;未知状态显示“未知状态”,`probation` 显示“试用期”,Todo 四态显示自然中文;console/pageerror=0 | `01-status-badge-fallback-1440x960.png` |
| 2. Portal | PASS | 合成员工/经理,PC 1440x960 | 3 个聚焦用例通过(3/3),顺序、权限过滤、中文错误与重试、焦点均有证据 | 任务目录内 Portal 截图 |
| 3. PC 审批 | PASS | 合成审批人,1280x800/1100x768 | 后果说明、取消不调用接口、转审 10 字门槛、焦点顺序通过(1/1) | 任务目录内审批截图 |
| 4. R6 七视口 | PASS(等价临时 runner) | iOS/Android 企业微信 UA + 普通移动 UA;320/360/375/390/393/414/430 | 7/7 通过;无横溢、无桌面表格、底部导航、sticky action、安全区和按钮尺寸通过 | `w320`–`w430` 系列;原始 spec 的过期请假断言失败另记为测试夹具问题 |
| 4. R6 七视口 | PASS(等价临时 runner) | iOS/Android 企业微信 UA + 普通移动 UA;320/360/375/390/393/414/430 | 7/7 通过;无横溢、无桌面表格、底部导航、sticky action、安全区和按钮尺寸通过 | `w320`–`w430` 系列;原始 fixture 角色缺少 `leave.manage`,导致 `effectiveView` 回落 `records`,历史断言失败另记为测试夹具问题 |
| 5. R111 材料区 | PASS(等价临时 runner) | iOS 企业微信 UA,移动 390x844 | 1/1 通过;材料区、sticky action、safe-area、按钮尺寸和无横溢通过 | 原始 spec 的同名文本 strict locator 命中两项;临时副本仅收窄 `.first()`,未改仓库测试 |
| 6. Wave3 详情 | PASS | PC/移动合成账号;1440、1280、1100 断点及移动代表视口 | PC/移动截图用例 2/2 通过,详情返回链路和双栏布局通过 | `01-pc-reimbursement-detail.png` 等 |
| 7. R14 差旅 | PASS | 合成管理员,PC 1440x960 | 两条合成记录加载后 URL 只有 `tab=requests`,没有 `id`;右侧保持未选择/新建态 | `18-travel-list-no-auto-select-1440x960.png` |
@@ -185,7 +185,7 @@ PC `1440x960`:
### 结果边界
- 未修改 `client/src`、`server/`、仓库 E2E spec 或依赖;R6/R111 的临时副本均位于 `/tmp`,只用于收窄历史测试断言和重定向截图。
- 原始 R6 请假断言与已确认“企业微信主办、OA 只读”边界不一致;原始 R111 断言违反 Playwright strict locator。两项均保留失败分类,不伪装成原始 spec 无条件通过。
- 原始 R6 失败不是产品能力下线或宿主边界限制:fixture 角色不能解析出 `leave.manage`,导致 `effectiveView` 回落到 `records`;`mobile-leave-form` 仍是有效覆盖。原始 R111 断言违反 Playwright strict locator。两项均保留当时失败分类,不伪装成原始 spec 无条件通过。
- 代表性 iPhone/Android 企业微信 WebView、真机键盘与 safe-area,以及公网/部署验证均为 `PAUSED`。
## 最终输出格式