mirror of
https://github.com/millionco/react-doctor.git
synced 2026-09-14 20:00:24 +08:00
3cc997108b
Co-authored-by: Cursor <cursoragent@cursor.com>
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vite-plus/test";
|
|
import { TERMINAL_HANGUP_EXIT_CODE } from "../src/cli/utils/constants.js";
|
|
import { guardStdin, handleStdinError } from "../src/cli/utils/guard-stdin.js";
|
|
|
|
const makeStdinError = (code: string | undefined): NodeJS.ErrnoException => {
|
|
const error: NodeJS.ErrnoException = new Error(`read ${code}`);
|
|
error.code = code;
|
|
return error;
|
|
};
|
|
|
|
// Make `process.exit` throw a sentinel instead of exiting the test runner, so
|
|
// a call is observable and `handleStdinError`'s post-exit `throw` stays
|
|
// unreached (mirroring how a real exit halts execution there).
|
|
const stubProcessExitToThrow = () =>
|
|
vi.spyOn(process, "exit").mockImplementation(((exitCode?: number) => {
|
|
throw new Error(`__exit__:${exitCode}`);
|
|
}) as never);
|
|
|
|
describe("handleStdinError", () => {
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
// A vanished terminal surfaces as `read EIO` on the raw-mode stdin handle.
|
|
// Exit like an interrupted run instead of crashing as an uncaught exception.
|
|
it.each(["EIO", "ENXIO"])("exits with the hangup code on %s", (code) => {
|
|
const exit = stubProcessExitToThrow();
|
|
|
|
expect(() => handleStdinError(makeStdinError(code))).toThrow(
|
|
`__exit__:${TERMINAL_HANGUP_EXIT_CODE}`,
|
|
);
|
|
expect(exit).toHaveBeenCalledWith(TERMINAL_HANGUP_EXIT_CODE);
|
|
});
|
|
|
|
// Anything that isn't a terminal hangup must keep funneling to the crash
|
|
// reporter exactly as it did before the guard existed: re-thrown, not exited.
|
|
it.each(["EPIPE", "EACCES", undefined])("re-throws and never exits on %s", (code) => {
|
|
const exit = stubProcessExitToThrow();
|
|
const error = makeStdinError(code);
|
|
|
|
expect(() => handleStdinError(error)).toThrow(error);
|
|
expect(exit).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("guardStdin", () => {
|
|
afterEach(() => {
|
|
process.stdin.removeListener("error", handleStdinError);
|
|
});
|
|
|
|
it("registers the stdin error handler", () => {
|
|
expect(process.stdin.listeners("error")).not.toContain(handleStdinError);
|
|
guardStdin();
|
|
expect(process.stdin.listeners("error")).toContain(handleStdinError);
|
|
});
|
|
});
|