generated from coulomb/repo-seed
Persist capture data per session (field values, schema, links)
Capture mode state lived only in React memory and was lost when reopening a session or remounting EngineProvider. - Add per-session localStorage capture snapshot (schema, values, links) - Restore on session mount; persist on field/schema/link changes - Seed binder links from storage without spurious bus events - Clean up capture key when session is deleted - Integration test for reload persistence
This commit is contained in:
95
src/app/forms/capture-persistence.test.ts
Normal file
95
src/app/forms/capture-persistence.test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* CE-WP-0008 — per-session capture state round-trip.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { SessionId } from "@shared/ids";
|
||||
|
||||
import {
|
||||
CAPTURE_STATE_VERSION,
|
||||
captureStateKey,
|
||||
defaultCaptureState,
|
||||
loadCaptureState,
|
||||
persistCapturePatch,
|
||||
removeCaptureState,
|
||||
saveCaptureState,
|
||||
} from "./capture-persistence";
|
||||
|
||||
function memoryStorage(): Storage {
|
||||
const map = new Map<string, string>();
|
||||
return {
|
||||
get length() {
|
||||
return map.size;
|
||||
},
|
||||
clear: () => map.clear(),
|
||||
getItem: (k) => map.get(k) ?? null,
|
||||
key: (i) => [...map.keys()][i] ?? null,
|
||||
removeItem: (k) => void map.delete(k),
|
||||
setItem: (k, v) => void map.set(k, v),
|
||||
};
|
||||
}
|
||||
|
||||
const SESSION = "sess_capture" as SessionId;
|
||||
|
||||
describe("capture-persistence", () => {
|
||||
it("uses a per-session storage key", () => {
|
||||
expect(captureStateKey(SESSION)).toBe(
|
||||
"citation-evidence:session:sess_capture:capture-state:v1",
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips schema, field values, and links", () => {
|
||||
const storage = memoryStorage();
|
||||
const state = defaultCaptureState();
|
||||
const withData = {
|
||||
...state,
|
||||
fieldValues: { summary: "Tenant owes arrears", deadline: "2026-12-15" },
|
||||
evidenceLinks: [
|
||||
{
|
||||
id: "evlink_1",
|
||||
evidenceItemId: "evi_1",
|
||||
targetType: "form-field",
|
||||
targetId: "summary",
|
||||
relation: "supports",
|
||||
status: "candidate",
|
||||
createdAt: "2026-06-08T00:00:00.000Z",
|
||||
updatedAt: "2026-06-08T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
saveCaptureState(SESSION, withData, storage);
|
||||
const loaded = loadCaptureState(SESSION, storage);
|
||||
|
||||
expect(loaded?.version).toBe(CAPTURE_STATE_VERSION);
|
||||
expect(loaded?.fieldValues).toEqual(withData.fieldValues);
|
||||
expect(loaded?.evidenceLinks).toHaveLength(1);
|
||||
expect(loaded?.formSchema.id).toBe("demo-form");
|
||||
});
|
||||
|
||||
it("persistCapturePatch merges without dropping other fields", () => {
|
||||
const storage = memoryStorage();
|
||||
saveCaptureState(
|
||||
SESSION,
|
||||
{
|
||||
...defaultCaptureState(),
|
||||
fieldValues: { amount: "1200" },
|
||||
evidenceLinks: [],
|
||||
},
|
||||
storage,
|
||||
);
|
||||
|
||||
persistCapturePatch(SESSION, { fieldValues: { amount: "1500", summary: "Updated" } }, storage);
|
||||
|
||||
const loaded = loadCaptureState(SESSION, storage);
|
||||
expect(loaded?.fieldValues).toEqual({ amount: "1500", summary: "Updated" });
|
||||
});
|
||||
|
||||
it("removeCaptureState clears the key", () => {
|
||||
const storage = memoryStorage();
|
||||
saveCaptureState(SESSION, defaultCaptureState(), storage);
|
||||
removeCaptureState(SESSION, storage);
|
||||
expect(loadCaptureState(SESSION, storage)).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user