@@ -2,6 +2,7 @@ import { type ChildProcess, exec, spawn } from "node:child_process";
2
2
import { randomUUID } from "node:crypto" ;
3
3
import path from "node:path" ;
4
4
import { Duplex } from "node:stream" ;
5
+ import net from "node:net" ;
5
6
import { type BrowserContext , type Page , expect , test } from "@playwright/test" ;
6
7
import { API } from "api/api" ;
7
8
import type {
@@ -685,6 +686,8 @@ export class Awaiter {
685
686
export const createServer = async (
686
687
port : number ,
687
688
) : Promise < ReturnType < typeof express > > => {
689
+ await waitForPort ( port ) ; // Wait until the port is available
690
+
688
691
const e = express ( ) ;
689
692
// We need to specify the local IP address as the web server
690
693
// tends to fail with IPv6 related error:
@@ -693,6 +696,36 @@ export const createServer = async (
693
696
return e ;
694
697
} ;
695
698
699
+ async function waitForPort ( port : number , host = "0.0.0.0" , timeout = 5000 ) : Promise < void > {
700
+ const start = Date . now ( ) ;
701
+ while ( Date . now ( ) - start < timeout ) {
702
+ const available = await isPortAvailable ( port , host ) ;
703
+ if ( available ) {
704
+ return ;
705
+ }
706
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ; // Wait 1 second before retrying
707
+ }
708
+ throw new Error ( `Timeout: port ${ port } is still in use after ${ timeout / 1000 } seconds.` ) ;
709
+ }
710
+
711
+ function isPortAvailable ( port : number , host = "0.0.0.0" ) : Promise < boolean > {
712
+ return new Promise ( ( resolve ) => {
713
+ const probe = net . createServer ( )
714
+ . once ( 'error' , ( err : any ) => {
715
+ if ( err . code === 'EADDRINUSE' ) {
716
+ resolve ( false ) ; // port is in use
717
+ } else {
718
+ resolve ( false ) ; // some other error occurred
719
+ }
720
+ } )
721
+ . once ( 'listening' , ( ) => {
722
+ probe . close ( ) ;
723
+ resolve ( true ) ; // port is available
724
+ } )
725
+ . listen ( port , host ) ;
726
+ } ) ;
727
+ }
728
+
696
729
export const findSessionToken = async ( page : Page ) : Promise < string > => {
697
730
const cookies = await page . context ( ) . cookies ( ) ;
698
731
const sessionCookie = cookies . find ( ( c ) => c . name === "coder_session_token" ) ;
0 commit comments