From e7c0c1bf51ed5306de8c74668e0340a86b1c63d9 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Thu, 11 Jun 2026 23:59:40 -0700 Subject: [PATCH] test(make-pdf): diagram pre-pass unit suite + e2e render gates 34 unit tests (fence extraction incl. nested/tilde/unclosed/render=false, info-string parsing, slot substitution, diagnostic/figure escaping + SVG script strip, byte-level dimension probing across 5 formats, content-box math, image inlining incl. strict/remote/missing/data-URI paths). E2E gate proves through the compiled binary: both fences render as vector text (id-collision check), raw mermaid ships only via render=false, broken fence yields the diagnostic block, and the relative fixture image rasterizes to colored pixels (CRITICAL regression for the about:blank image fix). --strict exits non-zero on a missing image. Co-Authored-By: Claude Fable 5 --- make-pdf/test/diagram-prepass.test.ts | 300 ++++++++++++++++++ make-pdf/test/e2e/diagram-gate.test.ts | 155 +++++++++ .../test/fixtures/diagram-assets/red-box.png | Bin 0 -> 131 bytes make-pdf/test/fixtures/diagram-gate.md | 37 +++ 4 files changed, 492 insertions(+) create mode 100644 make-pdf/test/diagram-prepass.test.ts create mode 100644 make-pdf/test/e2e/diagram-gate.test.ts create mode 100644 make-pdf/test/fixtures/diagram-assets/red-box.png create mode 100644 make-pdf/test/fixtures/diagram-gate.md diff --git a/make-pdf/test/diagram-prepass.test.ts b/make-pdf/test/diagram-prepass.test.ts new file mode 100644 index 000000000..04df04c27 --- /dev/null +++ b/make-pdf/test/diagram-prepass.test.ts @@ -0,0 +1,300 @@ +/** + * Unit tests for the diagram pre-pass: fence extraction, info-string parsing, + * slot substitution, diagnostic blocks, image inlining policy, and the + * byte-level image dimension prober. No browse daemon required — the tab + * factory returns null so downscale paths are exercised as no-ops. + */ +import { describe, expect, test } from "bun:test"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import zlib from "node:zlib"; + +import { + StrictModeError, + buildDiagnosticBlock, + buildDiagramFigure, + contentWidthInches, + dimToInches, + extractDiagramFences, + inlineLocalImages, + parseInfoString, + substituteSlots, +} from "../src/diagram-prepass"; +import { imageDims } from "../src/image-size"; + +// ─── fence extraction ───────────────────────────────────────────────── + +describe("extractDiagramFences", () => { + test("extracts a mermaid fence and replaces it with a token paragraph", () => { + const md = "# T\n\n```mermaid\ngraph LR\n A --> B\n```\n\ntail"; + const { markdown, fences } = extractDiagramFences(md); + expect(fences).toHaveLength(1); + expect(fences[0].lang).toBe("mermaid"); + expect(fences[0].source).toBe("graph LR\n A --> B"); + expect(markdown).toContain(fences[0].token); + expect(markdown).not.toContain("```mermaid"); + }); + + test("extracts excalidraw fences", () => { + const md = '```excalidraw\n{"type":"excalidraw","elements":[]}\n```'; + const { fences } = extractDiagramFences(md); + expect(fences).toHaveLength(1); + expect(fences[0].lang).toBe("excalidraw"); + }); + + test("render=false keeps the fence as code and strips the flag", () => { + const md = "```mermaid render=false\ngraph LR\n X --> Y\n```"; + const { markdown, fences } = extractDiagramFences(md); + expect(fences).toHaveLength(0); + expect(markdown).toContain("```mermaid\ngraph LR"); + expect(markdown).not.toContain("render=false"); + }); + + test("title is captured from the info string", () => { + const md = '```mermaid title="Auth flow"\ngraph LR\n A --> B\n```'; + const { fences } = extractDiagramFences(md); + expect(fences[0].title).toBe("Auth flow"); + }); + + test("non-diagram fences pass through untouched", () => { + const md = "```js\nconst a = 1;\n```"; + const { markdown, fences } = extractDiagramFences(md); + expect(fences).toHaveLength(0); + expect(markdown).toBe(md); + }); + + test("a mermaid example inside a plain fence is never extracted", () => { + const md = "````\n```mermaid\ngraph LR\n```\n````"; + const { markdown, fences } = extractDiagramFences(md); + expect(fences).toHaveLength(0); + expect(markdown).toBe(md); + }); + + test("tilde fences work", () => { + const md = "~~~mermaid\ngraph TD\n A --> B\n~~~"; + const { fences } = extractDiagramFences(md); + expect(fences).toHaveLength(1); + }); + + test("unclosed fence at EOF replays verbatim", () => { + const md = "```mermaid\ngraph LR\n A --> B"; + const { markdown, fences } = extractDiagramFences(md); + expect(fences).toHaveLength(0); + expect(markdown).toBe(md); + }); + + test("multiple fences get distinct ordinals and tokens", () => { + const md = "```mermaid\nA\n```\n\nmiddle\n\n```mermaid\nB\n```"; + const { fences } = extractDiagramFences(md); + expect(fences).toHaveLength(2); + expect(fences[0].ordinal).toBe(1); + expect(fences[1].ordinal).toBe(2); + expect(fences[0].token).not.toBe(fences[1].token); + }); +}); + +describe("parseInfoString", () => { + test("plain language", () => { + expect(parseInfoString("mermaid")).toEqual({ lang: "mermaid", render: true, title: undefined }); + }); + test("render=false", () => { + expect(parseInfoString("mermaid render=false").render).toBe(false); + }); + test("single-quoted title", () => { + expect(parseInfoString("mermaid title='Hi there'").title).toBe("Hi there"); + }); +}); + +// ─── slots ──────────────────────────────────────────────────────────── + +describe("substituteSlots", () => { + test("replaces the

-wrapped token with slot HTML", () => { + const slots = new Map([["gstack-diagram-slot-ab-1", "

X
"]]); + const html = "

T

\n

gstack-diagram-slot-ab-1

\n

tail

"; + const out = substituteSlots(html, slots); + expect(out).toContain("
X
"); + expect(out).not.toContain("gstack-diagram-slot"); + expect(out).not.toContain("

"); + }); +}); + +describe("diagnostic + figure blocks", () => { + const fence = { + lang: "mermaid", source: "graph LR\n A --> B", render: true, + token: "t", ordinal: 3, title: undefined, + }; + test("diagnostic block escapes error content and names the lang", () => { + const block = buildDiagnosticBlock(fence, 'Parse "quoted"'); + expect(block).toContain("diagram-error"); + expect(block).toContain("Diagram failed to render (mermaid)"); + expect(block).toContain("Parse <error>"); + expect(block).not.toContain(""); + }); + test("figure carries role=img and ordinal-based aria-label fallback", () => { + const fig = buildDiagramFigure(fence, ""); + expect(fig).toContain('role="img"'); + expect(fig).toContain('aria-label="diagram 3"'); + expect(fig).toContain(""); + }); + test("figure strips scripts from SVG (sanitizer second layer)", () => { + const fig = buildDiagramFigure(fence, ""); + expect(fig).not.toContain("