From e6b23772c9c88ea031cb16ce89ca15428c9ea5a8 Mon Sep 17 00:00:00 2001 From: WeiCHEN Date: Tue, 22 Sep 2026 23:40:22 +0800 Subject: [PATCH] =?UTF-8?q?fix(e2e):=20approve=20intake=20via=20=E4=B8=89?= =?UTF-8?q?=E6=9D=83=20before=20UI-11=20prelim=5Fdesign?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V0.1 blocking intake caused tasks/runs 403 for V0.5 prelim_design; drive submit/review/approve on page and accept bd_prelim_design keys. --- web/e2e/document-revision-runtime.spec.ts | 94 +++++++++++++++++++++-- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/web/e2e/document-revision-runtime.spec.ts b/web/e2e/document-revision-runtime.spec.ts index 3379ffe5..62f0decf 100644 --- a/web/e2e/document-revision-runtime.spec.ts +++ b/web/e2e/document-revision-runtime.spec.ts @@ -454,21 +454,92 @@ async function openWorkflowDocByControlledTaskKey( const trigger = page.getByTestId('doc-selector-trigger'); await expect(trigger).toBeVisible({ timeout: 15_000 }); await trigger.click(); + // Prefer exact suffix; also accept bd_${taskKey} canonical keys from main.v2. const item = page - .locator(`.dropdown-menu button[data-doc-id^="doc_proj"][data-doc-id$="_${taskKey}"]`) + .locator( + [ + `.dropdown-menu button[data-doc-id^="doc_proj"][data-doc-id$="_bd_${taskKey}"]`, + `.dropdown-menu button[data-doc-id^="doc_proj"][data-doc-id$="_${taskKey}"]`, + ].join(', '), + ) .first(); if ((await item.count()) === 0) { - fail(`workflow menu missing registered doc_proj*_${taskKey}`); + fail(`workflow menu missing registered doc_proj*_${taskKey} (or bd_${taskKey})`); } const docId = (await item.getAttribute('data-doc-id')) || ''; const title = (await item.getAttribute('data-doc-title')) || ''; - if (!new RegExp(`^doc_proj\\d+_${taskKey}$`).test(docId)) { - fail(`expected doc_proj{n}_${taskKey}, got ${JSON.stringify(docId)}`); + if (!new RegExp(`^doc_proj\\d+_(?:bd_)?${taskKey}$`).test(docId)) { + fail(`expected doc_proj{n}_(?:bd_)?${taskKey}, got ${JSON.stringify(docId)}`); } await item.click(); return { title: title || docId, docId }; } +async function ensureControlledIntakeOpen(target: Page, docId: string) { + if (docId) { + await openWorkflowDocById(target, docId); + } else { + await openWorkflowDocByLabel(target, /用户初始需求|需求摸底|intake/i); + } + await expect(target.locator('[data-testid="doc-rev-version"]')).toBeVisible({ timeout: 30_000 }); +} + +/** + * V0.1 intake is blocking_when_enabled; prelim_design lives in V0.5 and is + * 403'd until intake completes 三权. Drive submit→review→approve via page only. + */ +async function driveIntakeGovernanceToApproved( + authorPage: Page, + docId: string, + creds: { userB: string; passB: string; userD: string; passD: string; projectName: string }, +) { + await ensureControlledIntakeOpen(authorPage, docId); + const claim = authorPage.getByRole('button', { name: /认领此文档/ }); + if (await claim.isVisible().catch(() => false)) { + await claim.click(); + await expect(claim).toHaveCount(0, { timeout: 15_000 }); + } + const submit = authorPage.getByRole('button', { name: /提交送审/ }); + await expect(submit).toBeVisible({ timeout: 30_000 }); + await submit.click(); + await expect(authorPage.getByRole('button', { name: /提交送审/ })).toHaveCount(0, { + timeout: 30_000, + }); + + const browser = authorPage.context().browser(); + if (!browser) fail('driveIntakeGovernanceToApproved requires browser'); + + const ctxB = await browser.newContext(); + const pageB = await ctxB.newPage(); + await login(pageB, creds.userB, creds.passB); + await pageB.getByText(creds.projectName, { exact: false }).first().click(); + await ensureControlledIntakeOpen(pageB, docId); + const review = pageB.getByRole('button', { name: /审核通过/ }); + await expect(review).toBeVisible({ timeout: 30_000 }); + await review.click(); + await expect(pageB.getByRole('button', { name: /审核通过/ })).toHaveCount(0, { timeout: 30_000 }); + await ctxB.close(); + + const ctxD = await browser.newContext(); + const pageD = await ctxD.newPage(); + await login(pageD, creds.userD, creds.passD); + await pageD.getByText(creds.projectName, { exact: false }).first().click(); + await ensureControlledIntakeOpen(pageD, docId); + const approve = pageD.getByRole('button', { name: /终审批准/ }); + await expect(approve).toBeVisible({ timeout: 30_000 }); + await approve.click(); + await expect(pageD.getByRole('button', { name: /终审批准/ })).toHaveCount(0, { timeout: 60_000 }); + await ctxD.close(); + + // Author session: reload so stage banner / task scope picks up approved intake. + await authorPage.reload(); + await expect(authorPage.getByText(creds.projectName, { exact: false }).first()).toBeVisible({ + timeout: 30_000, + }); + await ensureControlledIntakeOpen(authorPage, docId); + await expect(authorPage.getByText('必需文档尚未批准')).toHaveCount(0, { timeout: 90_000 }); +} + async function openWorkflowDocById(page: Page, docId: string): Promise { if (!docId) fail('openWorkflowDocById requires docId'); await selectDeliverableCategory(page, requiredCategoryForDocOpen({ kind: 'exact-id', docId })); @@ -1267,6 +1338,15 @@ test.describe.serial('document-revision-runtime UI acceptance', () => { test('UI-11 chat second controlled doc + page upload attachment', async () => { const t0 = Date.now(); logs = []; + // prelim_design is V0.5; V0.1 intake must finish 三权 or tasks/runs returns 403. + if (!controlledDocId) fail('UI-11 requires controlledDocId from UI-03'); + await driveIntakeGovernanceToApproved(page, controlledDocId, { + userB: USER_B, + passB: FINAL_PASS_B, + userD: USER_D, + passD: FINAL_PASS_D, + projectName: PROJECT_NAME, + }); const prompt = '请另行生成一份《系统设计输入》受控文档(第二份),不要覆盖上一份需求文档,并给出可打开的交付物。'; const taskRunWait = waitForTasksRunsPost(page); @@ -1276,8 +1356,10 @@ test.describe.serial('document-revision-runtime UI acceptance', () => { fail(`UI-11 tasks/runs must be HTTP 202, got ${taskRes.status()}`); } const taskBody = taskRes.request().postDataJSON() as { task_key?: string }; - if (taskBody.task_key !== 'prelim_design') { - fail(`UI-11 task_key must be prelim_design, got ${JSON.stringify(taskBody.task_key)}`); + if (taskBody.task_key !== 'prelim_design' && taskBody.task_key !== 'bd_prelim_design') { + fail( + `UI-11 task_key must be prelim_design family, got ${JSON.stringify(taskBody.task_key)}`, + ); } await waitForSquadCompletion(page, '系统设计输入'); // Prompt alias is 系统设计输入; card title is 系统初步设计方案; open by registered doc_proj id.