// Playwright's request context (it runs in Node), trying a connection again when it never came about — // as every TCP stack does. Measured on the Windows dev desktop (2026-10-10, the work-group-timesheet // suite): now and then, in bursts of 1–15 s, a connect to localhost is dropped — every port, IPv4 and // IPv6, any program, no Docker needed; set off by any big test run on the machine. Every other client // sends its SYN again and gets in a moment later, but Node (libuv) sends a loopback SYN only once on // Windows and fails at ~300 ms with "connect ETIMEDOUT". The request never left this side, so trying it // again is safe: the waits the system's own retries take (0.5, 1, 2, 4, 8 s). Anything else — a refused // port, a reset, an answer — goes straight to the test. (The browser resends its own SYN: a page only // waits, and nothing here can or should hide that.) const METHODS = new Set(["get", "post", "put", "patch", "delete", "head", "fetch"]); const NO_CONNECTION = /\bconnect ETIMEDOUT\b/; const WAITS_MS = [500, 1000, 2000, 4000, 8000]; /** `attempt` again after each wait while it fails with `transient`; any other failure goes on. */ async function tryAgain(attempt: () => Promise, transient: RegExp): Promise { for (let i = 0; ; i++) { try { return await attempt(); } catch (e) { const wait = WAITS_MS[i]; if (wait === undefined || !transient.test(String((e as Error)?.message))) throw e; await new Promise((r) => setTimeout(r, wait)); } } } /** `ctx` with its request methods trying a connect that timed out again (see above). */ export function retryConnect(ctx: T): T { return new Proxy(ctx, { get(target, key) { const v: unknown = Reflect.get(target, key, target); if (typeof v !== "function") return v; if (typeof key !== "string" || !METHODS.has(key)) return v.bind(target); return (...args: unknown[]) => tryAgain(() => Promise.resolve(v.apply(target, args)), NO_CONNECTION); }, }); } // A page load has a failure of its own on the same machine: `net::ERR_NO_BUFFER_SPACE`, the socket // call refused for the moment. It stopped a release's e2e at its first page (0.116.3, with nothing // else running; 0.107.3 aborted on it too), and the unchanged rerun passed. A browser does not try // a main document again by itself, and the request never left, so this does. The suite's every page // load goes through here (`e2eRetryConnect.test.ts`). const NO_BUFFER = /\bERR_NO_BUFFER_SPACE\b/; export function gotoRetrying(page: { goto(url: string): Promise }, url: string): Promise { return tryAgain(() => page.goto(url), NO_BUFFER); }