co-mono/packages/tui/test/virtual-terminal.test.ts
Mario Zechner 66cefb236e Massive refactor of API
- Switch to function based API
- Anthropic SDK style async generator
- Fully typed with escape hatches for custom models
2025-09-02 23:59:36 +02:00

164 lines
4.5 KiB
TypeScript

import assert from "node:assert";
import { describe, test } from "node:test";
import { VirtualTerminal } from "./virtual-terminal.js";
describe("VirtualTerminal", () => {
test("writes and reads simple text", async () => {
const terminal = new VirtualTerminal(80, 24);
terminal.write("Hello, World!");
// Wait for write to process
const output = await terminal.flushAndGetViewport();
assert.strictEqual(output[0], "Hello, World!");
assert.strictEqual(output[1], "");
});
test("handles newlines correctly", async () => {
const terminal = new VirtualTerminal(80, 24);
terminal.write("Line 1\r\nLine 2\r\nLine 3");
const output = await terminal.flushAndGetViewport();
assert.strictEqual(output[0], "Line 1");
assert.strictEqual(output[1], "Line 2");
assert.strictEqual(output[2], "Line 3");
});
test("handles ANSI cursor movement", async () => {
const terminal = new VirtualTerminal(80, 24);
// Write text with proper newlines, move cursor up, overwrite
terminal.write("First line\r\nSecond line");
terminal.write("\x1b[1A"); // Move up 1 line
terminal.write("\rOverwritten");
const output = await terminal.flushAndGetViewport();
assert.strictEqual(output[0], "Overwritten");
assert.strictEqual(output[1], "Second line");
});
test("handles clear line escape sequence", async () => {
const terminal = new VirtualTerminal(80, 24);
terminal.write("This will be cleared");
terminal.write("\r\x1b[2K"); // Clear line
terminal.write("New text");
const output = await terminal.flushAndGetViewport();
assert.strictEqual(output[0], "New text");
});
test("tracks cursor position", async () => {
const terminal = new VirtualTerminal(80, 24);
terminal.write("Hello");
await terminal.flush();
const cursor = terminal.getCursorPosition();
assert.strictEqual(cursor.x, 5); // After "Hello"
assert.strictEqual(cursor.y, 0); // First line
terminal.write("\r\nWorld"); // Use CR+LF for proper newline
await terminal.flush();
const cursor2 = terminal.getCursorPosition();
assert.strictEqual(cursor2.x, 5); // After "World"
assert.strictEqual(cursor2.y, 1); // Second line
});
test("handles viewport overflow with scrolling", async () => {
const terminal = new VirtualTerminal(80, 10); // Small viewport
// Write more lines than viewport can hold
for (let i = 1; i <= 15; i++) {
terminal.write(`Line ${i}\r\n`);
}
const viewport = await terminal.flushAndGetViewport();
const scrollBuffer = terminal.getScrollBuffer();
// Viewport should show lines 7-15 plus empty line (because viewport starts after scrolling)
assert.strictEqual(viewport.length, 10);
assert.strictEqual(viewport[0], "Line 7");
assert.strictEqual(viewport[8], "Line 15");
assert.strictEqual(viewport[9], ""); // Last line is empty after the final \r\n
// Scroll buffer should have all lines
assert.ok(scrollBuffer.length >= 15);
// Check specific lines exist in the buffer
const hasLine1 = scrollBuffer.some((line) => line === "Line 1");
const hasLine15 = scrollBuffer.some((line) => line === "Line 15");
assert.ok(hasLine1, "Buffer should contain 'Line 1'");
assert.ok(hasLine15, "Buffer should contain 'Line 15'");
});
test("resize updates dimensions", async () => {
const terminal = new VirtualTerminal(80, 24);
assert.strictEqual(terminal.columns, 80);
assert.strictEqual(terminal.rows, 24);
terminal.resize(100, 30);
assert.strictEqual(terminal.columns, 100);
assert.strictEqual(terminal.rows, 30);
});
test("reset clears terminal completely", async () => {
const terminal = new VirtualTerminal(80, 24);
terminal.write("Some text\r\nMore text");
let output = await terminal.flushAndGetViewport();
assert.strictEqual(output[0], "Some text");
assert.strictEqual(output[1], "More text");
terminal.reset();
output = await terminal.flushAndGetViewport();
assert.strictEqual(output[0], "");
assert.strictEqual(output[1], "");
});
test("sendInput triggers handler", async () => {
const terminal = new VirtualTerminal(80, 24);
let received = "";
terminal.start(
(data) => {
received = data;
},
() => {},
);
terminal.sendInput("a");
assert.strictEqual(received, "a");
terminal.sendInput("\x1b[A"); // Up arrow
assert.strictEqual(received, "\x1b[A");
terminal.stop();
});
test("resize triggers handler", async () => {
const terminal = new VirtualTerminal(80, 24);
let resized = false;
terminal.start(
() => {},
() => {
resized = true;
},
);
terminal.resize(100, 30);
assert.strictEqual(resized, true);
terminal.stop();
});
});