/** * CE-WP-0007-T10/T11/T12 — add-field type+label dialog and field edit icon. */ // @vitest-environment happy-dom import { act, 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 type { Selector } from "@shared/selector"; import type { PdfSelectionCapture } from "@anchor/index"; import manifest from "../../fixtures/pdfs/manifest.json" with { type: "json" }; interface ViewerProps { pdfUrl: string; storedAnnotations: readonly { id: string; text: string; selectors: readonly Selector[] }[]; scrollToAnnotationId?: string; onSelectionCaptured(capture: PdfSelectionCapture, selectors: Selector[]): void; } const viewerSnapshot: { onSelectionCaptured: ViewerProps["onSelectionCaptured"] | null } = { onSelectionCaptured: null, }; vi.mock("@anchor/index", async (importOriginal) => { const original = await importOriginal(); const MockPdfSpikeViewer = (props: ViewerProps) => { viewerSnapshot.onSelectionCaptured = props.onSelectionCaptured; return
; }; return { ...original, PdfSpikeViewer: MockPdfSpikeViewer }; }); const FIXTURE = manifest.fixtures.find((f) => f.id === "fristsetzung-bezifferung")!; const SYNTHETIC_CANONICAL = ["Pre.", FIXTURE.known_good_quote, "Post."].join(" "); import { seedSessionWithDoc } from "./helpers/seed-session"; function syntheticCaptureFor(text: string, page: number): PdfSelectionCapture { return { kind: "pdf", text, page, rects: [{ x: 0.1, y: 0.2, width: 0.4, height: 0.04 }], boundingRect: { x: 0.1, y: 0.2, width: 0.4, height: 0.04 }, }; } async function loadApp() { const { App } = await import("@app/App"); return render(); } async function saveEvidenceInReview( user: ReturnType, commentary: string, ) { await waitFor(() => expect(viewerSnapshot.onSelectionCaptured).not.toBeNull()); await act(async () => { viewerSnapshot.onSelectionCaptured!( syntheticCaptureFor(FIXTURE.known_good_quote, FIXTURE.known_good_quote_page), [{ type: "TextQuoteSelector", exact: FIXTURE.known_good_quote }], ); }); await user.type(screen.getByTestId("inline-capture-commentary"), commentary); await user.click(screen.getByTestId("inline-capture-save")); await screen.findByText(new RegExp(commentary)); } describe("Capture — field add/edit UX (CE-WP-0007-T10/T11)", () => { beforeEach(() => { viewerSnapshot.onSelectionCaptured = null; globalThis.localStorage?.clear(); if (typeof window !== "undefined") { history.replaceState(null, "", window.location.pathname); } seedSessionWithDoc({ sessionName: "T10-field-edit", documentTitle: FIXTURE.filename, canonicalText: SYNTHETIC_CANONICAL, mode: "forms", }); }); afterEach(() => { vi.restoreAllMocks(); cleanup(); }); it( "adds a date field with a custom label via the add-field form", { timeout: 15000 }, async () => { const user = userEvent.setup(); await loadApp(); await user.click(screen.getByTestId("add-field-button")); await screen.findByTestId("field-add-form"); await user.clear(screen.getByTestId("field-add-label-input")); await user.type(screen.getByTestId("field-add-label-input"), "Hearing date"); await user.selectOptions(screen.getByTestId("field-add-type-select"), "date"); await user.click(screen.getByTestId("field-add-save")); const hearingInput = await screen.findByLabelText("Hearing date"); expect(hearingInput.getAttribute("type")).toBe("date"); }, ); it( "edits an existing field label and type via the pencil icon", { timeout: 15000 }, async () => { const user = userEvent.setup(); await loadApp(); await user.click(screen.getByTestId("field-edit-toggle-summary")); await screen.findByTestId("field-edit-summary-form"); await user.clear(screen.getByTestId("field-edit-summary-label-input")); await user.type( screen.getByTestId("field-edit-summary-label-input"), "Matter summary", ); await user.selectOptions( screen.getByTestId("field-edit-summary-type-select"), "text", ); await user.click(screen.getByTestId("field-edit-summary-save")); await screen.findByLabelText("Matter summary"); expect(screen.queryByLabelText("Summary of the matter")).toBeNull(); }, ); it( "links evidence to a newly added field", { timeout: 20000 }, async () => { const user = userEvent.setup(); seedSessionWithDoc({ sessionName: "T12-field-link", documentTitle: FIXTURE.filename, canonicalText: SYNTHETIC_CANONICAL, }); await loadApp(); await saveEvidenceInReview(user, "Link to custom field"); await user.click(screen.getByRole("button", { name: "Capture" })); await user.click(screen.getByTestId("add-field-button")); const addLabel = screen.getByTestId("field-add-label-input"); await user.clear(addLabel); await user.type(addLabel, "Custom slot"); await user.click(screen.getByTestId("field-add-save")); const customField = await screen.findByLabelText("Custom slot"); await user.click(customField); const stripCard = screen.getByRole("button", { name: /Link to custom field/ }); await user.click(stripCard); await waitFor(() => { const chips = screen.getAllByText(/1 evidence/); expect(chips.length).toBeGreaterThanOrEqual(1); }); }, ); });