Cover review decisions, rule binding and the full package in the renderer e2e spec

Why: The renderer browser acceptance still recorded GAP markers for approve, final, rule binding and the full package, which the workspace now provides; the spec must exercise those entries through the UI first, falling back to the API only when an entry is absent.

Evidence: tsc --noEmit on e2e/document-renderer.playwright.ts exits 0; playwright test --list loads 13 tests (adds §11-2b and §11-3b); the spec still skips without PLAYWRIGHT_BASE_URL and RBA_OUT, so no browser run was possible here; leak scan in §11-8 is untouched.

Impact: approve and final are attempted from the workspace and asserted by success text or a readable failure; the package download asserts a ZIP header; binding asserts the refresh and the controlled-document refusal copy. Not run against a real product in this session.

Refs: unified renderer acceptance §11 items 2, 3, 5 and 6; RDM 7169944 (review decision contract), 4b7ac2a (profile bind), 75ba50c (package).
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-10-17 02:36:02 +00:00
co-authored by Cursor
parent 7f3538da22
commit 22f59cd1b2
+78 -17
View File
@@ -210,6 +210,42 @@ test('§11-1 文档树 → 结构画布与阅读视图(共用选中与过滤
await s.close()
})
test('§11-2b 规则绑定:项目设置权限可绑定与解绑(非受控文档)', async ({ browser }) => {
items(2)
const s = await session(browser, U.admin, 'item2b-admin')
const { page } = s
await openAudit(page, A.name)
await openDoc(page, UNCTRL.title)
const binding = panel(page, '规则绑定')
await expect(binding, '具备项目设置权限的用户可见规则绑定区').toBeVisible({ timeout: 20_000 })
await page.locator('.drw-bind-rule', { hasText: `${GENERAL.id}@${GENERAL.version}` }).locator('input').check()
await page.locator('.drw-bind-row', { hasText: '本文档' }).getByRole('button', { name: '绑定所选规则', exact: true }).click()
await expect(panel(page, '规则绑定').getByText('绑定规则已完成')).toBeVisible({ timeout: 30_000 })
await expectReadableFailures(page)
await expect(panel(page, '渲染规则').locator('.drw-profile-source', { hasText: '文档规则' }), '绑定后规则列表刷新出文档规则').toBeVisible({ timeout: 15_000 })
await shot(page, 'item2b-bound', binding)
await page.locator('.drw-bind-row', { hasText: '本文档' }).getByRole('button', { name: '解绑', exact: true }).click()
await expect(panel(page, '规则绑定').getByText('解绑规则已完成')).toBeVisible({ timeout: 30_000 })
await expectReadableFailures(page)
await s.close()
})
test('§11-3b 受控文档拒绝绑定通用规则,失败以可读文案显示', async ({ browser }) => {
items(3)
const s = await session(browser, U.admin, 'item3b-admin')
const { page } = s
await openAudit(page, A.name)
await openDoc(page, A.title)
const binding = panel(page, '规则绑定')
await expect(binding, '具备项目设置权限的用户可见规则绑定区').toBeVisible({ timeout: 20_000 })
await page.locator('.drw-bind-rule', { hasText: `${GENERAL.id}@${GENERAL.version}` }).locator('input').check()
await page.locator('.drw-bind-row', { hasText: '本文档' }).getByRole('button', { name: '绑定所选规则', exact: true }).click()
await expect(binding.locator('.drw-failure-msg')).toContainText('受控文档', { timeout: 30_000 })
await expect(binding.locator('.drw-failure-msg')).not.toContainText('controlled_requires_exact_profile')
await shot(page, 'item3b-controlled-refusal', binding)
await s.close()
})
test('§11-3 受控文档:规则来源、版本与不回退说明', async ({ browser }) => {
items(3)
const s = await session(browser, U.author, 'item3-author')
@@ -393,12 +429,23 @@ test('§11-5c 审核通过与批准(签署)', async ({ browser }) => {
expect(submissionId, 'BLOCKER[RDM] 无渲染审阅提交(见 §11-5a),审核与批准签署无从进行').not.toBe('')
}
await expect(reviewPanel.locator('.drw-mono')).toHaveAttribute('title', submissionId, { timeout: 20_000 })
const reviewerButtons = await reviewPanel.getByRole('button', { name: /通过|同意|批准|签署|驳回/ }).count()
expect.soft(reviewerButtons,
'GAP[Frontend] 审阅面板没有审核通过/驳回入口;rdm.review.doc.approve 是需会话来源锚点的正式写命令,只能经对话代理触发').toBeGreaterThan(0)
apiAssisted('rdm.review.doc.approve 以审核人会话 + 本人会话来源锚点调用')
const approve = await command(r.page, 'rdm.review.doc.approve', { projectId: A.pid, docId: A.doc, note: `§11 审核通过 ${RUN}` }, env('RBA_SRC_REVIEWER_A'))
expect(approve.body?.receipt?.state, `approve 回执:${approve.text.slice(0, 400)}`).toBe('committed')
// 工作区「整文审签」入口只在服务端允许(审核人且未审)时出现;成功与可读失败都必须可见。
const decisionPanel = panel(r.page, '整文审签')
const uiApprove = decisionPanel.getByRole('button', { name: '审核通过', exact: true })
if ((await uiApprove.count()) > 0) {
await uiApprove.click()
const done = decisionPanel.getByText('整文审核通过已完成')
const failed = decisionPanel.locator('.drw-failure-msg')
await expect(done.or(failed).first()).toBeVisible({ timeout: 60_000 })
expect.soft(await done.count(), `审核通过应成功:${(await failed.allTextContents()).join(' | ')}`).toBe(1)
await expectReadableFailures(r.page)
await shot(r.page, 'item5c-reviewer-approved', decisionPanel)
} else {
expect.soft(false, 'GAP[Frontend] 审核人在待审状态下工作区没有「审核通过」入口').toBe(true)
apiAssisted('rdm.review.doc.approve 以审核人会话 + 本人会话来源锚点调用(工作区入口不可用时的取证路径)')
const approve = await command(r.page, 'rdm.review.doc.approve', { projectId: A.pid, docId: A.doc, note: `§11 审核通过 ${RUN}` }, env('RBA_SRC_REVIEWER_A'))
expect(approve.body?.receipt?.state, `approve 回执:${approve.text.slice(0, 400)}`).toBe('committed')
}
await r.close()
const a = await session(browser, U.approver, 'item5c-approver')
@@ -406,12 +453,22 @@ test('§11-5c 审核通过与批准(签署)', async ({ browser }) => {
await openDoc(a.page, A.title)
const approverPanel = panel(a.page, '审阅')
await expect(approverPanel.locator('.drw-mono')).toHaveAttribute('title', submissionId, { timeout: 20_000 })
const approverButtons = await approverPanel.getByRole('button', { name: /批准|签署|定稿|通过/ }).filter({ hasNotText: '生成批准稿' }).count()
expect.soft(approverButtons,
'GAP[Frontend] 审阅面板没有批准/定稿(签署)入口;rdm.review.doc.final 需会话来源锚点,浏览器工作区无法完成批准').toBeGreaterThan(0)
apiAssisted('rdm.review.doc.final 以批准人会话 + 本人会话来源锚点调用')
const final = await command(a.page, 'rdm.review.doc.final', { projectId: A.pid, docId: A.doc, round, reason: `§11 批准 ${RUN}` }, env('RBA_SRC_APPROVER_A'))
expect(final.body?.receipt?.state, `final 回执:${final.text.slice(0, 400)}`).toBe('committed')
// 终批入口只在批准人且全员已审核通过时出现;载荷绑定界面所示轮次。
const finalPanel = panel(a.page, '整文审签')
const uiFinal = finalPanel.getByRole('button', { name: '终批', exact: true })
if ((await uiFinal.count()) > 0) {
await uiFinal.click()
const done = finalPanel.getByText('整文终批已完成')
const failed = finalPanel.locator('.drw-failure-msg')
await expect(done.or(failed).first()).toBeVisible({ timeout: 60_000 })
expect.soft(await done.count(), `终批应成功:${(await failed.allTextContents()).join(' | ')}`).toBe(1)
await expectReadableFailures(a.page)
} else {
expect.soft(false, 'GAP[Frontend] 批准人在全员审核通过后工作区没有「终批」入口').toBe(true)
apiAssisted('rdm.review.doc.final 以批准人会话 + 本人会话来源锚点调用(工作区入口不可用时的取证路径)')
const final = await command(a.page, 'rdm.review.doc.final', { projectId: A.pid, docId: A.doc, round, reason: `§11 批准 ${RUN}` }, env('RBA_SRC_APPROVER_A'))
expect(final.body?.receipt?.state, `final 回执:${final.text.slice(0, 400)}`).toBe('committed')
}
await a.page.reload()
await openAudit(a.page, A.name)
await openDoc(a.page, A.title)
@@ -445,8 +502,9 @@ test('§11-6 下载 Markdown、批准稿、审阅留痕稿与完整包', async (
await shot(page, 'item6-preview-downloads', panel(page, '交付记录'))
expect.soft(await deliveryOf(page, '批准').count(), 'BLOCKER[RDM] 无渲染提交 → 无批准交付,批准稿不可下载').toBeGreaterThan(0)
expect.soft(await page.getByRole('button', { name: '审阅留痕 DOCX', exact: true }).count(), 'BLOCKER[RDM] 无渲染提交 → 无审阅交付,审阅留痕稿不可下载').toBeGreaterThan(0)
expect.soft(await page.locator('.drw-download', { hasText: /完整包|全部|打包|ZIP|zip/ }).count(),
'GAP[RDM] rdm.render.download 只支持 docx/review-docx/markdown/manifest/diagnostics,无完整交付包(zip)种类;GAP[Frontend] 交付记录无「完整包」下载入口').toBeGreaterThan(0)
const pkg = await download(page, preview, '完整交付包(ZIP)')
expect(pkg.name).toMatch(/\.zip$/)
expect(fs.readFileSync(pkg.file).subarray(0, 2).toString()).toBe('PK')
await s.close()
return
}
@@ -493,9 +551,12 @@ test('§11-6 下载 Markdown、批准稿、审阅留痕稿与完整包', async (
if (manifest.name.length > 36) expect(shown).toContain('…')
await shot(page, 'item6-downloaded-longname', panel(page, '交付记录'))
writeState({ downloads: [md, docx, trace, manifest].map(f => f.name) })
const packageButtons = await page.locator('.drw-download', { hasText: /完整包|全部|打包|ZIP|zip/ }).count()
expect.soft(packageButtons,
'GAP[RDM] rdm.render.download 只支持 docx/review-docx/markdown/manifest/diagnostics,无完整交付包(zip)种类;GAP[Frontend] 交付记录无「完整包」下载入口').toBeGreaterThan(0)
// 完整包:rdm.render.download kind=package,服务端确定性 ZIP;与单件下载同一鉴权路径。
const pkg = await download(page, approved, '完整交付包(ZIP)')
expect(pkg.name).toMatch(/\.zip$/)
expect(fs.readFileSync(pkg.file).subarray(0, 2).toString()).toBe('PK')
note(`完整交付包:${pkg.name}(${pkg.size} B)`)
writeState({ downloads: [md, docx, trace, manifest, pkg].map(f => f.name) })
await s.close()
})