mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-24 12:06:54 -05:00
Try to fix E2E test port stall
This commit is contained in:
@@ -10,7 +10,7 @@ description: Run, debug, and manage Playwright e2e tests. Use when running e2e t
|
||||
- Tests live in `e2e/*.spec.ts`, config in `playwright.config.ts`
|
||||
- Page objects live in `e2e/pages/<feature>/` — every spec uses them; conventions in `docs/dev/e2e-page-objects.md`, gotchas in `docs/dev/e2e-page-objects-migration.md`
|
||||
- Global setup (`e2e/global-setup.ts`) builds the app (skipped when no build input changed since the last e2e build — tracked via `.e2e-build-marker`), creates/migrates per-worker databases (via `scripts/ensure-test-db.ts`: pending migrations are applied, drifted databases are rebuilt), and starts one server per worker
|
||||
- Port calculation: `E2E_BASE_PORT = PORT (from .env) + 500`. Default PORT is typically 4001, so base port = 4501. Worker N uses port base+N
|
||||
- Port calculation: `E2E_BASE_PORT = PORT (from .env) + 500`. Worker N uses port base+N, except ports on the WHATWG fetch bad port list (e.g. 6679) are skipped — see `e2eWorkerPort` in `e2e/helpers/playwright.ts`
|
||||
- Worker count: `E2E_WORKERS` env, defaulting to `min(8, max(4, cores - 2))`
|
||||
- Worker databases: `db-test-e2e-<N>.sqlite3` in the project root; every test starts from a wiped database holding only the admin (Sendou) and N-ZAP users, and builds its own data with the `factories` fixture
|
||||
- MinIO (S3-compatible storage) is started via Docker Compose if not already running
|
||||
|
||||
@@ -2,7 +2,7 @@ import { type ChildProcess, execSync, spawn } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import type { FullConfig } from "@playwright/test";
|
||||
import { ensureMigratedDb } from "../scripts/ensure-test-db";
|
||||
import { E2E_BASE_PORT } from "./helpers/playwright";
|
||||
import { E2E_BASE_PORT, e2eWorkerPort } from "./helpers/playwright";
|
||||
|
||||
const DEBUG = process.env.E2E_DEBUG === "true";
|
||||
const SERVER_PROCESSES: ChildProcess[] = [];
|
||||
@@ -182,12 +182,13 @@ async function globalSetup(config: FullConfig) {
|
||||
// Prepare databases and start servers for each worker
|
||||
const serverPromises: Promise<void>[] = [];
|
||||
|
||||
// Kill any existing processes on our ports before starting
|
||||
// Kill any existing processes on our ports before starting; sweep beyond the
|
||||
// current worker count so leftovers from a run with more workers also die
|
||||
// biome-ignore lint/suspicious/noConsole: CLI script output
|
||||
console.log("Cleaning up any existing processes on e2e ports...");
|
||||
const killedSomething = killProcessesOnPorts(
|
||||
E2E_BASE_PORT,
|
||||
E2E_BASE_PORT + workerCount - 1,
|
||||
e2eWorkerPort(Math.max(workerCount, 8) - 1),
|
||||
);
|
||||
if (killedSomething) {
|
||||
// Wait briefly for ports to be released
|
||||
@@ -195,7 +196,7 @@ async function globalSetup(config: FullConfig) {
|
||||
}
|
||||
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
const port = E2E_BASE_PORT + i;
|
||||
const port = e2eWorkerPort(i);
|
||||
const dbPath = `db-test-e2e-${i}.sqlite3`;
|
||||
|
||||
ensureMigratedDb(dbPath);
|
||||
@@ -255,12 +256,13 @@ async function globalSetup(config: FullConfig) {
|
||||
);
|
||||
}
|
||||
|
||||
// Store server processes globally for teardown before awaiting readiness so
|
||||
// a failed startup still gets every already-spawned server cleaned up
|
||||
global.__E2E_SERVERS__ = SERVER_PROCESSES;
|
||||
|
||||
// Wait for all servers to be ready
|
||||
await Promise.all(serverPromises);
|
||||
|
||||
// Store server processes globally for teardown
|
||||
global.__E2E_SERVERS__ = SERVER_PROCESSES;
|
||||
|
||||
// biome-ignore lint/suspicious/noConsole: CLI script output
|
||||
console.log("\nAll servers started successfully!\n");
|
||||
}
|
||||
|
||||
@@ -24,6 +24,25 @@ export const E2E_BASE_PORT = Number(process.env.PORT || 5173) + 500;
|
||||
export const MOBILE_VIEWPORT = { width: 375, height: 667 };
|
||||
export const TABLET_VIEWPORT = { width: 768, height: 1024 };
|
||||
|
||||
/** Registered (>=1024) ports on the WHATWG fetch bad port list: Node's fetch
|
||||
* fails on them with "bad port" and Chromium with ERR_UNSAFE_PORT, so no worker
|
||||
* server may listen on one (e.g. base port 6673 would put worker 6 on 6679). */
|
||||
const UNSAFE_PORTS = new Set([
|
||||
1719, 1720, 1723, 2049, 3659, 4045, 4190, 5060, 5061, 5432, 5500, 5938, 6000,
|
||||
6566, 6665, 6666, 6667, 6668, 6669, 6679, 6697, 10080,
|
||||
]);
|
||||
|
||||
/** The port of the given worker's server: base port + index, skipping unsafe ports. */
|
||||
export function e2eWorkerPort(workerIndex: number) {
|
||||
let port = E2E_BASE_PORT - 1;
|
||||
for (let i = 0; i <= workerIndex; i++) {
|
||||
do {
|
||||
port++;
|
||||
} while (UNSAFE_PORTS.has(port));
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
type WorkerFixtures = {
|
||||
workerPort: number;
|
||||
workerBaseURL: string;
|
||||
@@ -49,7 +68,7 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
|
||||
workerPort: [
|
||||
// biome-ignore lint/correctness/noEmptyPattern: Playwright requires object destructuring
|
||||
async ({}, use, workerInfo) => {
|
||||
const port = E2E_BASE_PORT + workerInfo.parallelIndex;
|
||||
const port = e2eWorkerPort(workerInfo.parallelIndex);
|
||||
await use(port);
|
||||
},
|
||||
{ scope: "worker" },
|
||||
|
||||
Reference in New Issue
Block a user