clarc/src/terminal.test.ts

100 lines
3.4 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { createTerminal, disposeTerminal, serializeAsHTML } from "./terminal";
// Helper to write to terminal and wait for completion
function writeSync(
term: ReturnType<typeof createTerminal>,
data: string,
): Promise<void> {
return new Promise((resolve) => {
term.terminal.write(data, resolve);
});
}
describe("terminal", () => {
test("creates terminal with correct dimensions", () => {
const term = createTerminal(80, 24);
expect(term.terminal.cols).toBe(80);
expect(term.terminal.rows).toBe(24);
disposeTerminal(term);
});
test("writes data to terminal buffer", async () => {
const term = createTerminal(80, 24);
await writeSync(term, "Hello, world!");
const html = serializeAsHTML(term);
expect(html).toContain("Hello, world!");
disposeTerminal(term);
});
test("handles ANSI cursor movement", async () => {
const term = createTerminal(80, 24);
await writeSync(term, "AAA\x1b[3D"); // Write AAA, move cursor back 3
await writeSync(term, "BBB"); // Overwrite with BBB
const html = serializeAsHTML(term);
expect(html).toContain("BBB");
expect(html).not.toContain("AAA");
disposeTerminal(term);
});
test("handles carriage return correctly", async () => {
const term = createTerminal(80, 24);
await writeSync(term, "Old text\rNew"); // CR should move to col 0
const html = serializeAsHTML(term);
expect(html).toContain("New");
expect(html).not.toContain("Old");
disposeTerminal(term);
});
test("handles incomplete ANSI sequences across writes", async () => {
const term = createTerminal(80, 24);
await writeSync(term, "Hello\x1b["); // Incomplete CSI
await writeSync(term, "31mRed\x1b[0m"); // Complete it
const html = serializeAsHTML(term);
expect(html).toContain("Red");
// Check for red styling (color may vary in format)
expect(html).toMatch(/color|red|#[a-f0-9]{6}/i);
disposeTerminal(term);
});
test("handles screen clearing", async () => {
const term = createTerminal(80, 24);
await writeSync(term, "First line\n");
await writeSync(term, "Second line");
await writeSync(term, "\x1b[2J"); // Clear screen
await writeSync(term, "\x1b[H"); // Cursor home
await writeSync(term, "After clear");
const html = serializeAsHTML(term);
expect(html).toContain("After clear");
// "First line" should be in scrollback but not current screen
disposeTerminal(term);
});
test("handles cursor position command", async () => {
const term = createTerminal(80, 24);
await writeSync(term, "\x1b[5;10H"); // Move cursor to row 5, col 10
await writeSync(term, "Positioned");
const html = serializeAsHTML(term);
expect(html).toContain("Positioned");
// CSI H should not leak through as literal characters
expect(html).not.toMatch(/^[HJ]$/m);
disposeTerminal(term);
});
test("handles resize", () => {
const term = createTerminal(80, 24);
term.terminal.resize(120, 40);
expect(term.terminal.cols).toBe(120);
expect(term.terminal.rows).toBe(40);
disposeTerminal(term);
});
test("serialize returns valid HTML structure", async () => {
const term = createTerminal(80, 24);
await writeSync(term, "Test content");
const html = serializeAsHTML(term);
// Should be valid HTML (has span structure from xterm serialize addon)
expect(html).toMatch(/<span|<div|Test content/);
disposeTerminal(term);
});
});