* 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
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
import { readFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { getE2EDaemonPort } from "./daemon-port";
|
|
import { createNodeWebSocketFactory, type NodeWebSocketFactory } from "./node-ws-factory";
|
|
|
|
export async function loadDaemonClientConstructor<ClientConfig, ClientInstance>(): Promise<
|
|
new (config: ClientConfig) => ClientInstance
|
|
> {
|
|
const repoRoot = path.resolve(__dirname, "../../../../../");
|
|
const moduleUrl = pathToFileURL(
|
|
path.join(repoRoot, "packages/client/dist/daemon-client.js"),
|
|
).href;
|
|
const mod = (await import(moduleUrl)) as {
|
|
DaemonClient: new (config: ClientConfig) => ClientInstance;
|
|
};
|
|
return mod.DaemonClient;
|
|
}
|
|
|
|
interface E2EDaemonClientConfig {
|
|
url: string;
|
|
clientId: string;
|
|
clientType: "cli";
|
|
appVersion?: string;
|
|
webSocketFactory?: NodeWebSocketFactory;
|
|
}
|
|
|
|
function resolveDaemonWsUrl(port?: number): string {
|
|
return `ws://127.0.0.1:${port ?? getE2EDaemonPort()}/ws`;
|
|
}
|
|
|
|
export interface ConnectDaemonClientOptions {
|
|
clientIdPrefix: string;
|
|
appVersion?: string;
|
|
port?: number;
|
|
}
|
|
|
|
/**
|
|
* Connects an in-test daemon client over the isolated E2E daemon's WebSocket.
|
|
* The port-6767 guard keeps tests off the developer daemon. Each helper passes
|
|
* its own typed client interface as the generic.
|
|
*/
|
|
export async function connectDaemonClient<ClientInstance extends { connect(): Promise<void> }>(
|
|
options: ConnectDaemonClientOptions,
|
|
): Promise<ClientInstance> {
|
|
const DaemonClient = await loadDaemonClientConstructor<E2EDaemonClientConfig, ClientInstance>();
|
|
const client = new DaemonClient({
|
|
url: resolveDaemonWsUrl(options.port),
|
|
clientId: `${options.clientIdPrefix}-${randomUUID()}`,
|
|
clientType: "cli",
|
|
appVersion: options.appVersion ?? loadAppVersion(),
|
|
webSocketFactory: createNodeWebSocketFactory(),
|
|
});
|
|
await client.connect();
|
|
return client;
|
|
}
|
|
|
|
function loadAppVersion(): string {
|
|
const packageJsonPath = path.resolve(__dirname, "../../../package.json");
|
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as { version?: unknown };
|
|
if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
throw new Error(`Missing app version in ${packageJsonPath}`);
|
|
}
|
|
return packageJson.version;
|
|
}
|