diff --git a/apps/web/tests/vite-entry.e2e.ts b/apps/web/tests/vite-entry.e2e.ts index 80b9e2e683..131526819f 100644 --- a/apps/web/tests/vite-entry.e2e.ts +++ b/apps/web/tests/vite-entry.e2e.ts @@ -2,11 +2,28 @@ import { fileURLToPath } from 'node:url' import { join } from 'node:path' +import { createServer } from 'node:net' import { execa } from 'execa' import { describe, expect, it } from 'vitest' const WEB_ROOT = fileURLToPath(new URL('..', import.meta.url)) +/** Reserve an available loopback port, then release it for the child invocation. */ +async function freePort(): Promise { + const server = createServer() + await new Promise((resolve, reject) => { + server.once('error', reject) + server.listen(0, '127.0.0.1', resolve) + }) + const address = server.address() + if (address === null || typeof address === 'string') throw new Error('port probe returned no address') + await new Promise((resolve, reject) => server.close((error) => { + if (error === undefined) resolve() + else reject(error) + })) + return address.port +} + describe('Web development entry', () => { it('rejects the package dev alias with the full-host correction', async () => { const result = await execa('pnpm', ['run', 'dev'], { cwd: WEB_ROOT, reject: false }) @@ -16,14 +33,24 @@ describe('Web development entry', () => { }) it('rejects the standalone Vite server with the full-host correction', async () => { - const result = await execa(join(WEB_ROOT, 'node_modules/.bin/vite'), ['--host', '127.0.0.1', '--port', '0'], { + const port = await freePort() + const result = await execa(join(WEB_ROOT, 'node_modules/.bin/vite'), ['--host', '127.0.0.1', '--port', String(port)], { cwd: WEB_ROOT, reject: false, - timeout: 2_000, + timeout: 10_000, }) + expect(result.timedOut).toBe(false) expect(result.exitCode).not.toBe(0) expect(result.stderr).toContain('apps/web is not a standalone application') expect(result.stderr).toContain('dsh web') expect(result.stderr).toContain('window.__DSH_BOOT__') + await expect(new Promise((resolve, reject) => { + const probe = createServer() + probe.once('error', reject) + probe.listen(port, '127.0.0.1', () => probe.close((error) => { + if (error === undefined) resolve() + else reject(error) + })) + })).resolves.toBeUndefined() }) }) diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 659dfa7ebd..d624fc2472 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -10,12 +10,11 @@ const STANDALONE_ERROR = 'apps/web is not a standalone application: bare Vite ca /** Fail before a Vite dev or preview server can expose the boot-manifest-free shell. */ function rejectStandaloneServe(): Plugin { - const reject = (): never => { throw new Error(STANDALONE_ERROR) } return { name: 'dsh-reject-standalone-web-serve', - apply: 'serve', - configureServer: reject, - configurePreviewServer: reject, + config(_config, env) { + if (env.command === 'serve') throw new Error(STANDALONE_ERROR) + }, } }