import { test, expect } from "bun:test"; import { htmlToSx } from "./a31_sxdoc_parse.ts"; import { SX_DOC_VERSION } from "./a31_sxdoc.ts"; test("returns an empty document for empty input", () => { const doc = htmlToSx(""); expect(doc.v).toBe(SX_DOC_VERSION); expect(doc.blocks).toEqual([]); }); test("parses a simple paragraph", () => { const doc = htmlToSx("
Hello world
"); expect(doc.blocks).toHaveLength(1); expect(doc.blocks[0]).toEqual({ t: "p", c: [{ t: "text", v: "Hello world" }], }); }); test("parses headings with correct level for h1-h6", () => { for (const level of [1, 2, 3, 4, 5, 6] as const) { const doc = htmlToSx(`"); expect(doc.blocks).toEqual([{ t: "quote", c: [{ t: "p", c: [{ t: "text", v: "quoted" }] }], }]); }); test("parses blockquote with loose text wraps it in a paragraph", () => { const doc = htmlToSx("quoted
loose"); expect(doc.blocks[0]).toEqual({ t: "quote", c: [{ t: "p", c: [{ t: "text", v: "loose" }] }], }); }); test("parses pre>code with language hint", () => { const doc = htmlToSx(`
const x = 1;`);
expect(doc.blocks[0]).toEqual({
t: "code", lang: "ts", src: "const x = 1;",
});
});
test("parses pre without inner code element", () => {
const doc = htmlToSx("raw text"); expect(doc.blocks[0]).toEqual({ t: "code", lang: "", src: "raw text", }); }); test("preserves encoded entities in code blocks", () => { const doc = htmlToSx(`
<p>`);
expect(doc.blocks[0]).toEqual({
t: "code", lang: "", src: "",
});
});
test("parses img with src and alt", () => {
const doc = htmlToSx(`
`);
expect(doc.blocks[0]).toEqual({ t: "img", src: "/x.png", alt: "x icon" });
});
test("parses img with width and height attributes", () => {
const doc = htmlToSx(`
`);
expect(doc.blocks[0]).toEqual({ t: "img", src: "/a.jpg", w: 200, h: 100 });
});
test("skips img with empty src", () => {
const doc = htmlToSx(``);
expect(doc.blocks).toEqual([]);
});
test("parses figure with figcaption", () => {
const doc = htmlToSx(`

bold and ital
"); expect(doc.blocks[0]).toEqual({ t: "p", c: [ { t: "text", v: "bold", m: ["b"] }, { t: "text", v: " and " }, { t: "text", v: "ital", m: ["i"] }, ], }); }); test("composes nested marks into a single mark array", () => { const doc = htmlToSx("both
"); expect(doc.blocks[0]).toEqual({ t: "p", c: [{ t: "text", v: "both", m: ["b", "i"] }], }); }); test("dedupes repeated marks across nested wrappers", () => { const doc = htmlToSx("x
"); const para = doc.blocks[0] as { c: Array<{ m?: string[] }> }; expect(para.c[0].m).toEqual(["b"]); }); test("treatsa
b
before middle after
`); expect(doc.blocks[0]).toEqual({ t: "p", c: [ { t: "text", v: "before " }, { t: "text", v: "middle" }, { t: "text", v: " after" }, ], }); }); test("parses a standalone shortcode out of plain text", () => { const doc = htmlToSx("[[sx:event-count]]
"); expect(doc.blocks).toEqual([ { t: "shortcode", name: "event-count", args: {} }, ]); }); test("parses a shortcode with quoted and bare args", () => { const doc = htmlToSx(`[[sx:list tag="blog" limit=5]]
`); expect(doc.blocks).toEqual([ { t: "shortcode", name: "list", args: { tag: "blog", limit: "5" } }, ]); }); test("lifts a shortcode out of a mixed paragraph", () => { const doc = htmlToSx("before [[sx:x]] after
"); expect(doc.blocks).toEqual([ { t: "p", c: [{ t: "text", v: "before " }] }, { t: "shortcode", name: "x", args: {} }, { t: "p", c: [{ t: "text", v: " after" }] }, ]); }); test("recurses into div/section/article containers", () => { const doc = htmlToSx("one
two
| x |