* 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
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
import { createServer, type Server } from "node:http";
|
|
import { afterEach, expect, test } from "vitest";
|
|
|
|
import { waitForMetro, warmMetro } from "../e2e/support/global-setup";
|
|
|
|
class MetroPort {
|
|
private readonly responses = new Map<string, { status: number; body: string }>();
|
|
readonly requests: string[] = [];
|
|
|
|
private constructor(
|
|
readonly port: number,
|
|
private readonly server: Server,
|
|
) {}
|
|
|
|
static async listen(): Promise<MetroPort> {
|
|
let endpoint!: MetroPort;
|
|
const server = createServer((request, response) => {
|
|
const pathname = new URL(request.url ?? "/", "http://localhost").pathname;
|
|
endpoint.requests.push(pathname);
|
|
const served = endpoint.responses.get(pathname) ?? { status: 500, body: "fallback" };
|
|
response.writeHead(served.status, { "content-type": "text/plain" });
|
|
response.end(served.body);
|
|
});
|
|
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
const address = server.address();
|
|
if (!address || typeof address === "string") {
|
|
server.close();
|
|
throw new Error("Failed to listen for Metro readiness test");
|
|
}
|
|
endpoint = new MetroPort(address.port, server);
|
|
return endpoint;
|
|
}
|
|
|
|
serveMetro(): void {
|
|
this.responses.set("/status", { status: 200, body: "packager-status:running" });
|
|
}
|
|
|
|
serveWarmableDocument(): void {
|
|
this.responses.set("/", {
|
|
status: 200,
|
|
body: '<html><script src="/index.bundle?platform=web"></script></html>',
|
|
});
|
|
this.responses.set("/index.bundle", { status: 200, body: "compiled bundle" });
|
|
}
|
|
|
|
async close(): Promise<void> {
|
|
await new Promise<void>((resolve, reject) => {
|
|
this.server.close((error) => {
|
|
if (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
let endpoint: MetroPort | null = null;
|
|
|
|
afterEach(async () => {
|
|
await endpoint?.close();
|
|
endpoint = null;
|
|
});
|
|
|
|
test("Metro readiness rejects another HTTP listener on the selected port", async () => {
|
|
endpoint = await MetroPort.listen();
|
|
|
|
await expect(waitForMetro(endpoint.port, { label: "Metro", timeoutMs: 150 })).rejects.toThrow(
|
|
"Expected Metro status",
|
|
);
|
|
|
|
endpoint.serveMetro();
|
|
await expect(waitForMetro(endpoint.port, { label: "Metro", timeoutMs: 150 })).resolves.toBe(
|
|
undefined,
|
|
);
|
|
});
|
|
|
|
test("Metro warmup compiles the document's same-origin scripts before tests start", async () => {
|
|
endpoint = await MetroPort.listen();
|
|
endpoint.serveWarmableDocument();
|
|
|
|
await warmMetro(endpoint.port);
|
|
|
|
expect(endpoint.requests).toEqual(["/", "/index.bundle"]);
|
|
});
|