import { describe, expect, test } from "bun:test"; import { renderTelegramHtml } from "./render.js"; // Expected values are derived from Telegram's documented Rich HTML vocabulary // (https://core.telegram.org/bots/api#rich-html-style), not from the renderer's // output. Notable doc-grounded choices: // - ``/``/`` are listed as supported alongside ``/``/ // ``, so the serializer's defaults need no remapping. // - Character references are numeric (`&`) because Telegram supports all // numeric references but only 13 named ones; numeric is unambiguously safe. // - `` is a supported tag, but only as a standalone media block, so // an image-only paragraph is unwrapped from its `

`. describe("renderTelegramHtml", () => { test("returns undefined for empty or whitespace-only input", () => { expect(renderTelegramHtml("")).toBeUndefined(); expect(renderTelegramHtml(" \n\t ")).toBeUndefined(); }); test("wraps a plain paragraph", () => { expect(renderTelegramHtml("Hello world")).toBe("

Hello world

"); }); test("maps headings to h1-h6", () => { expect(renderTelegramHtml("# A")).toBe("

A

"); expect(renderTelegramHtml("###### F")).toBe("
F
"); }); test("maps inline emphasis to Telegram's supported tags", () => { expect(renderTelegramHtml("**b** _i_ ~~d~~ `c`")).toBe( "

b i d c

", ); }); test("renders links with a numeric-escaped href", () => { expect(renderTelegramHtml("[text](https://x.test/?a=1&b=2)")).toBe( '

text

', ); }); test("renders fenced code with a language class and escaped body", () => { expect(renderTelegramHtml("```ts\nconst a = 1 < 2;\n```")).toBe( '
const a = 1 < 2;
', ); }); test("renders a no-language code block as a bare pre", () => { expect(renderTelegramHtml("```\nplain\n```")).toBe("
plain
"); }); test("renders bullet and ordered lists", () => { expect(renderTelegramHtml("- one\n- two")).toBe( "
  • one
  • two
", ); expect(renderTelegramHtml("3. a\n4. b")).toBe( '
  1. a
  2. b
', ); }); test("renders GFM task lists with bare checkbox inputs", () => { // Telegram's documented form is `` with no // `disabled` attribute and no list classes. expect(renderTelegramHtml("- [x] done\n- [ ] todo")).toBe( '
  • done
  • ' + '
  • todo
', ); }); test("renders a nested list inside a list item", () => { expect(renderTelegramHtml("- a\n - b")).toBe( "
  • a
    • b
", ); }); test("renders a GFM table without thead/tbody wrappers", () => { const html = renderTelegramHtml("| H1 | H2 |\n| :- | -: |\n| a | b |"); expect(html).toBe( "" + '' + '' + "
H1H2
ab
", ); }); test("renders blockquotes, preserving paragraph structure", () => { expect(renderTelegramHtml("> quoted")).toBe( "

quoted

", ); expect(renderTelegramHtml("> line one\n>\n> line two")).toBe( "

line one

line two

", ); }); test("renders a thematic break", () => { expect(renderTelegramHtml("---")).toBe("
"); }); test("renders a standalone image as a top-level media block", () => { // Telegram accepts `` only as a standalone media block, never nested // in a `

`, so the serializer's `

` wrapper is removed. expect(renderTelegramHtml("![alt text](https://x.test/i.png)")).toBe( 'alt text', ); }); test("keeps an image inline when it shares a paragraph with text", () => { // A paragraph mixing prose and an image keeps its `

` so the surrounding // text is not orphaned; only image-only paragraphs are unwrapped. expect(renderTelegramHtml("see ![a](https://x.test/i.png) here")).toBe( '

see a here

', ); }); test("preserves significant whitespace between inline elements in a list item", () => { // The space between adjacent inline nodes is content, not layout, so it must // survive: `Status: ready`, never `Status:ready`. expect(renderTelegramHtml("- **Status:** _ready_")).toBe( "
  • Status: ready
", ); }); // Fidelity: characters that Telegram's Rich *Markdown* dialect reinterprets // ($ math, == highlight, || spoiler, < HTML) must survive as literal text in // HTML mode rather than triggering those extensions. test("keeps Telegram Rich-Markdown-only syntax literal", () => { expect(renderTelegramHtml("Pay $100 to $200")).toBe( "

Pay $100 to $200

", ); expect(renderTelegramHtml("a ==hi== b")).toBe("

a ==hi== b

"); expect(renderTelegramHtml("a ||spoiler|| b")).toBe( "

a ||spoiler|| b

", ); }); test("escapes raw HTML so it renders verbatim", () => { expect(renderTelegramHtml("a not bold z")).toBe( "

a <b>not bold</b> z

", ); }); test("emits numeric references for the characters HTML must escape", () => { // Only `&` and `<` require escaping in element text; `>`, `"`, and `'` are // left literal. Escaped characters use numeric references. expect(renderTelegramHtml(`& < > " '`)).toBe(`

& < > " '

`); }); });