Skip to content

Commit 8ca8e01

Browse files
mtojekdannykopping
andauthored
fix(site): wait until port is available in e2e (#15537)
Related: coder/internal#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>
1 parent 5861e51 commit 8ca8e01

File tree

1 file changed

+41
-0
lines changed

1 file changed

+41
-0
lines changed

site/e2e/helpers.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type ChildProcess, exec, spawn } from "node:child_process";
22
import { randomUUID } from "node:crypto";
3+
import net from "node:net";
34
import path from "node:path";
45
import { Duplex } from "node:stream";
56
import { type BrowserContext, type Page, expect, test } from "@playwright/test";
@@ -687,6 +688,8 @@ export class Awaiter {
687688
export const createServer = async (
688689
port: number,
689690
): Promise<ReturnType<typeof express>> => {
691+
await waitForPort(port); // Wait until the port is available
692+
690693
const e = express();
691694
// We need to specify the local IP address as the web server
692695
// tends to fail with IPv6 related error:
@@ -695,6 +698,44 @@ export const createServer = async (
695698
return e;
696699
};
697700

701+
async function waitForPort(
702+
port: number,
703+
host = "0.0.0.0",
704+
timeout = 30000,
705+
): Promise<void> {
706+
const start = Date.now();
707+
while (Date.now() - start < timeout) {
708+
const available = await isPortAvailable(port, host);
709+
if (available) {
710+
return;
711+
}
712+
console.warn(`${host}:${port} is in use, checking again in 1s`);
713+
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second before retrying
714+
}
715+
throw new Error(
716+
`Timeout: port ${port} is still in use after ${timeout / 1000} seconds.`,
717+
);
718+
}
719+
720+
function isPortAvailable(port: number, host = "0.0.0.0"): Promise<boolean> {
721+
return new Promise((resolve) => {
722+
const probe = net
723+
.createServer()
724+
.once("error", (err: NodeJS.ErrnoException) => {
725+
if (err.code === "EADDRINUSE") {
726+
resolve(false); // port is in use
727+
} else {
728+
resolve(false); // some other error occurred
729+
}
730+
})
731+
.once("listening", () => {
732+
probe.close();
733+
resolve(true); // port is available
734+
})
735+
.listen(port, host);
736+
});
737+
}
738+
698739
export const findSessionToken = async (page: Page): Promise<string> => {
699740
const cookies = await page.context().cookies();
700741
const sessionCookie = cookies.find((c) => c.name === "coder_session_token");

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy