fix(e2e): persist the winning A1 body snippet for post-restart
UI-13b already made A1 the durable document, but phase-state still expected the UI-06 marker. Record successful saves as the latest snippet, ignore conflict drafts, and write that final value at UI-15-pre without relaxing the restore assertion. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -28,6 +28,7 @@ import {
|
||||
SHARED_PHASE_STATE_FILE,
|
||||
playwrightResultsFileName,
|
||||
} from '../src/lib/playwrightPhaseArtifacts';
|
||||
import { nextDurableBodySnippet } from '../src/lib/durableBodySnippet';
|
||||
|
||||
/**
|
||||
* Document revision runtime — real UI acceptance (UI-01..UI-16).
|
||||
@@ -670,6 +671,8 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
let secondDocTitle = '';
|
||||
let secondDocId = '';
|
||||
let uploadedDocId = '';
|
||||
/** Latest server-durable body marker; UI-15-pre writes this as bodySnippet. */
|
||||
let latestDurableBodySnippet = '';
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
context = await browser.newContext();
|
||||
@@ -1047,8 +1050,12 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
timeout: 30_000,
|
||||
});
|
||||
await shot(page, 'UI-06-after-refresh.png');
|
||||
latestDurableBodySnippet = nextDurableBodySnippet(latestDurableBodySnippet, {
|
||||
kind: 'persisted',
|
||||
snippet: marker,
|
||||
}) || marker;
|
||||
const st = readPhaseState();
|
||||
writePhaseState({ ...st, bodySnippet: marker, revisionText: r1 });
|
||||
writePhaseState({ ...st, bodySnippet: latestDurableBodySnippet, revisionText: r1 });
|
||||
recordPass({
|
||||
id: 'UI-06',
|
||||
name: 'save + hard refresh',
|
||||
@@ -1386,6 +1393,15 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
await expect(page.getByText(markerA2)).toHaveCount(0);
|
||||
await shot(pageA2, 'UI-13b-conflict-A2.png');
|
||||
await ctxA2.close();
|
||||
latestDurableBodySnippet = nextDurableBodySnippet(latestDurableBodySnippet, {
|
||||
kind: 'persisted',
|
||||
snippet: markerA1,
|
||||
}) || markerA1;
|
||||
latestDurableBodySnippet = nextDurableBodySnippet(latestDurableBodySnippet, {
|
||||
kind: 'conflict_draft',
|
||||
snippet: markerA2,
|
||||
}) || latestDurableBodySnippet;
|
||||
writePhaseState({ ...readPhaseState(), bodySnippet: latestDurableBodySnippet });
|
||||
recordPass({
|
||||
id: 'UI-13b',
|
||||
name: 'author A/A head conflict',
|
||||
@@ -1448,6 +1464,7 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
controlledDocId,
|
||||
versionText: v,
|
||||
revisionText: r,
|
||||
bodySnippet: latestDurableBodySnippet || st.bodySnippet,
|
||||
userA: USER_A,
|
||||
userB: USER_B,
|
||||
userC: USER_C,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { nextDurableBodySnippet, requireDurableSnippet } from './durableBodySnippet';
|
||||
|
||||
describe('durableBodySnippet', () => {
|
||||
it('rejects empty persisted snippets', () => {
|
||||
assert.throws(() => requireDurableSnippet(' '), /empty_durable_body_snippet/);
|
||||
assert.throws(
|
||||
() => nextDurableBodySnippet('UI06-old', { kind: 'persisted', snippet: ' ' }),
|
||||
/empty_durable_body_snippet/,
|
||||
);
|
||||
});
|
||||
|
||||
it('lets a later successful revision replace the old snippet', () => {
|
||||
const afterUi06 = nextDurableBodySnippet(undefined, { kind: 'persisted', snippet: ' UI06-mu0eq020 ' });
|
||||
assert.equal(afterUi06, 'UI06-mu0eq020');
|
||||
const afterA1 = nextDurableBodySnippet(afterUi06, { kind: 'persisted', snippet: 'A1-win-mu0eq020' });
|
||||
assert.equal(afterA1, 'A1-win-mu0eq020');
|
||||
assert.notEqual(afterA1, afterUi06);
|
||||
});
|
||||
|
||||
it('does not promote a conflict-failed draft to the expected body', () => {
|
||||
const afterUi06 = nextDurableBodySnippet(undefined, { kind: 'persisted', snippet: 'UI06-mu0eq020' });
|
||||
const afterA1 = nextDurableBodySnippet(afterUi06, { kind: 'persisted', snippet: 'A1-win-mu0eq020' });
|
||||
const afterA2Conflict = nextDurableBodySnippet(afterA1, {
|
||||
kind: 'conflict_draft',
|
||||
snippet: 'A2-conflict-mu0eq020',
|
||||
});
|
||||
assert.equal(afterA2Conflict, 'A1-win-mu0eq020');
|
||||
assert.notEqual(afterA2Conflict, 'A2-conflict-mu0eq020');
|
||||
assert.notEqual(afterA2Conflict, 'UI06-mu0eq020');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
export type DurableBodyEvent =
|
||||
| { kind: 'persisted'; snippet: string }
|
||||
| { kind: 'conflict_draft'; snippet: string };
|
||||
|
||||
export function requireDurableSnippet(snippet: string): string {
|
||||
const next = snippet.trim();
|
||||
if (!next) {
|
||||
throw new Error('empty_durable_body_snippet');
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Latest server-durable body snippet for post-restart.
|
||||
* A later successful save replaces the previous snippet.
|
||||
* A conflict-failed editor draft never becomes the expected body.
|
||||
*/
|
||||
export function nextDurableBodySnippet(
|
||||
current: string | undefined,
|
||||
event: DurableBodyEvent,
|
||||
): string | undefined {
|
||||
if (event.kind === 'conflict_draft') {
|
||||
return current;
|
||||
}
|
||||
return requireDurableSnippet(event.snippet);
|
||||
}
|
||||
Reference in New Issue
Block a user