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");
});
});
});