md.test.ts
3.8 kB · typescript · 69 lines
1import { describe, expect, test } from "bun:test";2import { front, inline, render, sheet, slug } from "./md.ts";34const math = (tex: string, display: boolean) => `<m${display ? " d" : ""}>${tex}</m>`;56const link = (url: string) => (/^(https?:|#|\/)/.test(url) ? url : `/L/${url}`);78describe("md", () => {9 test("headings carry the slug of their text and repeat with a counter", () => {10 const html = render("## Base $\\pi$ and *em*\n\n## Base $\\pi$ and *em*", { math });11 expect(html).toContain(`<h2 id="${slug("base π and em")}">`);12 expect(html).toContain(`<h2 id="${slug("base π and em")}-1">`);13 });1415 test("a lone image line is a figure with an inline caption, an inline image stays an image", () => {16 expect(render("", { link })).toBe('<figure><img src="/L/walks-fig" alt="the walk"><figcaption>the <em>walk</em></figcaption></figure>');17 expect(render("see  here", { link })).toBe('<p>see <img src="/L/walks-fig" alt="walk"> here</p>');18 });1920 test("the widget line mounts a view through the hook and is a figure without one", () => {21 const widget = (name: string, view: string, caption: string) => `<w ${name} ${view}>${caption}</w>`;22 expect(render("", { widget })).toBe("<w life grid>Life</w>");23 expect(render("", { link })).toContain('<img src="/L/demos/life/grid"');24 });2526 test("math is inline at $, display at $$ on its own line or block, with escaped braces unescaped", () => {27 expect(render("$a<b$ and $$x^2$$", { math })).toBe("<p><m>a<b</m> and <m>x^2</m></p>");28 expect(render("$$x^2$$", { math })).toBe("<m d>x^2</m>");29 expect(render("$$\ny\n$$", { math })).toBe("<m d>y</m>");30 expect(render("$F = \\\\{0\\\\}$", { math })).toBe("<p><m>F = \\{0\\}</m></p>");31 expect(render("$5 and $10")).toBe("<p>$5 and $10</p>");32 });3334 test("a table is wrapped, aligned by class and one row per line", () => {35 const html = render("| a | b | c |\n|:--|:-:|--:|\n| 1 | 2 | 3 |");36 expect(html).toBe('<div class="table"><table><thead><tr><th>a</th><th class="center">b</th><th class="right">c</th></tr></thead><tbody>\n<tr><td>1</td><td class="center">2</td><td class="right">3</td></tr>\n</tbody></table></div>');37 });3839 test("an asterisk between two word characters is literal, around words it is emphasis, in code it is untouched", () => {40 expect(render("3*n*(n+1) and *\"2*20^n + 4*8^n\"* and `a*b*c`")).toBe('<p>3*n*(n+1) and <em>"2*20^n + 4*8^n"</em> and <code>a*b*c</code></p>');41 });4243 test("every link and bare url goes through the resolver", () => {44 expect(render("[a](x.md) and https://oeis.org/A1", { link })).toBe('<p><a href="/L/x.md">a</a> and <a href="https://oeis.org/A1">https://oeis.org/A1</a></p>');45 });4647 test("raw html is shown as text", () => {48 expect(render("n<m and <b>x</b>")).toBe("<p>n<m and <b>x</b></p>");49 });5051 test("lists are tight, so a dated claim line starts its item", () => {52 expect(render("- 2026-09-19 [Proved] one\n- two\n\n1. a\n2. b")).toBe("<ul>\n<li>2026-09-19 [Proved] one</li>\n<li>two</li>\n</ul>\n<ol>\n<li>a</li>\n<li>b</li>\n</ol>");53 });5455 test("inline drops the paragraph", () => {56 expect(inline("a **b** [c](d)", { link })).toBe('a <strong>b</strong> <a href="/L/d">c</a>');57 });5859 test("sheet splits the title, the lead and the body", () => {60 const doc = sheet("# Hi *there*\n\nLead with [a](b).\n\n## Next\n\n- x", link);61 expect(doc).toEqual({ title: "Hi there", lead: 'Lead with <a href="/L/b">a</a>.', text: "Lead with a.", body: '<h2 id="next">Next</h2>\n<ul>\n<li>x</li>\n</ul>' });62 expect(sheet("Just prose.").title).toBe("");63 });6465 test("front matter is key colon value lines", () => {66 expect(front("---\ntitle: T\nlead: L\n---\nbody")).toEqual({ data: { title: "T", lead: "L" }, body: "body" });67 expect(front("body").data).toEqual({});68 });69});