import { test } from "node:test"; import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import * as React from "react"; import { renderToStaticMarkup } from "react-dom/server"; import type { Report } from "../../lib/report/schema"; import { validateReport } from "../../lib/report/validate"; import { buildReportPageView, type ReportPageView } from "../../lib/report/views"; import { buildReportSlides } from "../../lib/report/slides"; import { reportSlidesHtml, slidesImagePaths, SLIDES_EXPORT_CSS } from "../../lib/report/exportSlidesHtml"; import { Slide } from "./slides/Slide"; (globalThis as { React?: typeof React }).React = React; const HERE = path.dirname(fileURLToPath(import.meta.url)); const CSS = readFileSync(path.join(HERE, "..", "..", "styles", "slides.css"), "utf8"); function report(): Report { return { format: "archilyzer-report", version: 1, id: "demo", kind: "factcheck", series: "Demo Checks", title: "A demo ", summary: "One thing [right](cite:v1).", published: "2026-10-01", subject: { source: "s0" }, sources: { s0: { kind: "article", title: "An article", accent: "#8a6fb0" } }, citations: { v1: { kind: "video", channel: "demo-channel", id: "abc123", start: 10, end: 20, quote: "one" }, p1: { kind: "post", channel: "demo-social", id: "123", quote: "three" }, a1: { kind: "source", source: "s0", quote: "the claim", image: "stills/a1.png" }, }, slides: { closing: "The end." }, entries: [{ id: "e1", date: "2026-10-03T08:00:00Z", title: "An update", body: "It [moved](cite:v1). Then more. And more." }], sections: [ { id: "ch1", title: "Chapter one", body: "Opening. Second.", slide: { points: ["A point [citing](cite:v1)"], cite: "a1" }, claims: [ { id: "k1", text: "Claim one.", verdict: "CONTRADICTED", gist: "No.", sourceQuote: { citation: "a1" }, citations: ["a1", "v1"] }, { id: "k2", text: "Claim two.", verdict: "PARTLY", citations: ["p1"], slide: { layout: "quote" } }, { id: "k3", text: "Claim three.", flag: "No source given" }, ], }, ], }; } function view(r = report()): ReportPageView { assert.deepEqual(validateReport(r), []); return buildReportPageView(r, { record: (c) => ({ channel: c.channel, id: c.id, title: `Record ${c.id}` }), poster: () => "/media/posters/abc123.png", post: () => ({ author: "@demo", shot: "/media/posts/demo-social/123/shot.png" }), }); } const FOOTER = { revision: 2, date: "2026-10-04", reportSha256: "ab".repeat(32) }; const images: Record = { "/media/posters/abc123.png": "data:image/png;base64,AAA", "/reports/demo/stills/a1.png": "data:image/png;base64,BBB", "/media/posts/demo-social/123/shot.png": "data:image/png;base64,CCC", }; const html = (v = view(), siteUrl = "https://site.example/") => reportSlidesHtml(v, { css: CSS, accent: "#6a4bc4", siteUrl, footer: FOOTER, image: (p) => images[p] }); test("one page per slide, numbered as the site's hash numbers them, in the slides' order", () => { const v = view(); const slides = buildReportSlides(v); const out = html(v); const pages = [...out.matchAll(/
Number(m[1])); assert.deepEqual(pages, slides.map((s) => s.n)); const keys = [...out.matchAll(/
m[1]); assert.deepEqual(keys, slides.map((s) => s.key)); }); test("one file: the stylesheet and the script inline, every picture the resolver's, nothing fetched", () => { const out = html(); assert.ok(out.includes(CSS.slice(0, 200))); assert.ok(out.includes(SLIDES_EXPORT_CSS.slice(0, 80))); assert.equal([...out.matchAll(/