test(e2e): open pending change report before confirm or reject
UI-04 must close via the dialog 关闭 button; UI-05a/05b must click 待确认报告 and assert panel actions so leftover first-round reports cannot hide the decision path. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: "UI-05a 未打开待确认报告导致确认按钮缺失"
|
||||
status: in_progress
|
||||
created: 2026-09-13
|
||||
updated: 2026-09-13
|
||||
severity: high
|
||||
---
|
||||
|
||||
# UI-05a 未打开待确认报告导致确认按钮缺失
|
||||
|
||||
## 现象
|
||||
|
||||
第四轮真实 UI(schema 30)**UI-05a 首个失败**:
|
||||
|
||||
- UI-00~05 PASS;**UI-05a** 抛 `confirm button missing for pending high-risk candidate`
|
||||
- 工具条已显示「待确认 high」与「待确认报告」
|
||||
- 助手气泡写明候选 #91、变更报告 91、风险 high、状态 `pending_author_confirmation`
|
||||
- 页面仍挂着 **首轮** `dialog「变更报告」`:风险 **low** / `fallback`,无 `change-report-pending-actions`,无「确认提交」
|
||||
- 脚本用全局 `getByRole('button', { name: /确认提交/ }).count()` 判定,未先点「待确认报告」
|
||||
|
||||
项目 `UI验收-超声微创系统-mu00igjt`,文档 `doc_proj6119_intake`,首轮报告 `change-reports/90`,pending 候选/报告 `#91`。
|
||||
|
||||
## 根因分析
|
||||
|
||||
| 层 | 问题 |
|
||||
|---|---|
|
||||
| UI-04 关闭 | `ChangeReportPanel` 只有弹窗内「关闭」;**没有 Escape 处理**。`page.keyboard.press('Escape')` 被 `.catch` 吞掉,首轮 low 报告 `reportId` 仍打开 |
|
||||
| 产品条件 | `pendingDecision`(确认/拒绝)仅当 `reportId === pending_change_report_id` 才注入。首轮报告 90 ≠ pending 91,弹窗里不会出现「确认提交」 |
|
||||
| UI-05a | 未模拟真实用户点击「待确认报告」并等待 `change-report-panel`;只用全局 count,错过必须打开 pending 报告的路径 |
|
||||
|
||||
产品确认/拒绝逻辑本身未坏;失败来自验收脚本未走真实面板路径。本轮**禁止改产品业务逻辑**。
|
||||
|
||||
## 严重度
|
||||
|
||||
`high`
|
||||
|
||||
## 关联整改
|
||||
|
||||
- Short-term: [../ops/short-term/2026-09-13-document-revision-e2e-pending-flow.md](../ops/short-term/2026-09-13-document-revision-e2e-pending-flow.md)
|
||||
- 分支: `fix/document-revision-e2e-pending-flow`
|
||||
|
||||
## 证据
|
||||
|
||||
- Evidence root(勿删、勿提交本轮):`.doc/test-reports/assets/2026-09-13-document-revision-runtime-ui/schema30-ui05a-pending-report-selection-failure`
|
||||
- Playwright:`playwright-output/document-revision-runtime--91599-andidate-→-version-advances-chromium/error-context.md`
|
||||
- 结果:`ui-acceptance-results.json`(UI-04 PASS 131ms,打开 `/change-reports/90`;UI-05 PASS;UI-05a 未写入 PASS)
|
||||
|
||||
## 开放问题
|
||||
|
||||
- 占槽后真实 UI-05a/05b 重跑;本轮不操作容器、不重跑 E2E
|
||||
- 脚本收紧后状态仍为 `in_progress`,真实重跑前不得 `done`
|
||||
+4
-2
@@ -1,11 +1,11 @@
|
||||
# 当前焦点
|
||||
|
||||
**更新**: 2026-09-13
|
||||
**production_ready**: no(DOCREV:schema **30**;行动表状态「待续」假阳性在 `fix/document-revision-quality-context`;不操作容器)
|
||||
**production_ready**: no(DOCREV:schema **30**;第四轮 UI-05a 待确认报告未打开;不操作容器)
|
||||
|
||||
## 焦点
|
||||
|
||||
1. **DOCREV-RT**: [行动计划表状态「待续」窄豁免](short-term/2026-09-13-document-revision-quality-context.md)(`fix/document-revision-quality-context`:不删禁词、不放宽正文;不 push / 不操作容器)
|
||||
1. **DOCREV-RT**: [E2E 待确认报告真实打开再确认/拒绝](short-term/2026-09-13-document-revision-e2e-pending-flow.md)(`fix/document-revision-e2e-pending-flow`:不改产品逻辑;不重跑 E2E / 不操作容器)
|
||||
2. **CHAT**: [刷新恢复整改已实施](short-term/2026-09-13-chat-history-refresh-remediation.md)(待合 dev + PI E2E 补录)
|
||||
3. **CI**: `TEST_DATABASE_URL` + 真实 PI E2E 实录;INTENT 发布 blocker 见 conversation-intent short-term
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
| Cursor-Auto | document-revision-sse-idempotency | fix/document-revision-sse-idempotency | - | docgov | done |
|
||||
| Cursor-Auto | document-revision-active-doc-routing | fix/document-revision-active-doc-routing | - | docgov | done |
|
||||
| Cursor-Auto | document-revision-quality-context | fix/document-revision-quality-context | - | docgov | in_progress |
|
||||
| Cursor-Auto | document-revision-e2e-pending-flow | fix/document-revision-e2e-pending-flow | - | document-revision-runtime-ui | in_progress |
|
||||
|
||||
> 新任务开始前登记。`branch` 必须为 `feature/<slug>` 或 `fix/<slug>`,**禁止** `dev` / `test` / `main`。同一 `mutex` 同时仅一个 `in_progress`。
|
||||
|
||||
@@ -56,3 +57,4 @@
|
||||
| 质量门禁自污染审计 | [../audit/2026-09-13-document-revision-quality-gate-self-pollution.md](../audit/2026-09-13-document-revision-quality-gate-self-pollution.md) |
|
||||
| 当前文档路由审计 | [../audit/2026-09-13-document-revision-active-doc-routing.md](../audit/2026-09-13-document-revision-active-doc-routing.md) |
|
||||
| 行动表状态假阳性审计 | [../audit/2026-09-13-document-revision-quality-status-false-positive.md](../audit/2026-09-13-document-revision-quality-status-false-positive.md) |
|
||||
| 待确认报告选择失败审计 | [../audit/2026-09-13-document-revision-pending-report-selection.md](../audit/2026-09-13-document-revision-pending-report-selection.md) |
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: "E2E:待确认报告必须真实打开再确认/拒绝"
|
||||
status: in_progress
|
||||
created: 2026-09-13
|
||||
updated: 2026-09-13
|
||||
owner: Cursor-Auto
|
||||
branch: fix/document-revision-e2e-pending-flow
|
||||
worktree: "-"
|
||||
mutex: document-revision-runtime-ui
|
||||
---
|
||||
|
||||
# E2E:待确认报告必须真实打开再确认/拒绝
|
||||
|
||||
## 目标
|
||||
|
||||
第四轮 UI-05a 首错修复:验收脚本按真实用户路径关闭首轮报告、打开「待确认报告」,在面板内断言 high(critical 允许)与 pending 动作后再确认/拒绝。不改产品业务逻辑。
|
||||
|
||||
## 范围
|
||||
|
||||
- `web/e2e/document-revision-runtime.spec.ts` UI-04 / UI-05a / UI-05b
|
||||
- 文档:audit / CURRENT 认领(mutex `document-revision-runtime-ui`)
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不改 `ChangeReportPanel` / Toolbar / decision API
|
||||
- 不降低已有业务断言或超时;不伪造、不直接打 API
|
||||
- 不触碰/提交 `.doc/test-reports/assets`;不操作容器;本轮不重跑 E2E
|
||||
- 不改 FEATURE-MATRIX
|
||||
|
||||
## 步骤
|
||||
|
||||
1. [x] 认领 CURRENT;写 audit/short-term
|
||||
2. [x] UI-04 用弹窗内「关闭」并断言 dialog 消失
|
||||
3. [x] UI-05a 点击「待确认报告」,断言 risk/pending-actions/确认提交后确认
|
||||
4. [x] UI-05b 新 pending 后同样打开面板,断言 pending-actions/拒绝后再拒绝
|
||||
5. [ ] 占槽后真实 UI 重跑(本轮不做)
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [x] UI-04 不再使用无效 Escape
|
||||
- [x] UI-05a/05b 不靠全局 count 代替打开待确认报告
|
||||
- [x] web tests + `tsc`/build + `git diff --check`
|
||||
- [ ] 真实 UI-05a/05b PASS 后才可将本任务标 `done`
|
||||
|
||||
## 相关链接
|
||||
|
||||
- Audit: [../../audit/2026-09-13-document-revision-pending-report-selection.md](../../audit/2026-09-13-document-revision-pending-report-selection.md)
|
||||
- Evidence: [../../test-reports/assets/2026-09-13-document-revision-runtime-ui/schema30-ui05a-pending-report-selection-failure](../../test-reports/assets/2026-09-13-document-revision-runtime-ui/schema30-ui05a-pending-report-selection-failure)
|
||||
- Checklist: [../checklists/2026-09-13-document-revision-runtime-ui-acceptance.md](../checklists/2026-09-13-document-revision-runtime-ui-acceptance.md)
|
||||
@@ -649,19 +649,23 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
const reportBtn = page.getByRole('button', { name: /变更报告|待确认报告/ });
|
||||
await expect(reportBtn).toBeEnabled({ timeout: 15_000 });
|
||||
await reportBtn.click();
|
||||
const dialog = page.getByRole('dialog', { name: '变更报告' });
|
||||
await expect(dialog).toBeVisible({ timeout: 20_000 });
|
||||
await expect(page.getByTestId('change-report-panel')).toBeVisible({ timeout: 20_000 });
|
||||
const panel = page.locator('text=风险').or(page.locator('text=开放问题')).or(page.locator('text=项目影响'));
|
||||
await expect(panel.first()).toBeVisible({ timeout: 20_000 });
|
||||
// Report list / body must be non-empty
|
||||
const bodyText = await page.locator('body').innerText();
|
||||
if (!/风险|开放问题|摘要|影响/.test(bodyText)) fail('change report panel missing required fields');
|
||||
await shot(page, '04-first-report.png');
|
||||
// Close if possible
|
||||
await page.keyboard.press('Escape').catch(() => undefined);
|
||||
await dialog.getByRole('button', { name: '关闭' }).click();
|
||||
await expect(dialog).toHaveCount(0, { timeout: 10_000 });
|
||||
await expect(page.getByTestId('change-report-panel')).toHaveCount(0);
|
||||
recordPass({
|
||||
id: 'UI-04',
|
||||
name: 'change report fields',
|
||||
input: 'click 变更报告',
|
||||
actual: 'risk/impact/open questions visible',
|
||||
input: 'click 变更报告; close via 关闭',
|
||||
actual: 'risk/impact/open questions visible; dialog closed',
|
||||
networkLogs: logs,
|
||||
durationMs: Date.now() - t0,
|
||||
verdict: 'PASS',
|
||||
@@ -696,8 +700,21 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
test('UI-05a confirm high-risk candidate → version advances', async () => {
|
||||
const t0 = Date.now();
|
||||
logs = [];
|
||||
const confirm = page.getByRole('button', { name: /确认提交/ });
|
||||
if ((await confirm.count()) === 0) fail('confirm button missing for pending high-risk candidate');
|
||||
const pendingBtn = page.getByRole('button', { name: '待确认报告' });
|
||||
await expect(pendingBtn).toBeVisible({ timeout: 15_000 });
|
||||
await pendingBtn.click();
|
||||
const reportPanel = page.getByTestId('change-report-panel');
|
||||
await expect(reportPanel).toBeVisible({ timeout: 20_000 });
|
||||
const riskEl = reportPanel.getByTestId('change-report-risk');
|
||||
await expect(riskEl).toBeVisible({ timeout: 20_000 });
|
||||
const risk = (await riskEl.innerText()).trim().toLowerCase();
|
||||
if (risk !== 'high' && risk !== 'critical') {
|
||||
fail(`UI-05a pending report risk must be high (critical allowed); got ${JSON.stringify(risk)}`);
|
||||
}
|
||||
const actions = reportPanel.getByTestId('change-report-pending-actions');
|
||||
await expect(actions).toBeVisible({ timeout: 15_000 });
|
||||
const confirm = actions.getByRole('button', { name: '确认提交' });
|
||||
await expect(confirm).toBeVisible({ timeout: 15_000 });
|
||||
const decisionWait = page.waitForResponse(
|
||||
(r) => r.url().includes('/decision') && r.request().method() === 'POST',
|
||||
{ timeout: 60_000 },
|
||||
@@ -716,8 +733,8 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
recordPass({
|
||||
id: 'UI-05a',
|
||||
name: 'confirm pending',
|
||||
input: 'click 确认提交',
|
||||
actual: `decision ok; version ${v}`,
|
||||
input: 'click 待确认报告; assert pending actions; click 确认提交',
|
||||
actual: `risk=${risk}; decision ok; version ${v}`,
|
||||
networkLogs: logs,
|
||||
durationMs: Date.now() - t0,
|
||||
verdict: 'PASS',
|
||||
@@ -735,8 +752,15 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
await expect(page.getByTestId('doc-rev-pending-badge')).toBeVisible({ timeout: 60_000 });
|
||||
const { v: vPend } = await readVersionBadge(page);
|
||||
if (vPend !== vBefore) fail(`second pending must not advance version (${vBefore}→${vPend})`);
|
||||
const reject = page.getByRole('button', { name: /拒绝/ });
|
||||
if ((await reject.count()) === 0) fail('reject button missing');
|
||||
const pendingBtn = page.getByRole('button', { name: '待确认报告' });
|
||||
await expect(pendingBtn).toBeVisible({ timeout: 15_000 });
|
||||
await pendingBtn.click();
|
||||
const reportPanel = page.getByTestId('change-report-panel');
|
||||
await expect(reportPanel).toBeVisible({ timeout: 20_000 });
|
||||
const actions = reportPanel.getByTestId('change-report-pending-actions');
|
||||
await expect(actions).toBeVisible({ timeout: 15_000 });
|
||||
const reject = actions.getByRole('button', { name: '拒绝' });
|
||||
await expect(reject).toBeVisible({ timeout: 15_000 });
|
||||
const decisionWait = page.waitForResponse(
|
||||
(r) => r.url().includes('/decision') && r.request().method() === 'POST',
|
||||
{ timeout: 60_000 },
|
||||
@@ -753,7 +777,7 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
recordPass({
|
||||
id: 'UI-05b',
|
||||
name: 'reject pending',
|
||||
input: prompt,
|
||||
input: `${prompt}; click 待确认报告; click 拒绝`,
|
||||
actual: `rejected; version stayed ${vAfter}`,
|
||||
networkLogs: logs,
|
||||
durationMs: Date.now() - t0,
|
||||
|
||||
Reference in New Issue
Block a user