7d6a92ea15
Round-2 test-only locks (behaviors already at parity with Ink test/render.tsx, exit.tsx, errors.tsx, hooks.tsx): - resize re-render reflows content to the new width (exact narrowed round-border bytes) + consecutive width decreases each clear; onRender fires exactly once per rerender (tightened from `>`); onMounted runs before the first frame write callback (#596); bsu/esu wraps a trailing throttled content change, and an unchanged trailing rerender emits neither; patchConsole puts a log above the live frame. - exit-with-static #397 non-duplication (A/B/C each render once) — the fixture is fixed to function slots so no [Vue warn] pollutes stdout; exit-with-thrown-error via run()'s strict exit-0 gate; DEV is inert; the process stays alive ~500ms while raw mode is held. - raw mode is disabled on the thrown-error cleanup path; the unhandledRejection test moved to a *.sequential file (it mutates a process-global listener). - alternate-screen enter sequence hides the cursor; useStdout().write() preserves the frame with the external write ordered above it. Codex-reviewed GENUINE. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
2.6 KiB
TypeScript
55 lines
2.6 KiB
TypeScript
import { test as it, expect } from "vite-plus/test";
|
|
import ansiEscapes from "ansi-escapes";
|
|
import stripAnsi from "strip-ansi";
|
|
import { run } from "./helpers/run.ts";
|
|
|
|
it("console.log doesn't corrupt output", async () => {
|
|
const output = await run("console");
|
|
expect(output).toContain("First log");
|
|
expect(output).toContain("Second log");
|
|
});
|
|
|
|
// Port of Ink render.tsx:797-812 ("intercept console methods and display result
|
|
// above output"): a console.log emitted WHILE a frame is on screen is hoisted
|
|
// ABOVE the frame (the frame is erased, the log printed, the frame re-rendered).
|
|
// Splitting the raw bytes on eraseLines(2) — the exact erase the patched console
|
|
// writes to lift the one-line "Hello World" frame — isolates the pre-frame text
|
|
// ("First log", logged while mounted) from the post-frame text ("Second log",
|
|
// logged after unmount, below the final frame).
|
|
it("patched console.log appears above the on-screen frame", async () => {
|
|
const output = await run("console");
|
|
|
|
const frames = output.split(ansiEscapes.eraseLines(2)).map((line) => stripAnsi(line));
|
|
|
|
// First segment is the initial frame; the second is the post-log re-render,
|
|
// with "First log" hoisted above the re-rendered frame and "Second log" below.
|
|
expect(frames).toEqual(["Hello World\r\n", "First log\r\nHello World\r\nSecond log\r\n"]);
|
|
|
|
// Ordering is explicit: First log precedes the (re-rendered) frame, which
|
|
// precedes Second log.
|
|
const firstIdx = frames[1]!.indexOf("First log");
|
|
const frameIdx = frames[1]!.indexOf("Hello World");
|
|
const secondIdx = frames[1]!.indexOf("Second log");
|
|
expect(firstIdx).toBeGreaterThanOrEqual(0);
|
|
expect(firstIdx).toBeLessThan(frameIdx);
|
|
expect(frameIdx).toBeLessThan(secondIdx);
|
|
});
|
|
|
|
it("useStdout.write in real terminal", async () => {
|
|
const output = await run("use-stdout");
|
|
expect(output).toContain("Hello from vue-tui to stdout");
|
|
expect(output).toContain("exited");
|
|
});
|
|
|
|
// Port of Ink hooks.tsx:92-103 ("useStdout - write to stdout"): an external
|
|
// useStdout().write while a frame is on screen is hoisted ABOVE the preserved
|
|
// frame. After stripping ANSI and splitting on \r\n, the inner lines (dropping
|
|
// the leading initial-frame line and the trailing empty segment) are EXACTLY
|
|
// the external write, the preserved frame, and the post-exit log — in order.
|
|
it("useStdout.write appears above the preserved frame (exact ordering)", async () => {
|
|
const output = await run("use-stdout");
|
|
|
|
const lines = stripAnsi(output).split("\r\n");
|
|
expect(lines.slice(1, -1)).toEqual(["Hello from vue-tui to stdout", "Hello World", "exited"]);
|
|
});
|