import { assert, assertEquals } from "@std/assert"; import { existsSync } from "node:fs"; import { withDom } from "@berry/util/dom.ts"; import { Grid } from "./grid.ts"; import { Ghostty } from "./ghostty.ts"; Deno.test("rescaling the grid repaints cached cells without replacing selected text", async () => { await withDom((window) => { const rows = document.createElement("div"); const canvas = document.createElement("canvas"); const painted: number[][] = []; Object.defineProperty(canvas, "getContext", { value: () => ({ clearRect: () => painted.splice(0), fillRect: (...rect: number[]) => painted.push(rect), }), }); const grid = new Grid(rows, canvas); const metrics = { width: 200, height: 100, ratio: 1, cell: { width: 8.25, height: 18.5 } }; grid.fit(metrics); const bg = [61, 34, 74] as const; grid.update({ cols: 20, rows: 2, cursor: null, changed: new Map([ [0, [{ text: "e\u0301", bg }, { text: "🦀", bg, wide: true }, { text: "x", bg }]], [1, [{ text: "next", bg }]], ]), }); assertEquals(rows.firstChild!.textContent, "e\u0301🦀x"); const range = window.document.createRange(); const text = rows.firstChild!.firstChild!.firstChild!; range.selectNodeContents(text as unknown as Parameters[0]); window.getSelection().addRange(range); for (const ratio of [1, 1.25, 1.5, 2, 0.8]) { grid.fit({ ...metrics, ratio }); assertEquals(window.getSelection().toString(), "e\u0301"); assertEquals(rows.firstChild!.firstChild!.firstChild, text); assertEquals(painted.length, 4); const [first, wide, last, next] = painted; assertEquals(first[0], 0); assertEquals(first[0] + first[2], wide[0]); assertEquals(wide[0] + wide[2], last[0]); assertEquals(first[1] + first[3], next[1]); assertEquals(last[0] + last[2], next[2]); assert(wide[2] > first[2]); assert(painted.flat().every(Number.isInteger)); } grid.update({ cols: 20, rows: 2, cursor: null, changed: new Map() }); assertEquals(painted.length, 4); assertEquals(rows.firstChild!.firstChild!.firstChild, text); }); }); Deno.test({ name: "screen-filling backgrounds include blank cells through the screen edges", ignore: !existsSync(new URL("./ghostty-vt.wasm", import.meta.url)), async fn() { const ghostty = await Ghostty.load( await Deno.readFile(new URL("./ghostty-vt.wasm", import.meta.url)), ); const terminal = ghostty.terminal(20, 4); try { terminal.write(new TextEncoder().encode( "\x1b[?25l\x1b[48;2;61;34;74m" + " ".repeat(80) + "\x1b[Hé🦀x", )); const frame = terminal.frame(); await withDom(() => { const canvas = document.createElement("canvas"); const painted: number[][] = []; Object.defineProperty(canvas, "getContext", { value: () => ({ clearRect: () => painted.splice(0), fillRect: (...rect: number[]) => painted.push(rect), }), }); const grid = new Grid(document.createElement("div"), canvas); grid.fit({ width: 165, height: 74, ratio: 1.25, cell: { width: 8.25, height: 18.5 } }); grid.update(frame); assertEquals( painted.reduce((area, [, , width, height]) => area + width * height, 0), canvas.width * canvas.height, ); assertEquals(Math.max(...painted.map(([x, , width]) => x + width)), canvas.width); assertEquals(Math.max(...painted.map(([, y, , height]) => y + height)), canvas.height); }); } finally { terminal.free(); } }, });