import { assert, assertEquals } from "@std/assert"; import { withDom } from "@berry/util/dom.ts"; import { TERMINAL_PALETTE } from "@berry/util/palette.ts"; import { ansiOutput } from "./ansi-view.ts"; Deno.test("coloured output appends in place and resets for replacement snapshots", async (t) => { await withDom(async () => { const element = document.createElement("pre"); const show = ansiOutput(element); const colour = (value: string) => { const span = document.createElement("span"); span.style.color = value; return span.style.color; }; await t.step("colour arrives as soon as a split escape is completed", () => { assertEquals(element.hidden, true); show("start\x1b[38;2;12;"); assertEquals(element.textContent, "start"); const first = element.firstChild; show("start\x1b[38;2;12;34;56mred"); assertEquals(element.textContent, "startred"); assertEquals(element.firstChild, first); assertEquals( (element.lastElementChild as HTMLElement).style.color, colour("rgb(12, 34, 56)"), ); const red = element.lastChild!; const text = red.firstChild; show("start\x1b[38;2;12;34;56mredder\x1b[0mplain"); assertEquals(element.textContent, "startredderplain"); assertEquals(element.childNodes[1], red); assertEquals(red.firstChild, text); assertEquals(red.textContent, "redder"); assertEquals((element.lastElementChild as HTMLElement).style.color, ""); }); await t.step("a truncated or replaced result resets text, style and pending controls", () => { show("\x1b[31mold\x1b]unfinished"); show("replacement"); assertEquals(element.textContent, "replacement"); assertEquals((element.firstElementChild as HTMLElement).style.color, ""); show(""); assertEquals(element.hidden, true); assertEquals(element.childNodes.length, 0); show("new\x1b[32mgreen"); assertEquals(element.textContent, "newgreen"); assertEquals( (element.lastElementChild as HTMLElement).style.color, colour(TERMINAL_PALETTE[2]), ); }); await t.step("output stays literal text rather than markup", () => { show('\x1b[31mred'); assertEquals(element.textContent, 'red'); assertEquals(element.querySelector("script, a"), null); }); await t.step("large outputs bound individual text-node edits without splitting emoji", () => { show(""); const text = "x".repeat(16383) + "😀" + "y".repeat(50000); show(text); assertEquals(element.textContent, text); for (const span of element.children) { assert(span.textContent!.length < 20000); assert(!/[\ud800-\udbff]$/.test(span.textContent!)); assert(!/^[\udc00-\udfff]/.test(span.textContent!)); } const first = element.firstChild; show(text + "end"); assertEquals(element.firstChild, first); assertEquals(element.textContent, text + "end"); }); }); });