generated from coulomb/repo-seed
Add link: dependency on citation-engine, retarget @shared/@engine aliases, remove in-repo shared/engine copies. ADR-0002 accepted (option B). 172 tests, typecheck, and lint pass.
100 lines
3.1 KiB
TypeScript
100 lines
3.1 KiB
TypeScript
/**
|
|
* Capture state survives switching away from and back to a session.
|
|
*/
|
|
|
|
// @vitest-environment happy-dom
|
|
|
|
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import manifest from "../../fixtures/pdfs/manifest.json" with { type: "json" };
|
|
|
|
import { captureStateKey, loadCaptureState } from "@app/forms/capture-persistence";
|
|
|
|
vi.mock("@anchor/index", async (importOriginal) => {
|
|
const original = await importOriginal<typeof import("@anchor/index")>();
|
|
const MockPdfSpikeViewer = () => (
|
|
<div data-testid="mock-pdf-viewer" />
|
|
);
|
|
return { ...original, PdfSpikeViewer: MockPdfSpikeViewer };
|
|
});
|
|
|
|
const FIXTURE = manifest.fixtures.find((f) => f.id === "fristsetzung-bezifferung")!;
|
|
const SYNTHETIC_CANONICAL = ["Pre.", FIXTURE.known_good_quote, "Post."].join(" ");
|
|
const SESSION_NAME = "capture-persist-session";
|
|
|
|
import { seedSessionWithDoc } from "./helpers/seed-session";
|
|
|
|
async function loadApp() {
|
|
const { App } = await import("@app/App");
|
|
return render(<App />);
|
|
}
|
|
|
|
describe("Capture session persistence", () => {
|
|
beforeEach(() => {
|
|
globalThis.localStorage?.clear();
|
|
if (typeof window !== "undefined") {
|
|
history.replaceState(null, "", window.location.pathname);
|
|
}
|
|
seedSessionWithDoc({
|
|
sessionName: SESSION_NAME,
|
|
documentTitle: FIXTURE.filename,
|
|
canonicalText: SYNTHETIC_CANONICAL,
|
|
mode: "forms",
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
cleanup();
|
|
});
|
|
|
|
it(
|
|
"restores typed field values after leaving and reopening the session",
|
|
{ timeout: 20000 },
|
|
async () => {
|
|
const user = userEvent.setup();
|
|
const first = await loadApp();
|
|
|
|
const summary = screen.getByLabelText("Summary of the matter");
|
|
await user.type(summary, "Persisted summary text");
|
|
await user.click(screen.getByLabelText("Disputed amount"));
|
|
|
|
await waitFor(() => {
|
|
const stored = Object.values(globalThis.localStorage ?? {}).join("");
|
|
expect(stored).toContain("Persisted summary text");
|
|
});
|
|
|
|
first.unmount();
|
|
|
|
await loadApp();
|
|
|
|
const restored = screen.getByLabelText("Summary of the matter") as HTMLTextAreaElement;
|
|
await waitFor(() => {
|
|
expect(restored.value).toBe("Persisted summary text");
|
|
});
|
|
},
|
|
);
|
|
|
|
it(
|
|
"writes capture state under the per-session storage key",
|
|
{ timeout: 15000 },
|
|
async () => {
|
|
const user = userEvent.setup();
|
|
await loadApp();
|
|
|
|
await user.type(screen.getByLabelText("Disputed amount"), "EUR 500");
|
|
|
|
await waitFor(() => {
|
|
const keys = Object.keys(globalThis.localStorage ?? {});
|
|
const captureKey = keys.find((k) => k.includes(":capture-state:v1"));
|
|
expect(captureKey).toBeTruthy();
|
|
const sessionId = captureKey!.split(":")[2];
|
|
const state = loadCaptureState(sessionId as never);
|
|
expect(state?.fieldValues.amount).toBe("EUR 500");
|
|
expect(captureStateKey(sessionId as never)).toBe(captureKey);
|
|
});
|
|
},
|
|
);
|
|
}); |