reduce e2e test flakiness
This commit is contained in:
+43
-18
@@ -12,13 +12,36 @@ import * as path from "path";
|
||||
export const test = base.extend<{ server: { baseURL: string }; page: Page }>({
|
||||
server: [
|
||||
async ({}, use) => {
|
||||
const server = await startServer();
|
||||
await use({ baseURL: server.baseURL });
|
||||
await killTree(server.child);
|
||||
// Clean up the DB files (including -wal / -shm).
|
||||
for (const suffix of ["", "-wal", "-shm"]) {
|
||||
fs.rmSync(server.dbPath + suffix, { force: true });
|
||||
}
|
||||
},
|
||||
{ scope: "test", auto: true },
|
||||
],
|
||||
|
||||
// Provide a page whose baseURL points at this test's server.
|
||||
page: async ({ browser, server }, use) => {
|
||||
const context = await browser.newContext({ baseURL: server.baseURL });
|
||||
const page = await context.newPage();
|
||||
await use(page);
|
||||
await context.close();
|
||||
},
|
||||
});
|
||||
|
||||
/** Starts a server, retrying on a fresh port if the first attempt fails to bind. */
|
||||
async function startServer() {
|
||||
for (let attempt = 0; attempt < 5; attempt++) {
|
||||
const dbPath = path.join(
|
||||
os.tmpdir(),
|
||||
`sustenance-e2e-${process.pid}-${Date.now()}-${Math.random()
|
||||
.toString(36)
|
||||
.slice(2)}.db`,
|
||||
);
|
||||
const port = 3200 + Math.floor(Math.random() * 2000);
|
||||
const port = 20000 + Math.floor(Math.random() * 30000);
|
||||
const baseURL = `http://127.0.0.1:${port}`;
|
||||
|
||||
const child = spawn(
|
||||
@@ -33,33 +56,35 @@ export const test = base.extend<{ server: { baseURL: string }; page: Page }>({
|
||||
// Point SEED_CONFIG at a nonexistent file so no default user is created.
|
||||
SEED_CONFIG: path.join(os.tmpdir(), "sustenance-e2e-no-seed.json"),
|
||||
},
|
||||
stdio: "ignore",
|
||||
stdio: ["ignore", "ignore", "pipe"],
|
||||
// Run in its own process group so we can kill the whole tree.
|
||||
detached: true,
|
||||
},
|
||||
);
|
||||
|
||||
let stderr = "";
|
||||
child.stderr?.on("data", (chunk) => {
|
||||
stderr += chunk.toString();
|
||||
});
|
||||
|
||||
try {
|
||||
await waitForServer(baseURL, child);
|
||||
|
||||
await use({ baseURL });
|
||||
|
||||
return { baseURL, child, dbPath };
|
||||
} catch (error) {
|
||||
// The server may have failed to bind (port collision). Clean up and retry.
|
||||
await killTree(child);
|
||||
// Clean up the DB files (including -wal / -shm).
|
||||
for (const suffix of ["", "-wal", "-shm"]) {
|
||||
fs.rmSync(dbPath + suffix, { force: true });
|
||||
}
|
||||
},
|
||||
{ scope: "test", auto: true },
|
||||
],
|
||||
|
||||
// Provide a page whose baseURL points at this test's server.
|
||||
page: async ({ browser, server }, use) => {
|
||||
const context = await browser.newContext({ baseURL: server.baseURL });
|
||||
const page = await context.newPage();
|
||||
await use(page);
|
||||
await context.close();
|
||||
},
|
||||
});
|
||||
if (attempt === 4) {
|
||||
throw new Error(
|
||||
`server failed to start after retries; last stderr:\n${stderr}\n${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
|
||||
async function waitForServer(baseURL: string, child: ChildProcess) {
|
||||
const deadline = Date.now() + 60_000;
|
||||
|
||||
Reference in New Issue
Block a user