Files
hyperframes/packages/cli/src/renderSetupWorker.test.ts
T
Miguel Ángel 0241078560 fix(cli): name the unzip errors when every Windows unzip tool fails (#5355)
* fix(cli): name the unzip errors when every Windows unzip tool fails

* fix(cli): a cached empty or folder Chrome counts as missing, not found

* fix(engine): skip an empty cached Chrome the same way the CLI does

* fix(engine): keep scanning the Chrome cache past a build it cannot read

* test(engine): a folder at the cached Chrome path is skipped
2026-10-09 19:18:27 +00:00

97 lines
3.2 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
installRenderSetupSignalHandlers,
renderSetupFailureFrom,
renderSetupErrorLine,
renderSetupResultFrom,
renderSetupResultLine,
} from "./renderSetupWorkerLifecycle.js";
describe("render setup worker signal lifecycle", () => {
function collectHandlers(handleHangup = true) {
const handlers = new Map<string, () => void>();
const dispose = installRenderSetupSignalHandlers(
{
on: vi.fn((signal, handler) => handlers.set(signal, handler)),
off: vi.fn((signal) => handlers.delete(signal)),
},
vi.fn(),
vi.fn(),
handleHangup,
);
return { handlers, dispose };
}
it("installs the complete interrupt set, including SIGHUP", () => {
const { handlers, dispose } = collectHandlers();
expect([...handlers.keys()]).toEqual(["SIGINT", "SIGTERM", "SIGHUP"]);
dispose();
});
it("does not override inherited SIGHUP behavior for detached setup workers", () => {
const { handlers, dispose } = collectHandlers(false);
expect([...handlers.keys()]).toEqual(["SIGINT", "SIGTERM"]);
dispose();
});
it.each(["SIGINT", "SIGTERM", "SIGHUP"] as const)(
"releases the browser lock before forwarding %s",
(signal) => {
const calls: string[] = [];
const handlers = new Map<string, () => void>();
installRenderSetupSignalHandlers(
{
on: (_signal, handler) => handlers.set(_signal, handler),
off: (_signal) => {
calls.push(`off:${_signal}`);
handlers.delete(_signal);
},
},
() => calls.push("release-lock"),
(forwardedSignal) => calls.push(`forward:${forwardedSignal}`),
);
handlers.get(signal)?.();
expect(calls.slice(0, 3)).toEqual(["release-lock", `off:${signal}`, `forward:${signal}`]);
},
);
});
describe("render setup worker failure line", () => {
it("carries the whole reason past the crash output around it", () => {
const reason =
"Failed to install chrome-headless-shell: missing.\n\n export HYPERFRAMES_BROWSER_PATH=x";
const failure = new Error(reason, { cause: new Error("tar.exe extraction failed") });
const stderr = [
`warning without a newline${renderSetupErrorLine(failure).trimEnd()}`,
"/worker.ts:20",
" throw error;",
"Error: Failed to install chrome-headless-shell: missing.",
" at downloadBrowser (manager.ts:845:11)",
].join("\n");
expect(renderSetupFailureFrom(stderr)).toEqual({
reason,
earlierOutput: "warning without a newline",
});
});
it.each([
["a worker that crashed without one", "Error: boom\n at x (y.ts:1:1)"],
["a cut-off line", 'HYPERFRAMES_RENDER_SETUP_ERROR:"Failed to ins'],
["an empty message", renderSetupErrorLine(new Error(""))],
])("finds no reason in %s, so the parent shows the raw output", (_label, stderr) => {
expect(renderSetupFailureFrom(stderr)).toBeUndefined();
});
it("reads back the result line the worker writes", () => {
const result = { executablePath: "/chrome", source: "cache" };
expect(renderSetupResultFrom(`noise\n${renderSetupResultLine(result)}`)).toEqual(result);
expect(renderSetupResultFrom("noise only")).toBeUndefined();
});
});