Files
paseo/packages/app/e2e/support/helpers/daemon-update.ts
T
Mohamed BoudraandGitHub 5a30affd7f Skip unrelated required checks without losing their names (#2542)
* fix(ci): skip unrelated required checks cleanly

Matrix jobs had to start no-op runners to preserve interpolated required-check names. Static named jobs let GitHub report genuine skipped conclusions while keeping those names stable.

* fix(ci): gate changes to install patching script

* fix(ci): route cross-package test contracts narrowly

* fix(ci): keep routed contracts merge-blocking

* fix(server): exclude nested test utilities from builds

* fix(ci): cover app-owned native and browser domains

* refactor(ci): align tests with runtime domains

Own browser specs, desktop specs, and Electron-only app code by stable directories so PR routing does not depend on filename conventions. Keep desktop integration coverage inside the required desktop check.

* fix(ci): keep routing and desktop suites isolated

The routing regression must run before dependency installation, and desktop Vitest must not collect Playwright-owned specs.

* fix(ci): isolate desktop e2e runtime

* perf(test): isolate Playwright workers

* perf(test): parallelize Playwright within specs

* fix(test): keep Playwright file scheduling

* fix(test): share dynamic Metro fixture

* fix(test): preserve own-host restart fixtures

* fix(ci): preserve routed contracts after rebase

* Stabilize Playwright wheel-scroll assertions
2026-08-02 00:56:05 +08:00

98 lines
2.8 KiB
TypeScript

import { fork, type ChildProcess } from "node:child_process";
import { once } from "node:events";
import path from "node:path";
export interface OutdatedDaemon {
endpoint: string;
label: string;
serverId: string;
close(): Promise<void>;
}
interface OutdatedDaemonReadyMessage {
type: "ready";
endpoint: string;
serverId: string;
}
interface OutdatedDaemonErrorMessage {
type: "error";
error: string;
}
type OutdatedDaemonMessage = OutdatedDaemonReadyMessage | OutdatedDaemonErrorMessage;
export async function startOutdatedDaemon(options?: {
desktopManaged?: boolean;
daemonStatusRpcCapability?: boolean;
relayConfigCapability?: boolean;
}): Promise<OutdatedDaemon> {
const metroPort = process.env.E2E_METRO_PORT;
if (!metroPort) {
throw new Error("E2E_METRO_PORT is not set - globalSetup must run first");
}
const child = fork(
path.resolve(__dirname, "../../../../server/src/server/test-utils/outdated-daemon-process.ts"),
{
env: {
...process.env,
E2E_METRO_PORT: metroPort,
E2E_DESKTOP_MANAGED: options?.desktopManaged === true ? "1" : "0",
E2E_DAEMON_STATUS_RPC_CAPABILITY: options?.daemonStatusRpcCapability === false ? "0" : "1",
E2E_RELAY_CONFIG_CAPABILITY: options?.relayConfigCapability === false ? "0" : "1",
},
execArgv: ["--import", "tsx"],
stdio: ["ignore", "pipe", "pipe", "ipc"],
},
);
const stderr: string[] = [];
child.stderr?.on("data", (data: Buffer) => stderr.push(data.toString("utf8")));
try {
const ready = await waitForDaemon(child, stderr);
return {
endpoint: ready.endpoint,
label: options?.desktopManaged === true ? "outdated Desktop host" : "outdated host",
serverId: ready.serverId,
async close() {
if (child.exitCode !== null || child.signalCode !== null) return;
child.kill("SIGTERM");
await once(child, "exit");
},
};
} catch (error) {
child.kill("SIGTERM");
throw error;
}
}
async function waitForDaemon(
child: ChildProcess,
stderr: string[],
): Promise<OutdatedDaemonReadyMessage> {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error(`Timed out starting outdated daemon. ${stderr.join("")}`));
}, 20_000);
child.once("exit", (code, signal) => {
clearTimeout(timeout);
reject(
new Error(
`Outdated daemon exited before startup (code ${String(code)}, signal ${String(signal)}). ${stderr.join("")}`,
),
);
});
child.once("message", (message: OutdatedDaemonMessage) => {
if (message.type === "error") {
clearTimeout(timeout);
reject(new Error(message.error));
return;
}
clearTimeout(timeout);
resolve(message);
});
});
}