import { describe, expect, it } from "vitest"; import { parseMarkdown, roundTripMarkdown } from "@/components/MemoEditor/Editor/markdownCodec"; function blockTypes(markdown: string): string[] { return (parseMarkdown(markdown).content ?? []).map((node) => node.type ?? ""); } describe("PreservedBlock", () => { it("captures a table as a single preservedBlock node", () => { const md = "| a | b |\n| --- | --- |\n| 1 | 2 |"; expect(blockTypes(md)).toEqual(["preservedBlock"]); }); it("captures block math as preservedBlock", () => { const md = "$$\nx^2\n$$"; expect(blockTypes(md)).toEqual(["preservedBlock"]); }); it("captures block HTML as preservedBlock", () => { const md = "
\nhello\n
"; expect(blockTypes(md)).toEqual(["preservedBlock"]); }); }); describe("PreservedInline", () => { it("keeps inline math verbatim — underscores must not get escaped", () => { expect(roundTripMarkdown("index $x_1$ here").trim()).toBe("index $x_1$ here"); }); it("keeps inline HTML tags literal instead of converting them to marks", () => { expect(roundTripMarkdown("an emphasis tag").trim()).toBe("an emphasis tag"); }); it("does not swallow autolinks", () => { const doc = parseMarkdown("see now"); const para = doc.content?.[0]; const hasLinkMark = (para?.content ?? []).some((n) => (n.marks ?? []).some((m) => m.type === "link")); expect(hasLinkMark).toBe(true); }); }); describe("preserved-syntax edge cases", () => { it.each([ "I paid $$ for this", "tip: 20%, cost: $$, worth it", "that costs $$$ these days", "$$$$", "$a$$b$", ])("mid-line $$ does not split paragraphs: %s", (s) => { expect(roundTripMarkdown(s).trim()).toBe(s); }); it("block math followed directly by text normalizes once to blank-line separation, then is stable", () => { // Byte-identity is impossible here regardless of how the tokenizer treats // the trailing newline: the math block and the following paragraph are // sibling block nodes, and the Document serializer joins siblings with // \n\n. The single \n separator is normalized to a blank line exactly // once; after that the output is a fixed point. const out = roundTripMarkdown("$$\nx\n$$\nnext line"); expect(out.trim()).toBe("$$\nx\n$$\n\nnext line"); // known one-time normalization expect(roundTripMarkdown(out)).toBe(out); // stable afterward }); it("preserves inline HTML comments", () => { expect(roundTripMarkdown("text more").trim()).toBe("text more"); }); it("preserves block HTML comments", () => { expect(roundTripMarkdown("").trim()).toBe(""); }); it("handles > inside quoted attributes", () => { expect(roundTripMarkdown('a hm b').trim()).toBe('a hm b'); }); it("does not catastrophically backtrack on unterminated tags full of quotes", () => { const hostile = `a