diff --git a/tests/integration/anchor-html-roundtrip.test.ts b/tests/integration/anchor-html-roundtrip.test.ts new file mode 100644 index 0000000..69c7580 --- /dev/null +++ b/tests/integration/anchor-html-roundtrip.test.ts @@ -0,0 +1,68 @@ +/** + * Integration round-trip: HTML/Markdown ingest → createSelectors → resolveSelectors. + * + * Crosses the source ↔ anchor boundary for non-paginated formats (EANCH-WP-0003). + */ + +import { describe, expect, it } from "vitest"; + +import { ingestHtml, ingestMarkdown } from "@citation-evidence/evidence-source"; +import { createSelectors, resolveSelectors } from "@citation-evidence/evidence-anchor/selectors"; +import type { DomSelectionCapture } from "@citation-evidence/evidence-anchor/types"; + +const HTML_FIXTURE = ` +
+Known good quote for HTML ingest testing.
+`; + +const MARKDOWN_FIXTURE = `# Hello World + +Known good quote for Markdown ingest testing. + +**bold** text. +`; + +const CASES = [ + { + id: "html-basic", + ingest: () => ingestHtml(HTML_FIXTURE, { filename: "sample.html" }), + quote: "Known good quote for HTML ingest testing.", + }, + { + id: "markdown-basic", + ingest: () => ingestMarkdown(MARKDOWN_FIXTURE, { filename: "sample.md" }), + quote: "Known good quote for Markdown ingest testing.", + }, +] as const; + +function domCapture(text: string): DomSelectionCapture { + return { + kind: "dom", + text, + startPath: [0, 0], + startOffset: 0, + endPath: [0, 0], + endOffset: text.length, + structuralPath: [{ kind: "section", index: 0 }], + }; +} + +describe("create + resolve round-trip — HTML/Markdown", () => { + for (const c of CASES) { + it(`${c.id}: known-good quote round-trips with confidence ≥ 0.9`, async () => { + const { representation } = await c.ingest(); + const selectors = createSelectors(domCapture(c.quote), representation); + const resolution = resolveSelectors(selectors, representation); + + expect(resolution.status).toBe("resolved"); + expect(resolution.confidence).toBeGreaterThanOrEqual(0.9); + + const span = resolution.candidates[0]?.textPosition; + expect(span).toBeDefined(); + const canonical = representation.canonicalText ?? ""; + expect(canonical.slice(span!.start, span!.end)).toBe(c.quote); + expect(selectors.some((s) => s.type === "DomRangeSelector")).toBe(true); + }); + } +}); \ No newline at end of file