mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 17:00:59 +00:00
- Switch to function based API - Anthropic SDK style async generator - Fully typed with escape hatches for custom models
164 lines
4.5 KiB
TypeScript
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();
|
|
});
|
|
});
|