(
entrypoint: string,
fn: (url: string) => Promise<void>,
options: { runningTimeout?: number; runtime?: 'node' | 'bun' } = {}
)
| 41 | } |
| 42 | |
| 43 | async function withDevServer( |
| 44 | entrypoint: string, |
| 45 | fn: (url: string) => Promise<void>, |
| 46 | options: { runningTimeout?: number; runtime?: 'node' | 'bun' } = {} |
| 47 | ) { |
| 48 | const { runningTimeout, runtime = 'node' } = options; |
| 49 | const child = await testForkDevServer(entrypoint, runtime); |
| 50 | |
| 51 | const result = await readMessage(child); |
| 52 | if (result.state !== 'message') { |
| 53 | throw new Error('Exited. error: ' + JSON.stringify(result.value)); |
| 54 | } |
| 55 | const { address = 'localhost', port } = result.value; |
| 56 | const url = `http://${address}:${port}`; |
| 57 | |
| 58 | const start = Date.now(); |
| 59 | |
| 60 | try { |
| 61 | return await fn(url); |
| 62 | } finally { |
| 63 | const elapsed = Date.now() - start; |
| 64 | if (runningTimeout) await setTimeout(runningTimeout - elapsed); |
| 65 | |
| 66 | // Bun uses spawn() not fork(), so no IPC. Use SIGTERM to gracefully shut down. |
| 67 | if (runtime === 'bun') { |
| 68 | if (child.pid) { |
| 69 | try { |
| 70 | process.kill(child.pid, 'SIGTERM'); |
| 71 | } catch (_err) { |
| 72 | // Process might have already exited |
| 73 | } |
| 74 | } |
| 75 | } else { |
| 76 | child.send('shutdown', error => { |
| 77 | if (error) { |
| 78 | console.log('Shutdown error:', error); |
| 79 | try { |
| 80 | child.kill(9); |
| 81 | } catch (killError) { |
| 82 | // In Node 22, there's a bug where attempting to kill a child process |
| 83 | // results in an EPERM error. Ignore the error in that case. |
| 84 | // See: https://github.com/nodejs/node/issues/51766 |
| 85 | console.log('Ignoring kill error:', killError); |
| 86 | } |
| 87 | } |
| 88 | }); |
| 89 | } |
| 90 | if (child.exitCode === null) await once(child, 'exit'); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | describe('web handlers', () => { |
| 95 | describe.each(['node', 'bun'] as const)('for %s runtime', runtime => { |
no test coverage detected