feat(testing): test harness for @vue-tui/runtime

Provides render() for integration testing — async with auto-flush,
trimmed frames, fake terminal with resize(), stdin.write() with
auto-flush, and auto-cleanup via afterEach.

Exports: render, cleanup, RenderResult, RenderOptions, Terminal

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yunfei He
2026-05-24 18:13:47 +08:00
parent ff82f6e064
commit 71c517b79f
10 changed files with 327 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
import { nextTick, ref } from "vue";
import { expect, test } from "vite-plus/test";
import { render } from "../src/index.ts";
import { Text } from "@vue-tui/runtime";
test("lastFrame captures rendered output", async () => {
const { lastFrame } = await render(() => <Text>hello</Text>);
expect(lastFrame()).toContain("hello");
});
test("frames accumulate on reactive updates", async () => {
const message = ref("first");
const { lastFrame, frames } = await render(() => <Text>{message.value}</Text>);
const initialCount = frames.length;
expect(lastFrame()).toContain("first");
message.value = "second";
await nextTick();
expect(lastFrame()).toContain("second");
expect(frames.length).toBeGreaterThan(initialCount);
});
test("render with custom columns", async () => {
const { lastFrame } = await render(() => <Text>hello</Text>, { columns: 20 });
expect(lastFrame()).toContain("hello");
});
test("lastFrame trims trailing whitespace", async () => {
const { lastFrame } = await render(() => <Text>hi</Text>);
const frame = lastFrame()!;
for (const line of frame.split("\n")) {
expect(line).toBe(line.trimEnd());
}
});
test("auto cleanup — no manual unmount needed", async () => {
const { lastFrame } = await render(() => <Text>auto</Text>);
expect(lastFrame()).toContain("auto");
});
+23
View File
@@ -0,0 +1,23 @@
import { expect, test } from "vite-plus/test";
import { makeFakeStdin, makeFakeWritable } from "../src/streams.ts";
test("fake stdout reports columns/rows and isTTY", () => {
const s = makeFakeWritable({ columns: 50, rows: 10 });
expect(s.columns).toBe(50);
expect(s.rows).toBe(10);
expect(s.isTTY).toBe(true);
});
test("fake stdin supports setRawMode and emits data", () => {
const { stream: s, rawMode } = makeFakeStdin();
expect(s.isTTY).toBe(true);
let observed = "";
s.on("data", (c) => {
observed += c.toString();
});
s.setRawMode(true);
expect(rawMode.current).toBe(true);
expect(rawMode.history).toEqual([true]);
s.emit("data", "hello");
expect(observed).toBe("hello");
});