test(e2e): tighten UI-01 zero revision-state 404 assertions
Match only hyphenated doc-p template paths, reset logs after login, and expect registered uploads to use server doc-free ledgers. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -236,15 +236,21 @@ async function sendChat(page: Page, text: string) {
|
||||
await page.getByRole('button', { name: /发送/ }).click();
|
||||
}
|
||||
|
||||
async function assertNoRevisionState404(logs: NetLog[], projectDocPrefix: string) {
|
||||
/** Assert zero revision-state 404 and zero requests for template doc-p* slots. */
|
||||
function assertRevisionStateCleanForNewProject(logs: NetLog[]) {
|
||||
const hits = logs.filter((l) => l.path.includes('/revision-state'));
|
||||
const bad404 = hits.filter((l) => l.status === 404);
|
||||
if (bad404.length) {
|
||||
fail(`revision-state 404 is FAIL: ${JSON.stringify(bad404)}`);
|
||||
}
|
||||
const templateHits = hits.filter((l) => l.path.includes(projectDocPrefix) || /doc-p\d+-/.test(l.path));
|
||||
if (templateHits.length) {
|
||||
fail(`template/local doc must not request revision-state: ${JSON.stringify(templateHits)}`);
|
||||
// Match only hyphen template IDs (doc-p123-intake), NOT doc_proj* (underscore).
|
||||
const docPTemplateHits = hits.filter((l) => /\/documents\/doc-p\d+-[^/]+\/revision-state/.test(l.path));
|
||||
if (docPTemplateHits.length !== 0) {
|
||||
fail(`new project template must issue 0 revision-state for doc-p*: got ${JSON.stringify(docPTemplateHits)}`);
|
||||
}
|
||||
const legacyHits = hits.filter((l) => /\/documents\/doc-\d{3}\/revision-state/.test(l.path));
|
||||
if (legacyHits.length !== 0) {
|
||||
fail(`legacy local mock must not request revision-state: ${JSON.stringify(legacyHits)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,16 +398,15 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
|
||||
test('UI-01 login A → page create project → template hint, no revision-state for doc-p', async () => {
|
||||
const t0 = Date.now();
|
||||
logs = [];
|
||||
await login(page, USER_A, FINAL_PASS_A);
|
||||
// Isolate network evidence to project-create window (exclude login-era noise).
|
||||
logs = [];
|
||||
await page.getByRole('button', { name: '新建立项会话' }).click();
|
||||
await expect(page.getByText('发起医疗器械新项目立项')).toBeVisible();
|
||||
await page.fill('input[placeholder*="无线便携式"]', PROJECT_NAME);
|
||||
await page.fill('textarea, input[placeholder*="临床"]', '用于骨科微创手术的双频超声吸引与止血,需符合 GB 9706.1 与相关法规要求。').catch(async () => {
|
||||
const desc = page.locator('textarea').first();
|
||||
if ((await desc.count()) === 0) fail('project form missing description field');
|
||||
await desc.fill('用于骨科微创手术的双频超声吸引与止血,需符合 GB 9706.1 与相关法规要求。');
|
||||
});
|
||||
const desc = page.locator('textarea').first();
|
||||
if ((await desc.count()) === 0) fail('project form missing required description textarea');
|
||||
await desc.fill('用于骨科微创手术的双频超声吸引与止血,需符合 GB 9706.1 与相关法规要求。');
|
||||
// Select B as member
|
||||
const memberRow = page.locator('div').filter({ hasText: `协作者B-${suffix}` }).first();
|
||||
if ((await memberRow.count()) === 0) fail('member B not listed in create-project form');
|
||||
@@ -415,12 +420,7 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
await expect(page.locator('text=版本账本未就绪')).toHaveCount(0);
|
||||
await shot(page, '01-project.png');
|
||||
|
||||
const revHits = logs.filter((l) => l.path.includes('/revision-state'));
|
||||
const docPHits = revHits.filter((l) => /doc-p\d+-/.test(l.path));
|
||||
if (docPHits.length !== 0) {
|
||||
fail(`new project template must issue 0 revision-state for doc-p*: got ${JSON.stringify(docPHits)}`);
|
||||
}
|
||||
assertNoRevisionState404(logs, 'doc-p');
|
||||
assertRevisionStateCleanForNewProject(logs);
|
||||
|
||||
writePhaseState({
|
||||
projectName: PROJECT_NAME,
|
||||
@@ -433,7 +433,7 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
id: 'UI-01',
|
||||
name: 'create project workbench',
|
||||
input: `login A; create project ${PROJECT_NAME}; assign B`,
|
||||
actual: 'template hint visible; doc-p revision-state count=0; no 404',
|
||||
actual: 'template hint visible; doc-p revision-state count=0; no revision-state 404',
|
||||
networkLogs: logs,
|
||||
durationMs: Date.now() - t0,
|
||||
verdict: 'PASS',
|
||||
@@ -856,26 +856,27 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
saveResults();
|
||||
});
|
||||
|
||||
test('UI-16 local upload orphan must not fake V1 ledger', async () => {
|
||||
test('UI-16 registered upload uses server doc-free ledger (no doc-upload orphan)', async () => {
|
||||
const t0 = Date.now();
|
||||
logs = [];
|
||||
// Switch to uploaded private tab if present
|
||||
const uploadTab = page.getByText(`upload-${suffix}`).or(page.getByText(/attachment/));
|
||||
if ((await uploadTab.count()) === 0) fail('upload orphan tab missing (UI-11 must have uploaded)');
|
||||
if ((await uploadTab.count()) === 0) fail('upload tab missing (UI-11 must have uploaded via page)');
|
||||
await uploadTab.first().click();
|
||||
await expect(page.locator('text=版本账本未就绪')).toHaveCount(0);
|
||||
// Must not show real Vn ledger for orphan; template/local hint OK
|
||||
const hasToolbar = await page.locator('[data-testid="doc-rev-version"]').isVisible().catch(() => false);
|
||||
if (hasToolbar) fail('IndexedDB-only upload must not mount live revision toolbar / fake V1');
|
||||
await expect(page.getByTestId('doc-rev-template-hint').or(page.locator('text=本地预览'))).toBeVisible({
|
||||
timeout: 10_000,
|
||||
});
|
||||
await shot(page, 'UI-16-orphan-upload.png');
|
||||
// Registered upload must mount real toolbar (doc-free), not orphan hint.
|
||||
await expect(page.locator('[data-testid="doc-rev-version"]')).toBeVisible({ timeout: 30_000 });
|
||||
const orphanHits = logs.filter(
|
||||
(l) => l.path.includes('/revision-state') && /\/documents\/doc-upload-/.test(l.path),
|
||||
);
|
||||
if (orphanHits.length) {
|
||||
fail(`must not request revision-state for doc-upload orphan: ${JSON.stringify(orphanHits)}`);
|
||||
}
|
||||
await shot(page, 'UI-16-registered-upload.png');
|
||||
recordPass({
|
||||
id: 'UI-16',
|
||||
name: 'upload orphan empty ledger',
|
||||
input: 'open page-uploaded attachment tab',
|
||||
actual: 'no fake V1; local/template hint',
|
||||
name: 'registered upload server ledger',
|
||||
input: 'open page-uploaded attachment after createFreeform registration',
|
||||
actual: 'toolbar V visible; no doc-upload revision-state',
|
||||
networkLogs: logs,
|
||||
durationMs: Date.now() - t0,
|
||||
verdict: 'PASS',
|
||||
|
||||
Reference in New Issue
Block a user