/** * packages/shared — renderMarkdown tests (TASK-0112 SEND_EMAIL). * * The critical contract is XSS-safety: input is escaped FIRST, then * transforms are applied, so no raw HTML survives. Also: the supported * inline/block set, safe link targets only (javascript: defanged), and * total behavior (any string in → a safe string out). */ import { describe, expect, it } from "vitest"; import { renderMarkdown } from "./markdown.js"; describe("renderMarkdown", () => { it("escapes raw HTML (the XSS contract)", () => { expect(renderMarkdown("")).toBe("

<script>alert(1)</script>

"); expect(renderMarkdown('')).toBe("

<img src=x onerror="alert(1)">

"); expect(renderMarkdown("**bold**")).toContain("bold"); expect(renderMarkdown("**bold**")).not.toContain(" { expect(renderMarkdown("# H1")).toBe("

H1

"); expect(renderMarkdown("## H2")).toBe("

H2

"); expect(renderMarkdown("### H3")).toBe("

H3

"); }); it("renders bold, italic, code", () => { expect(renderMarkdown("**b**")).toContain("b"); expect(renderMarkdown("*i*")).toContain("i"); expect(renderMarkdown("a `c` b")).toContain("c"); }); it("renders unordered + ordered lists", () => { expect(renderMarkdown("- a\n- b")).toBe(""); expect(renderMarkdown("1. a\n2. b")).toBe("
    \n
  1. a
  2. \n
  3. b
  4. \n
"); }); it("links: http(s) only; javascript: defanged", () => { expect(renderMarkdown("[t](https://x.io/a)")).toBe('

t

'); expect(renderMarkdown("[t](javascript:alert(1))")).toContain('href="#"'); expect(renderMarkdown("[t](javascript:alert(1))")).not.toContain("javascript:"); }); it("paragraphs + line breaks", () => { expect(renderMarkdown("line1\nline2")).toBe("

line1
line2

"); expect(renderMarkdown("a\n\nb")).toBe("

a

\n

b

"); }); it("empty string → empty output", () => { expect(renderMarkdown("")).toBe(""); }); it("no raw < ever emitted from user text (except our own tags)", () => { const out = renderMarkdown("
hi
\n"); expect(out).not.toMatch(/