Files
vue-tui/packages/runtime-tests/integration/lifecycle/test-streams.ts
T
Yunfei He 7d03a110e1 fix: address /simplify review — frame-writer dedup desync, scheduler edge cases
Correctness fixes found by max-effort review of the branch diff:

- frame-writer.sync() now updates lastFrame alongside log-update's
  previousOutput. Previously the two dedup layers desynced after a sync()
  (the clearTerminal path), silently dropping a legitimately-changed frame
  and emitting an empty BSU/ESU pair. Adds a regression test.
- scheduler: the queuePostFlushCb callback now bails if scheduled was reset
  by cancel(), so a stale callback can't commit on a torn-down tree or
  re-arm an uncancellable trailing timer.
- scheduler.flush() now collects multiple concurrent waiters instead of
  overwriting a single resolver — fixes a hang when two waitUntilRenderFlush()
  calls await the same pending commit.
- render teardown nulls mountedClear so a post-unmount app.clear() can't
  write to a torn-down stream.
- test-streams getContentWrites imports bsu/esu instead of hardcoding the
  escape literals.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 14:18:23 +08:00

86 lines
2.3 KiB
TypeScript

import { PassThrough, Writable } from "node:stream";
import { bsu, esu } from "../../../runtime/src/io/write-synchronized.ts";
export interface FakeWritableOptions {
columns?: number;
rows?: number;
}
export function makeFakeWritable(options: FakeWritableOptions = {}): NodeJS.WriteStream {
const s = new PassThrough() as unknown as NodeJS.WriteStream;
Object.assign(s, {
columns: options.columns ?? 100,
rows: options.rows ?? 100,
isTTY: true,
});
return s;
}
export function makeFakeStdin(): { stream: NodeJS.ReadStream } {
const s = new PassThrough() as unknown as NodeJS.ReadStream;
Object.assign(s, {
isTTY: true,
setRawMode(this: NodeJS.ReadStream) {
return this;
},
setEncoding(this: NodeJS.ReadStream) {
return this;
},
});
(s as any).ref = () => {};
(s as any).unref = () => {};
return { stream: s };
}
export function createDelayedWriteCallbackStdout({
shouldDelay,
onDelayElapsed,
delayMs = 150,
}: {
shouldDelay: (chunk: string | Uint8Array) => boolean;
onDelayElapsed: () => void;
delayMs?: number;
}): NodeJS.WriteStream {
let didDelayOnce = false;
const stdout = new Writable({
write(
chunk: string | Uint8Array,
_encoding: BufferEncoding,
callback: (error?: Error) => void,
) {
if (!didDelayOnce && shouldDelay(chunk)) {
didDelayOnce = true;
setTimeout(() => {
onDelayElapsed();
callback();
}, delayMs);
return;
}
callback();
},
}) as unknown as NodeJS.WriteStream;
stdout.columns = 100;
stdout.isTTY = true;
return stdout;
}
export const isWriteBarrierChunk = (chunk: string | Uint8Array): boolean =>
(typeof chunk === "string" && chunk === "") ||
(chunk instanceof Uint8Array && chunk.length === 0);
export function captureWrites(stdout: NodeJS.WriteStream): string[] {
const writes: string[] = [];
const original = stdout.write.bind(stdout);
stdout.write = ((...args: unknown[]) => {
writes.push(String(args[0]));
return (original as Function)(...args);
}) as NodeJS.WriteStream["write"];
return writes;
}
export function getContentWrites(writes: string[]): string[] {
return writes.filter((w) => w !== "" && !w.startsWith("\x1b[?25") && w !== bsu && w !== esu);
}