/** * HTML citation card renderer tests (CE-WP-0004-T03). * * Snapshots lock the output format defined in * `docs/decisions/ADR-0007-citation-card-format.md`. Class names are * part of the public contract — renaming any of them requires updating * both this test and the ADR. */ import { describe, expect, it } from "vitest"; import type { Annotation } from "@shared/annotation"; import type { Document } from "@shared/document"; import type { EvidenceItem } from "@shared/evidence"; import type { AnnotationId, DocumentId, EvidenceItemId, RepresentationId, } from "@shared/ids"; import { renderCitationCardHtml } from "./html"; const DOC_ID = "doc_2024-order" as DocumentId; const REP_ID = "rep_2024-order" as RepresentationId; const ANN_ID = "ann_para3" as AnnotationId; const EV_ID = "ev_para3" as EvidenceItemId; function makeDoc(overrides: Partial = {}): Document { return { id: DOC_ID, title: "Order from 14 Mar 2024", mediaType: "application/pdf", createdAt: "2026-05-25T00:00:00.000Z", updatedAt: "2026-05-25T00:00:00.000Z", ...overrides, }; } function makeAnn(overrides: Partial = {}): Annotation { return { id: ANN_ID, documentId: DOC_ID, representationId: REP_ID, selectors: [], quote: "Die Frist endet am 31. März 2024.", normalizeVersion: 1, createdAt: "2026-05-25T00:00:00.000Z", updatedAt: "2026-05-25T00:00:00.000Z", ...overrides, }; } function makeEv(overrides: Partial = {}): EvidenceItem { return { id: EV_ID, annotationIds: [ANN_ID], status: "candidate", createdAt: "2026-05-25T00:00:00.000Z", updatedAt: "2026-05-25T00:00:00.000Z", ...overrides, }; } describe("renderCitationCardHtml()", () => { it("renders the full aside with quote, attribution, and commentary", () => { const out = renderCitationCardHtml({ evidenceItem: makeEv({ commentary: "Deadline clause for the buyer." }), document: makeDoc(), annotation: makeAnn(), }); expect(out).toMatchInlineSnapshot(` " " `); }); it("omits the commentary div when none is set", () => { const out = renderCitationCardHtml({ evidenceItem: makeEv(), document: makeDoc(), annotation: makeAnn(), }); expect(out).toMatchInlineSnapshot(` " " `); }); it("converts \\n inside the quote to
for rich-text paste", () => { const out = renderCitationCardHtml({ evidenceItem: makeEv(), document: makeDoc(), annotation: makeAnn({ quote: "Line one.\nLine two." }), }); expect(out).toContain( '
Line one.
Line two.
', ); }); it("HTML-escapes &, <, >, \", and ' in user-supplied text", () => { const out = renderCitationCardHtml({ evidenceItem: makeEv({ commentary: `Notes: & 'untrusted'`, }), document: makeDoc({ title: `Order "draft" & more` }), annotation: makeAnn({ quote: "<> & 'inner'" }), }); // Quote escaping expect(out).toContain( "
<<value>> & 'inner'
", ); // Source label escaping expect(out).toContain( "Order "draft" & more", ); // Commentary escaping — and especially: no raw