import { assertEquals } from "@std/assert"; import { withDom } from "@berry/util/dom.ts"; import { code } from "./code.tsx"; import type { HighlightRequest } from "./highlight-worker.ts"; Deno.test("code highlights once on entering view and releases unmounted blocks", async (t) => { const observed = new Set(); const requests: HighlightRequest[] = []; let workers = 0; let intersect: (target: Element, isIntersecting: boolean) => void; class Visibility { constructor(callback: IntersectionObserverCallback) { intersect = (target, isIntersecting) => { callback( [{ target, isIntersecting } as IntersectionObserverEntry], this as unknown as IntersectionObserver, ); }; } observe(target: Element) { observed.add(target); } unobserve(target: Element) { observed.delete(target); } } class Highlighter { onmessage?: (event: { data: { id: number; html: string } }) => void; constructor() { workers++; } postMessage(request: HighlightRequest) { requests.push(request); queueMicrotask(() => this.onmessage?.({ data: { id: request.id, html: reply(request) }, }) ); } } let reply = (_: HighlightRequest) => "const x = 1;"; const globals = { IntersectionObserver: Visibility, Worker: Highlighter }; await withDom(async (window) => { const block = code("const x = 1;\n", { language: "ts" }); const container = document.createElement("div"); container.append(block); await t.step("detached and offscreen blocks stay plain without starting a worker", async () => { assertEquals(observed.size, 0); assertEquals(workers, 0); document.body.append(container); await window.happyDOM.waitUntilComplete(); assertEquals(observed.has(block), true); intersect(block, false); assertEquals(workers, 0); assertEquals(block.querySelector(".code-text")?.textContent, "const x = 1;"); }); await t.step("entering view highlights once, preserving the source and gutter", async () => { intersect(block, true); await window.happyDOM.waitUntilComplete(); assertEquals(workers, 1); assertEquals(requests.map(({ language, source }) => ({ language, source })), [ { language: "typescript", source: "const x = 1;" }, ]); assertEquals(block.querySelector("a-k")?.textContent, "const"); assertEquals(block.querySelector(".code-text")?.textContent, "const x = 1;"); assertEquals(observed.has(block), false); intersect(block, true); container.remove(); document.body.append(container); await window.happyDOM.waitUntilComplete(); assertEquals(requests.length, 1); assertEquals(observed.has(block), false); }); await t.step("blocks without a grammar do not request highlighting", async () => { container.replaceChildren(code("plain text")); await window.happyDOM.waitUntilComplete(); assertEquals(observed.size, 0); assertEquals(requests.length, 1); }); await t.step("highlighted lines keep the newlines that copying reads", async () => { reply = (request) => request.source; const multiline = code("const x = 1;\nconst y = 2;", { language: "ts" }); container.replaceChildren(multiline); await window.happyDOM.waitUntilComplete(); intersect(multiline, true); await window.happyDOM.waitUntilComplete(); const copied = [...multiline.querySelectorAll(".code-text")].map((line) => line.textContent); assertEquals(copied.join(""), "const x = 1;\nconst y = 2;"); }); }, { globals }); });