[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay bomb.sh/docs/tty
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266/** * Tests for specs/terminfo-spec.md: capability struct, baseline and * evidence model, terminfo binary parsing, and the detectTerminal API. * Probe response recognition is tested in input.test.ts. */
import { detectTerminal } from "../terminfo.ts";import { CLAYTERM_16, CLAYTERM_DIRECT, CLAYTERM_TC, XTERM_256COLOR,} from "./fixtures.ts";import { describe, expect, it } from "./suite.ts";
const decode = (b: Uint8Array) => new TextDecoder().decode(b);
const BASELINE = { colors: 256, trueColor: false, bce: true, autoMargin: true, xenl: true, altScreen: true, styledUnderline: false, pointerShape: false,};
describe("known OSC 22 pointer shape support (terminfo-spec §6.1)", () => { it("grants for ghostty by TERM and by TERM_PROGRAM", async () => { let byTerm = await detectTerminal({ env: { TERM: "xterm-ghostty" } }); expect(byTerm.capabilities.pointerShape).toBe(true); let byProgram = await detectTerminal({ env: { TERM_PROGRAM: "ghostty" } }); expect(byProgram.capabilities.pointerShape).toBe(true); });
it("grants for kitty by TERM and by KITTY_WINDOW_ID", async () => { let byTerm = await detectTerminal({ env: { TERM: "xterm-kitty" } }); expect(byTerm.capabilities.pointerShape).toBe(true); let byWindow = await detectTerminal({ env: { KITTY_WINDOW_ID: "1" } }); expect(byWindow.capabilities.pointerShape).toBe(true); });
it("grants for foot by TERM prefix", async () => { let d = await detectTerminal({ env: { TERM: "foot" } }); expect(d.capabilities.pointerShape).toBe(true); });
it("grants for xterm at the version floor and above", async () => { let old = await detectTerminal({ env: { XTERM_VERSION: "xterm(366)" } }); expect(old.capabilities.pointerShape).toBe(false); let floor = await detectTerminal({ env: { XTERM_VERSION: "xterm(367)" } }); expect(floor.capabilities.pointerShape).toBe(true); let newer = await detectTerminal({ env: { XTERM_VERSION: "xterm(388)" } }); expect(newer.capabilities.pointerShape).toBe(true); });
it("does not grant for terminals that rewrite their identity", async () => { for ( let TERM of [ "tmux-256color", "screen-256color", "xterm-256color", ] ) { let d = await detectTerminal({ env: { TERM } }); expect(d.capabilities.pointerShape).toBe(false); } let vscode = await detectTerminal({ env: { TERM_PROGRAM: "vscode" } }); expect(vscode.capabilities.pointerShape).toBe(false); });});
describe("baseline", () => { it("initializes to the baseline with no evidence", async () => { let d = await detectTerminal({ env: {} }); expect(d.capabilities).toEqual(BASELINE); });
it("does not assume truecolor at baseline", async () => { let d = await detectTerminal({ env: {} }); expect(d.capabilities.trueColor).toBe(false); });});
describe("terminfo parsing", () => { it("parses a real xterm-256color entry (legacy format)", async () => { let d = await detectTerminal({ env: {}, entry: XTERM_256COLOR }); let caps = d.capabilities; expect(caps.colors).toBe(256); expect(caps.trueColor).toBe(false); expect(caps.bce).toBe(true); expect(caps.autoMargin).toBe(true); expect(caps.altScreen).toBe(true); });
it("reads Tc / Su / Smulx from the extended capability table", async () => { let d = await detectTerminal({ env: {}, entry: CLAYTERM_TC }); let caps = d.capabilities; expect(caps.trueColor).toBe(true); expect(caps.styledUnderline).toBe(true); expect(caps.colors).toBe(256); expect(caps.bce).toBe(true); });
it("parses the extended number format (magic 01036)", async () => { let d = await detectTerminal({ env: {}, entry: CLAYTERM_DIRECT }); let caps = d.capabilities; expect(caps.colors).toBe(0x1000000); expect(caps.trueColor).toBe(true); expect(caps.altScreen).toBe(true); expect(caps.bce).toBe(false); });
it("downgrades below baseline on terminfo evidence", async () => { let d = await detectTerminal({ env: {}, entry: CLAYTERM_16 }); let caps = d.capabilities; expect(caps.colors).toBe(16); expect(caps.altScreen).toBe(false); expect(caps.bce).toBe(false); expect(caps.trueColor).toBe(false); });
it("keeps the baseline untouched on truncated input", async () => { let d = await detectTerminal({ env: {}, entry: XTERM_256COLOR.slice(0, 30), }); expect(d.capabilities).toEqual(BASELINE); });
it("keeps the baseline untouched on garbage input", async () => { let d = await detectTerminal({ env: {}, entry: new Uint8Array(128) }); expect(d.capabilities).toEqual(BASELINE); });
it("rejects a terminfo entry larger than 32768 bytes", async () => { await expect( detectTerminal({ env: {}, entry: new Uint8Array(32769) }), ).rejects.toThrow(RangeError); });});
describe("environment evidence", () => { it("grants truecolor from COLORTERM=truecolor", async () => { let d = await detectTerminal({ env: { COLORTERM: "truecolor" }, entry: XTERM_256COLOR, }); expect(d.capabilities.trueColor).toBe(true); });
it("grants truecolor from COLORTERM=24bit", async () => { let d = await detectTerminal({ env: { COLORTERM: "24bit" } }); expect(d.capabilities.trueColor).toBe(true); });
it("ignores other COLORTERM values", async () => { let d = await detectTerminal({ env: { COLORTERM: "yes" } }); expect(d.capabilities.trueColor).toBe(false); });});
describe("filesystem lookup", () => { async function fixtureDb(layout: "hex" | "letter"): Promise<string> { let dir = await Deno.makeTempDir(); let sub = layout === "hex" ? "63" : "c"; await Deno.mkdir(`${dir}/${sub}`, { recursive: true }); await Deno.writeFile(`${dir}/${sub}/clayterm-tc`, CLAYTERM_TC); return dir; }
it("finds an entry under $TERMINFO (hex directory layout)", async () => { let db = await fixtureDb("hex"); let d = await detectTerminal({ env: { TERM: "clayterm-tc", TERMINFO: db }, }); expect(d.capabilities.trueColor).toBe(true); });
it("finds an entry under $TERMINFO (first-letter layout)", async () => { let db = await fixtureDb("letter"); let d = await detectTerminal({ env: { TERM: "clayterm-tc", TERMINFO: db }, }); expect(d.capabilities.trueColor).toBe(true); });
it("finds an entry under $HOME/.terminfo", async () => { let home = await Deno.makeTempDir(); await Deno.mkdir(`${home}/.terminfo/63`, { recursive: true }); await Deno.writeFile(`${home}/.terminfo/63/clayterm-tc`, CLAYTERM_TC); let d = await detectTerminal({ env: { TERM: "clayterm-tc", HOME: home } }); expect(d.capabilities.trueColor).toBe(true); });
it("resolves to the baseline when no entry is found", async () => { let db = await Deno.makeTempDir(); let d = await detectTerminal({ env: { TERM: "does-not-exist", TERMINFO: db }, }); expect(d.capabilities).toEqual(BASELINE); });
it("skips an oversized entry and resolves to the baseline", async () => { let db = await Deno.makeTempDir(); await Deno.mkdir(`${db}/63`, { recursive: true }); let oversized = new Uint8Array(32769); oversized.set(CLAYTERM_TC); await Deno.writeFile(`${db}/63/clayterm-tc`, oversized); let d = await detectTerminal({ env: { TERM: "clayterm-tc", TERMINFO: db }, }); expect(d.capabilities).toEqual(BASELINE); });
it("rejects terminal names with path separators", async () => { let db = await Deno.makeTempDir(); let d = await detectTerminal({ env: { TERM: "../../etc/passwd", TERMINFO: db }, }); expect(d.capabilities).toEqual(BASELINE); });});
const PROBE = "\x1b]10;?\x07" + "\x1b]11;?\x07" + "\x1b]12;?\x07" + "\x1b]21;foreground=?;background=?;cursor=?\x1b\\" + "\x1b]22;?__current__\x1b\\" + "\x1bP+q524742;5463\x1b\\" + "\x1b[?2026$p" + "\x1b[?u" + "\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\" + "\x1b[c";
describe("probe", () => { it("returns the query batch fenced by DA1", async () => { let d = await detectTerminal({ env: {} }); expect(decode(d.probe)).toBe(PROBE); });
it("probe is a Uint8Array", async () => { let d = await detectTerminal({ env: {} }); expect(d.probe).toBeInstanceOf(Uint8Array); });});
describe("TerminalInfo shape", () => { it("is frozen", async () => { let d = await detectTerminal({ env: {} }); expect(Object.isFrozen(d)).toBe(true); expect(Object.isFrozen(d.capabilities)).toBe(true); });
it("keys is a Uint8Array (empty when no terminfo file found)", async () => { let d = await detectTerminal({ env: {} }); expect(d.keys).toBeInstanceOf(Uint8Array); });
it("keys holds the parsed terminfo bytes when an entry is found", async () => { let d = await detectTerminal({ env: {}, entry: CLAYTERM_TC }); expect(d.keys.byteLength).toBe(CLAYTERM_TC.byteLength); });});