fix(site): wait until port is available in e2e (#15537)

Related: https://github.com/coder/internal/issues/212

This PR modifies the logic responsible for creating a server in E2E
tests to check if the port is free. Alternatively, we could refactor the
framework to dynamically create server instances, but this solution
might be a cheaper quick win.

Note:

I'll leave it as is now, it might be worth asking somebody with a
frontend skillset to double-check this contribution.

---------

Signed-off-by: Danny Kopping <danny@coder.com>
Co-authored-by: Danny Kopping <danny@coder.com>
This commit is contained in:
Marcin Tojek
2024-11-18 10:28:16 +00:00
committed by GitHub
co-authored by Danny Kopping
parent 5861e516b9
commit 8ca8e016fa
+41
View File
@@ -1,5 +1,6 @@
import { type ChildProcess, exec, spawn } from "node:child_process";
import { randomUUID } from "node:crypto";
import net from "node:net";
import path from "node:path";
import { Duplex } from "node:stream";
import { type BrowserContext, type Page, expect, test } from "@playwright/test";
@@ -687,6 +688,8 @@ export class Awaiter {
export const createServer = async (
port: number,
): Promise<ReturnType<typeof express>> => {
await waitForPort(port); // Wait until the port is available
const e = express();
// We need to specify the local IP address as the web server
// tends to fail with IPv6 related error:
@@ -695,6 +698,44 @@ export const createServer = async (
return e;
};
async function waitForPort(
port: number,
host = "0.0.0.0",
timeout = 30000,
): Promise<void> {
const start = Date.now();
while (Date.now() - start < timeout) {
const available = await isPortAvailable(port, host);
if (available) {
return;
}
console.warn(`${host}:${port} is in use, checking again in 1s`);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second before retrying
}
throw new Error(
`Timeout: port ${port} is still in use after ${timeout / 1000} seconds.`,
);
}
function isPortAvailable(port: number, host = "0.0.0.0"): Promise<boolean> {
return new Promise((resolve) => {
const probe = net
.createServer()
.once("error", (err: NodeJS.ErrnoException) => {
if (err.code === "EADDRINUSE") {
resolve(false); // port is in use
} else {
resolve(false); // some other error occurred
}
})
.once("listening", () => {
probe.close();
resolve(true); // port is available
})
.listen(port, host);
});
}
export const findSessionToken = async (page: Page): Promise<string> => {
const cookies = await page.context().cookies();
const sessionCookie = cookies.find((c) => c.name === "coder_session_token");