Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions crates/vp_cli_snapshots/tests/cli_snapshots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,14 @@ Fixture trees are excluded from repo-wide fmt, lint, typecheck, and vitest
(`vite.config.ts`, `tsconfig.json`); recorded snapshots and
`snapshots.toml` are runner metadata and never appear inside the staged
workspace a test runs in.

Test servers must request port `0` so the OS selects an available port at bind
time. Fixed ports can be reserved on Windows, where `EACCES` does not trigger
Vite's port-in-use fallback. Vitest 5.0.4 replaces a plain `test.api.port: 0`
with its default, including when it comes from `--api.port=0`. Set the port in
a post-configuration hook instead; see
[`test_v5_browser_defines/config.mjs`](fixtures/test_v5_browser_defines/config.mjs)
for independent, inherited, referenced, and injected projects, and
[`test_v5_optional_packages/verify-ui.mjs`](fixtures/test_v5_optional_packages/verify-ui.mjs)
for the UI server. Assert that each resolved API port is `0` so regressions fail
on every platform, even when the default port is available.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ export const executablePath = existsSync('chromium-path.json')

export function project(name, browser = true) {
return {
plugins: [{
name: 'browser-server-port',
// Each project needs its own hook, even with extends: true.
// Vitest replaces a plain api.port: 0 with its default. Override it last.
enforce: 'post',
config: {
order: 'post',
handler(config) {
config.test.api.port = 0;
},
},
}],
define: {
__VP_STRING_DEFINE__: JSON.stringify('/messages?locale="en"'),
__VP_FALSE_DEFINE__: 'false',
Expand Down Expand Up @@ -43,7 +55,7 @@ export function configFor(name) {
switch (name) {
case 'injected-hook': {
const config = project('root');
config.plugins = [{
config.plugins.push({
name: 'inject-browser-project',
configureVitest: {
order: 'pre',
Expand All @@ -56,7 +68,7 @@ export function configFor(name) {
await injectTestProjects({ extends: false, ...injected });
},
},
}];
});
return config;
}
case 'injected': return project('root');
Expand All @@ -78,15 +90,18 @@ export function configFor(name) {
case 'inherited': {
const config = project('root');
config.test.projects = [
{ test: project('shared').test },
{ extends: true, test: project('inherited').test },
{ plugins: config.plugins, test: project('shared').test },
{ extends: true, plugins: config.plugins, test: project('inherited').test },
];
return config;
}
case 'separate': {
const config = project('root');
config.test.sharedViteServer = false;
config.test.projects = [{ test: project('first').test }, { test: project('second').test }];
config.test.projects = [
{ plugins: config.plugins, test: project('first').test },
{ plugins: config.plugins, test: project('second').test },
];
return config;
}
case 'independent': return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ try {
]);
}
assert.equal(runner.projects.length, count, name);
for (const project of runner.projects) {
if (project.config.browser.enabled) {
assert.equal(project.config.api.port, 0, `${name}: ${project.name} must use an OS-assigned port`);
}
}
const servers = new Set(runner.projects.map((project) => project.vite));
if (name === 'shared' || name === 'shared-node') assert.equal(servers.size, 1);
if (name === 'separate') assert.equal(servers.size, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,22 @@ import { createVitest } from 'vite-plus/test/node';

const runner = await createVitest({
config: false, watch: false, reporters: [], ui: true, open: false,
api: { host: '127.0.0.1', port: 0 },
}, defineConfig({}));
api: { host: '127.0.0.1' },
}, defineConfig({
plugins: [{
name: 'ui-server-port',
// Vitest replaces a plain api.port: 0 with its default. Override it last.
enforce: 'post',
config: {
order: 'post',
handler(config) {
config.test.api.port = 0;
},
},
}],
}));
try {
assert.equal(runner.config.api.port, 0);
if (!runner.vite.httpServer.listening) await runner.vite.listen(0);
const base = `http://127.0.0.1:${runner.vite.httpServer.address().port}`;
const ui = new URL(runner.config.uiBase, base);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ try {
browser: { enabled: true, provider, headless: false, instances: [{ browser: 'chromium' }] },
},
}));
assert.equal(runner.projects[0].config.api.port, 0);
const result = await runner.start();
assert.equal(result.unhandledErrors.length, 0, JSON.stringify(result.unhandledErrors));
assert.equal(runner.state.getFiles().length, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ for (const phase of ['record', 'compare', 'failure']) {
},
}));
try {
assert.equal(runner.projects[0].config.api.port, 0);
const result = await runner.start();
assert.equal(result.unhandledErrors.length, 0, JSON.stringify(result.unhandledErrors));
assert.equal(runner.state.getFiles().length, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,32 @@ provider.providerFactory = (project) => {
};
return instance;
};
const runner = await createVitest({ config: false, watch: false, reporters: [] }, defineConfig({ test: {
include: ['browser.test.js'],
browser: {
enabled: true, headless: true, provider, instances: [{ browser: 'chrome' }],
viewport: { width: 800, height: 600 },
commands: { inspectLocator(context, locator) {
assert.equal(typeof locator, 'object');
return { ...locator, session: typeof context.sessionId === 'string' && context.sessionId.length > 0 };
} },
const runner = await createVitest({ config: false, watch: false, reporters: [] }, defineConfig({
plugins: [{
name: 'browser-server-port',
// Vitest replaces a plain api.port: 0 with its default. Override it last.
enforce: 'post',
config: {
order: 'post',
handler(config) {
config.test.api.port = 0;
},
},
}],
test: {
include: ['browser.test.js'],
browser: {
enabled: true, headless: true, provider, instances: [{ browser: 'chrome' }],
viewport: { width: 800, height: 600 },
commands: { inspectLocator(context, locator) {
assert.equal(typeof locator, 'object');
return { ...locator, session: typeof context.sessionId === 'string' && context.sessionId.length > 0 };
} },
},
},
} }));
}));
try {
assert.equal(runner.projects[0].config.api.port, 0);
const result = await runner.start();
assert.equal(result.unhandledErrors.length, 0, inspect(result.unhandledErrors));
assert.equal(runner.state.getCountOfFailedTests(), 0, JSON.stringify(runner.state.getFiles().flatMap((file) => [file.result, ...file.tasks.map((task) => task.result)])));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import assert from 'node:assert/strict';
import { defineConfig } from 'vite-plus';
import { playwright } from 'vite-plus/test/browser-playwright';

export default defineConfig({
plugins: [
{
name: 'browser-server-port',
// Vitest replaces a plain api.port: 0 with its default. Override it last.
enforce: 'post',
config: {
order: 'post',
handler(config) {
config.test.api.port = 0;
},
},
},
{
name: 'vitest-browser-mode-suppress-known-vite-logs',
configResolved(config) {
// "is in use, trying another one": port fallback depends on other
// concurrently running servers. "[optimizer]" progress lines fire on
// a 1s timer, so they only appear on slow cold-cache runs.
const nonDeterministicLogs = [
'is in use, trying another one',
'[optimizer]',
];
assert.equal(config.test.api.port, 0);
// "[optimizer]" progress lines fire on a 1s timer, so they only
// appear on slow cold-cache runs.
const info = config.logger.info;
config.logger.info = (message, options) => {
if (!nonDeterministicLogs.some((log) => message.includes(log))) {
if (!message.includes('[optimizer]')) {
info(message, options);
}
};
Expand Down
Loading