Files
vue-tui/packages/testing/src/render.ts
T
Yunfei He 71c517b79f 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>
2026-05-24 18:13:47 +08:00

98 lines
2.4 KiB
TypeScript

import { PassThrough } from "node:stream";
import { nextTick, type Component } from "vue";
import { createApp, type TuiApp } from "@vue-tui/runtime";
import { makeFakeStdin, makeFakeWritable, type RawModeState } from "./streams.ts";
import { trackApp } from "./cleanup.ts";
export interface RenderOptions {
columns?: number;
rows?: number;
props?: Record<string, unknown>;
exitOnCtrlC?: boolean;
}
export interface Terminal {
readonly columns: number;
readonly rows: number;
resize(columns: number, rows: number): Promise<void>;
rawMode: RawModeState;
}
export interface RenderResult {
lastFrame(this: void): string | undefined;
frames: string[];
stdin: {
write(data: string): Promise<void>;
};
terminal: Terminal;
unmount(this: void): void;
waitUntilExit(this: void): Promise<void>;
}
function trimFrame(raw: string): string {
return raw
.split("\n")
.map((line) => line.trimEnd())
.join("\n")
.trimEnd();
}
export async function render(
component: Component,
options: RenderOptions = {},
): Promise<RenderResult> {
const stdout = makeFakeWritable({
columns: options.columns ?? 100,
rows: options.rows ?? 100,
});
const stderr = makeFakeWritable({
columns: options.columns ?? 100,
rows: options.rows ?? 100,
});
const { stream: stdin, rawMode } = makeFakeStdin();
const frames: string[] = [];
stdout.on("data", (chunk) => {
frames.push(trimFrame(chunk.toString()));
});
const app: TuiApp = createApp(component, options.props ?? undefined);
app.mount({ stdout, stdin, stderr, debug: true, exitOnCtrlC: options.exitOnCtrlC ?? false });
trackApp(app);
await nextTick();
const terminal: Terminal = {
get columns() {
return stdout.columns;
},
get rows() {
return stdout.rows;
},
async resize(columns: number, rows: number) {
stdout.columns = columns;
stdout.rows = rows;
(stderr as NodeJS.WriteStream).columns = columns;
(stderr as NodeJS.WriteStream).rows = rows;
(stdout as unknown as PassThrough).emit("resize");
await nextTick();
},
rawMode,
};
return {
lastFrame: () => frames.at(-1),
frames,
stdin: {
async write(data: string): Promise<void> {
stdin.emit("data", data);
await nextTick();
},
},
terminal,
unmount: app.unmount.bind(app),
waitUntilExit: app.waitUntilExit.bind(app),
};
}