feat(03-01): Task 2 — assertA29 + driveA29 + orchestrator wiring (A29 30/30 GREEN)
Page-side (tests/uat/extension-page-harness.ts):
- assertA29 dispatches probe-page DOM mutation (input value + modal
toggle), settles 500ms for rrweb IncrementalSnapshot to enqueue,
setupFreshRecording, 11s segment-settle, SAVE_ARCHIVE; pushes
A29.1 SAVE ack check. Module-local constants:
A29_SAVE_ARCHIVE_TIMEOUT_MS=15s, A29_SEGMENT_SETTLE_MS=11s,
A29_MUTATION_SETTLE_MS=500ms.
- declare global interface + window.__mokoshHarness object literal
extended with assertA29 (single-method-per-assertion contract).
- statusEl + console banner updated A28 → A29 + cite Plan 03-01.
Host-side (tests/uat/lib/harness-page-driver.ts):
- Add `import { EventType } from '@rrweb/types';`.
- driveA29 — 3-phase orchestration mirroring driveA26:
Phase 1 page.evaluate harness.assertA29(); Phase 2 findLatestZip;
Phase 3 JSZip.loadAsync rrweb/session.json + EventType grep.
Appends A29.0a (rrweb/session.json present) + A29.2..A29.5
(events.length>0 + Meta + FullSnapshot + IncrementalSnapshot).
Orchestrator (tests/uat/harness.test.ts):
- driveA29 imported after driveA28.
- driveA29Wrapped const captures handles.downloadsDir.
- drivers array push A29 entry with banner citing Plan 03-01 + Pitfall 1.
- Architecture banner string updated A28 → A29.
Empirical verification (HEADLESS=1 SKIP_PROD_REBUILD=0 npm run test:uat):
- UAT harness: 30/30 GREEN (29 prior + A29 NEW).
- A29 events.length=4; event types observed: 2, 3, 4 (FullSnapshot,
IncrementalSnapshot, Meta — all three required types present).
- Pitfall 1 mitigation empirically verified — the pre-SAVE DOM
mutation produced the IncrementalSnapshot.
- vitest 171/171 GREEN preserved (full suite).
- Tier-1 FORBIDDEN_HOOK_STRINGS unit gate 13/13 GREEN (12 strings × 0
hits each) — A29 rides production rrweb wiring + GET_RRWEB_EVENTS
bridge + sendMessageWithTimeout helper; NO new __MOKOSH_UAT__
symbols.
- npx tsc --noEmit exit 0.
This commit is contained in:
@@ -3315,6 +3315,109 @@ async function assertA28(): Promise<AssertionResult> {
|
||||
};
|
||||
}
|
||||
|
||||
/* ─── Plan 03-01 Task 2 — A29 (rrweb DOM verification; SPEC §10 #4) ─
|
||||
*
|
||||
* A29 — REQ-rrweb-dom-buffer empirical: rrweb's record() (already
|
||||
* wired at src/content/index.ts:285) emits Meta + FullSnapshot
|
||||
* + at least one IncrementalSnapshot when the harness page
|
||||
* contains the probe HTML (form + table + modal) AND the driver
|
||||
* injects a DOM mutation before SAVE (RESEARCH Pitfall 1:
|
||||
* static probe HTML emits Meta + FullSnapshot but not
|
||||
* IncrementalSnapshot without mutation).
|
||||
*
|
||||
* Page side: dispatch the probe-page DOM mutations (input value +
|
||||
* modal toggle), settle, setupFreshRecording, settle one segment,
|
||||
* dispatch SAVE_ARCHIVE, push A29.1 ack check. The host-side driveA29
|
||||
* does the EventType-enum-shape grep against rrweb/session.json from
|
||||
* the assembled zip (matches A26's chained-assertion pattern; JSZip
|
||||
* + @rrweb/types are host-only deps).
|
||||
*
|
||||
* FORBIDDEN_HOOK_STRINGS impact: NONE. A29 rides production rrweb
|
||||
* wiring (record() at src/content/index.ts:285 + GET_RRWEB_EVENTS
|
||||
* round-trip at src/background/index.ts → src/content/index.ts:318)
|
||||
* + the existing setupFreshRecording / sendMessageWithTimeout
|
||||
* helpers. Tier-1 inventory stays at 12 entries.
|
||||
*/
|
||||
|
||||
/** SAVE_ARCHIVE dispatch timeout for A29 — matches A24/A25/A27. */
|
||||
const A29_SAVE_ARCHIVE_TIMEOUT_MS = 15_000;
|
||||
/** Pre-SAVE segment-settle window (10s rotation + 1s slack). */
|
||||
const A29_SEGMENT_SETTLE_MS = 11_000;
|
||||
/** Settle window between DOM mutation and SAVE so rrweb's
|
||||
* IncrementalSnapshot lands in the in-memory buffer before
|
||||
* GET_RRWEB_EVENTS fires. */
|
||||
const A29_MUTATION_SETTLE_MS = 500;
|
||||
|
||||
/**
|
||||
* A29 — rrweb DOM event recording empirical (SPEC §10 #4).
|
||||
*
|
||||
* Page-side dispatches the probe-page mutation (input.value + modal
|
||||
* toggle), settles, runs setupFreshRecording + segment-settle, then
|
||||
* dispatches SAVE_ARCHIVE. Only the SAVE ack lives in the
|
||||
* page-side checks; the EventType-enum-shape grep is host-side
|
||||
* because @rrweb/types is host-only.
|
||||
*
|
||||
* @returns AssertionResult with 1 page-side check (SAVE ack); host-side
|
||||
* driveA29 appends A29.0a/A29.2..A29.5 EventType checks.
|
||||
*/
|
||||
async function assertA29(): Promise<AssertionResult> {
|
||||
const result: AssertionResult = {
|
||||
passed: false,
|
||||
name: 'A29 — rrweb DOM events recorded without errors (SPEC §10 #4 / REQ-rrweb-dom-buffer)',
|
||||
checks: [],
|
||||
diagnostics: [],
|
||||
};
|
||||
|
||||
try {
|
||||
diag(result, 'Step 1: dispatch probe-page DOM mutation (input value + modal toggle)');
|
||||
const textInput = document.querySelector<HTMLInputElement>('#probe-text');
|
||||
if (textInput !== null) {
|
||||
textInput.value = 'probe';
|
||||
textInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
const modalTrigger = document.querySelector<HTMLButtonElement>('#probe-modal-trigger');
|
||||
if (modalTrigger !== null) {
|
||||
modalTrigger.click();
|
||||
}
|
||||
diag(result, `Step 1 OK — mutations dispatched (#probe-text=${textInput !== null}, #probe-modal-trigger=${modalTrigger !== null})`);
|
||||
|
||||
diag(result, `Step 2: settle ${A29_MUTATION_SETTLE_MS}ms for rrweb IncrementalSnapshot to enqueue`);
|
||||
await new Promise((r) => setTimeout(r, A29_MUTATION_SETTLE_MS));
|
||||
|
||||
diag(result, 'Step 3: setupFreshRecording');
|
||||
const setupResp = await setupFreshRecording();
|
||||
if (!setupResp.ok) {
|
||||
throw new Error(`setupFreshRecording failed: ${setupResp.error ?? '(no error)'}`);
|
||||
}
|
||||
diag(result, 'Step 3 OK — REC state established');
|
||||
|
||||
diag(result, `Step 4: settle ${A29_SEGMENT_SETTLE_MS}ms for first segment rotation`);
|
||||
await new Promise((r) => setTimeout(r, A29_SEGMENT_SETTLE_MS));
|
||||
|
||||
diag(result, 'Step 5: dispatch SAVE_ARCHIVE');
|
||||
const ack = await sendMessageWithTimeout<{ success: boolean; error?: string }>(
|
||||
{ type: 'SAVE_ARCHIVE' },
|
||||
A29_SAVE_ARCHIVE_TIMEOUT_MS,
|
||||
'SAVE_ARCHIVE (A29)',
|
||||
);
|
||||
diag(result, `Step 5 result: ${JSON.stringify(ack)}`);
|
||||
|
||||
result.checks.push({
|
||||
name: 'A29.1: SAVE_ARCHIVE ack received with success=true',
|
||||
expected: true,
|
||||
actual: ack.success,
|
||||
passed: ack.success === true,
|
||||
});
|
||||
|
||||
result.passed = result.checks.every((c) => c.passed);
|
||||
} catch (err) {
|
||||
result.error = err instanceof Error ? err.message : String(err);
|
||||
diag(result, `THREW: ${result.error}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read `chrome.runtime.getManifest().version`. Used by the host-side
|
||||
* orchestrator at startup to capture the expected version for A13's
|
||||
@@ -3366,6 +3469,8 @@ declare global {
|
||||
assertA26: () => Promise<AssertionResult>;
|
||||
assertA27: () => Promise<A27Result>;
|
||||
assertA28: () => Promise<AssertionResult>;
|
||||
// Plan 03-01 — rrweb DOM verification (SPEC §10 #4)
|
||||
assertA29: () => Promise<AssertionResult>;
|
||||
getManifestVersion: () => Promise<string>;
|
||||
};
|
||||
}
|
||||
@@ -3400,14 +3505,15 @@ window.__mokoshHarness = {
|
||||
assertA26,
|
||||
assertA27,
|
||||
assertA28,
|
||||
assertA29,
|
||||
getManifestVersion,
|
||||
};
|
||||
|
||||
const statusEl = document.getElementById('status');
|
||||
if (statusEl !== null) {
|
||||
statusEl.textContent = 'Harness ready. window.__mokoshHarness.{assertA1..A28, getManifestVersion} available.';
|
||||
statusEl.textContent = 'Harness ready. window.__mokoshHarness.{assertA1..A29, getManifestVersion} available.';
|
||||
}
|
||||
|
||||
console.log('[harness-page] ready — window.__mokoshHarness installed (Plan 01-13 Task 9: A1..A14 + Plan 01-10 Wave 3: A15..A17 + Plan 01-12 Wave 6: A18..A22 + Plan 01-14: A23 + Plan 02-04 Tasks 1-2: A24+A25 + Plan 02-04 Task 3: A26+A27+A28 + getManifestVersion)');
|
||||
console.log('[harness-page] ready — window.__mokoshHarness installed (Plan 01-13 Task 9: A1..A14 + Plan 01-10 Wave 3: A15..A17 + Plan 01-12 Wave 6: A18..A22 + Plan 01-14: A23 + Plan 02-04 Tasks 1-2: A24+A25 + Plan 02-04 Task 3: A26+A27+A28 + Plan 03-01: A29 + getManifestVersion)');
|
||||
|
||||
export {};
|
||||
|
||||
Reference in New Issue
Block a user