Skip to content

Commit 64f2b43

Browse files
authored
fix: apply browser CLI options only if the project has the browser set in the config already (#8002)
1 parent 6e8d937 commit 64f2b43

File tree

18 files changed

+361
-185
lines changed

18 files changed

+361
-185
lines changed

packages/vitest/src/node/cli/cac.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,6 @@ function normalizeCliOptions(cliFilters: string[], argv: CliOptions): CliOptions
283283
argv.includeTaskLocation ??= true
284284
}
285285

286-
// running "vitest --browser.headless"
287-
if (typeof argv.browser === 'object' && !('enabled' in argv.browser)) {
288-
argv.browser.enabled = true
289-
}
290286
if (typeof argv.typecheck?.only === 'boolean') {
291287
argv.typecheck.enabled ??= true
292288
}

packages/vitest/src/node/cli/cli-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ export const cliOptionsConfig: VitestCLIOptions = {
361361
return { enabled: browser === 'yes' }
362362
}
363363
if (typeof browser === 'string') {
364-
return { enabled: true, name: browser }
364+
return { name: browser }
365365
}
366366
return browser
367367
},

packages/vitest/src/node/config/resolveConfig.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ResolvedConfig as ResolvedViteConfig } from 'vite'
22
import type { Vitest } from '../core'
33
import type { BenchmarkBuiltinReporters } from '../reporters'
4+
import type { ResolvedBrowserOptions } from '../types/browser'
45
import type {
56
ApiConfig,
67
ResolvedConfig,
@@ -13,6 +14,7 @@ import { toArray } from '@vitest/utils'
1314
import { resolveModule } from 'local-pkg'
1415
import { normalize, relative, resolve } from 'pathe'
1516
import c from 'tinyrainbow'
17+
import { mergeConfig } from 'vite'
1618
import {
1719
defaultBrowserPort,
1820
defaultInspectPort,
@@ -199,8 +201,6 @@ export function resolveConfig(
199201
resolved.minWorkers = resolveInlineWorkerOption(resolved.minWorkers)
200202
}
201203

202-
resolved.browser ??= {} as any
203-
204204
// run benchmark sequentially by default
205205
resolved.fileParallelism ??= mode !== 'benchmark'
206206

@@ -232,10 +232,23 @@ export function resolveConfig(
232232
}
233233
}
234234

235+
// apply browser CLI options only if the config already has the browser config and not disabled manually
236+
if (
237+
vitest._cliOptions.browser
238+
&& resolved.browser
239+
// if enabled is set to `false`, but CLI overrides it, then always override it
240+
&& (resolved.browser.enabled !== false || vitest._cliOptions.browser.enabled)
241+
) {
242+
resolved.browser = mergeConfig(
243+
resolved.browser,
244+
vitest._cliOptions.browser,
245+
) as ResolvedBrowserOptions
246+
}
247+
248+
resolved.browser ??= {} as any
235249
const browser = resolved.browser
236250

237-
// if browser was enabled via CLI and it's configured by the user, then validate the input
238-
if (browser.enabled && viteConfig.test?.browser) {
251+
if (browser.enabled) {
239252
if (!browser.name && !browser.instances) {
240253
throw new Error(`Vitest Browser Mode requires "browser.name" (deprecated) or "browser.instances" options, none were set.`)
241254
}
@@ -800,7 +813,6 @@ export function resolveConfig(
800813
)
801814
}
802815

803-
resolved.browser ??= {} as any
804816
resolved.browser.enabled ??= false
805817
resolved.browser.headless ??= isCI
806818
resolved.browser.isolate ??= true

packages/vitest/src/node/core.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { ViteDevServer } from 'vite'
55
import type { defineWorkspace } from 'vitest/config'
66
import type { SerializedCoverageConfig } from '../runtime/config'
77
import type { ArgumentsType, ProvidedContext, UserConsoleLog } from '../types/general'
8+
import type { CliOptions } from './cli/cli-api'
89
import type { ProcessPool, WorkspaceSpec } from './pool'
910
import type { TestSpecification } from './spec'
1011
import type { ResolvedConfig, TestProjectConfiguration, UserConfig, VitestRunMode } from './types/config'
@@ -97,7 +98,7 @@ export class Vitest {
9798
resolvedProjects: TestProject[] = []
9899
/** @internal */ _browserLastPort = defaultBrowserPort
99100
/** @internal */ _browserSessions = new BrowserSessions()
100-
/** @internal */ _options: UserConfig = {}
101+
/** @internal */ _cliOptions: CliOptions = {}
101102
/** @internal */ reporters: Reporter[] = []
102103
/** @internal */ vitenode: ViteNodeServer = undefined!
103104
/** @internal */ runner: ViteNodeRunner = undefined!
@@ -118,8 +119,10 @@ export class Vitest {
118119

119120
constructor(
120121
public readonly mode: VitestRunMode,
122+
cliOptions: UserConfig,
121123
options: VitestOptions = {},
122124
) {
125+
this._cliOptions = cliOptions
123126
this.logger = new Logger(this, options.stdout, options.stderr)
124127
this.packageInstaller = options.packageInstaller || new VitestPackageInstaller()
125128
this.specifications = new VitestSpecifications(this)
@@ -192,13 +195,12 @@ export class Vitest {
192195
}
193196

194197
/** @deprecated internal */
195-
setServer(options: UserConfig, server: ViteDevServer, cliOptions: UserConfig): Promise<void> {
196-
return this._setServer(options, server, cliOptions)
198+
setServer(options: UserConfig, server: ViteDevServer): Promise<void> {
199+
return this._setServer(options, server)
197200
}
198201

199202
/** @internal */
200-
async _setServer(options: UserConfig, server: ViteDevServer, cliOptions: UserConfig) {
201-
this._options = options
203+
async _setServer(options: UserConfig, server: ViteDevServer) {
202204
this.watcher.unregisterWatcher()
203205
clearTimeout(this._rerunTimer)
204206
this.restartsCount += 1
@@ -274,7 +276,7 @@ export class Vitest {
274276
}
275277
catch { }
276278

277-
const projects = await this.resolveWorkspace(cliOptions)
279+
const projects = await this.resolveWorkspace(this._cliOptions)
278280
this.resolvedProjects = projects
279281
this.projects = projects
280282

@@ -287,7 +289,7 @@ export class Vitest {
287289
}))
288290
}))
289291

290-
if (options.browser?.enabled) {
292+
if (this._cliOptions.browser?.enabled) {
291293
const browserProjects = this.projects.filter(p => p.config.browser.enabled)
292294
if (!browserProjects.length) {
293295
throw new Error(`Vitest received --browser flag, but no project had a browser configuration.`)
@@ -327,7 +329,7 @@ export class Vitest {
327329
const currentNames = new Set(this.projects.map(p => p.name))
328330
const workspace = await resolveWorkspace(
329331
this,
330-
this._options,
332+
this._cliOptions,
331333
undefined,
332334
Array.isArray(config) ? config : [config],
333335
currentNames,
@@ -1308,7 +1310,7 @@ export class Vitest {
13081310
* Check if the project with a given name should be included.
13091311
*/
13101312
matchesProjectFilter(name: string): boolean {
1311-
const projects = this._config?.project || this._options?.project
1313+
const projects = this._config?.project || this._cliOptions?.project
13121314
// no filters applied, any project can be included
13131315
if (!projects || !projects.length) {
13141316
return true

packages/vitest/src/node/create.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { CliOptions } from './cli/cli-api'
66
import type { VitestOptions } from './core'
77
import type { VitestRunMode } from './types/config'
88
import { resolve } from 'node:path'
9-
import { slash } from '@vitest/utils'
9+
import { deepClone, slash } from '@vitest/utils'
1010
import { findUp } from 'find-up'
1111
import { mergeConfig } from 'vite'
1212
import { configFiles } from '../constants'
@@ -20,7 +20,7 @@ export async function createVitest(
2020
viteOverrides: ViteUserConfig = {},
2121
vitestOptions: VitestOptions = {},
2222
): Promise<Vitest> {
23-
const ctx = new Vitest(mode, vitestOptions)
23+
const ctx = new Vitest(mode, deepClone(options), vitestOptions)
2424
const root = slash(resolve(options.root || process.cwd()))
2525

2626
const configPath
@@ -32,12 +32,14 @@ export async function createVitest(
3232

3333
options.config = configPath
3434

35+
const { browser: _removeBrowser, ...restOptions } = options
36+
3537
const config: ViteInlineConfig = {
3638
configFile: configPath,
3739
configLoader: options.configLoader,
3840
// this will make "mode": "test" | "benchmark" inside defineConfig
3941
mode: options.mode || mode,
40-
plugins: await VitestPlugin(options, ctx),
42+
plugins: await VitestPlugin(restOptions, ctx),
4143
}
4244

4345
const server = await createViteServer(

packages/vitest/src/node/plugins/index.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite'
22
import type { ResolvedConfig, UserConfig } from '../types/config'
33
import {
4+
deepClone,
45
deepMerge,
56
notNullish,
67
toArray,
@@ -27,13 +28,13 @@ import { VitestCoreResolver } from './vitestResolver'
2728

2829
export async function VitestPlugin(
2930
options: UserConfig = {},
30-
ctx: Vitest = new Vitest('test'),
31+
vitest: Vitest = new Vitest('test', deepClone(options)),
3132
): Promise<VitePlugin[]> {
3233
const userConfig = deepMerge({}, options) as UserConfig
3334

3435
async function UIPlugin() {
35-
await ctx.packageInstaller.ensureInstalled('@vitest/ui', options.root || process.cwd(), ctx.version)
36-
return (await import('@vitest/ui')).default(ctx)
36+
await vitest.packageInstaller.ensureInstalled('@vitest/ui', options.root || process.cwd(), vitest.version)
37+
return (await import('@vitest/ui')).default(vitest)
3738
}
3839

3940
return [
@@ -143,13 +144,13 @@ export async function VitestPlugin(
143144
},
144145
}
145146

146-
if (ctx.configOverride.project) {
147+
if (vitest.configOverride.project) {
147148
// project filter was set by the user, so we need to filter the project
148-
options.project = ctx.configOverride.project
149+
options.project = vitest.configOverride.project
149150
}
150151

151152
config.customLogger = createViteLogger(
152-
ctx.logger,
153+
vitest.logger,
153154
viteConfig.logLevel || 'warn',
154155
{
155156
allowClearScreen: false,
@@ -207,7 +208,7 @@ export async function VitestPlugin(
207208
name: string,
208209
filename: string,
209210
) => {
210-
const root = ctx.config.root || options.root || process.cwd()
211+
const root = vitest.config.root || options.root || process.cwd()
211212
return generateScopedClassName(
212213
classNameStrategy,
213214
name,
@@ -258,7 +259,7 @@ export async function VitestPlugin(
258259
})
259260

260261
const originalName = options.name
261-
if (options.browser?.enabled && options.browser?.instances) {
262+
if (options.browser?.instances) {
262263
options.browser.instances.forEach((instance) => {
263264
instance.name ??= originalName ? `${originalName} (${instance.browser})` : instance.browser
264265
})
@@ -274,9 +275,9 @@ export async function VitestPlugin(
274275
console.log('[debug] watcher is ready')
275276
})
276277
}
277-
await ctx._setServer(options, server, userConfig)
278+
await vitest._setServer(options, server)
278279
if (options.api && options.watch) {
279-
(await import('../../api/setup')).setup(ctx)
280+
(await import('../../api/setup')).setup(vitest)
280281
}
281282

282283
// #415, in run mode we don't need the watcher, close it would improve the performance
@@ -287,9 +288,9 @@ export async function VitestPlugin(
287288
},
288289
},
289290
SsrReplacerPlugin(),
290-
...CSSEnablerPlugin(ctx),
291-
CoverageTransform(ctx),
292-
VitestCoreResolver(ctx),
291+
...CSSEnablerPlugin(vitest),
292+
CoverageTransform(vitest),
293+
VitestCoreResolver(vitest),
293294
options.ui ? await UIPlugin() : null,
294295
...MocksPlugins(),
295296
VitestOptimizer(),

packages/vitest/src/node/plugins/publicConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type {
33
UserConfig as ViteUserConfig,
44
} from 'vite'
55
import type { ResolvedConfig, UserConfig } from '../types/config'
6-
import { slash } from '@vitest/utils'
6+
import { deepClone, slash } from '@vitest/utils'
77
import { findUp } from 'find-up'
88
import { resolve } from 'pathe'
99
import { mergeConfig, resolveConfig as resolveViteConfig } from 'vite'
@@ -27,7 +27,7 @@ export async function resolveConfig(
2727
: await findUp(configFiles, { cwd: root } as any)
2828
options.config = configPath
2929

30-
const vitest = new Vitest('test')
30+
const vitest = new Vitest('test', deepClone(options))
3131
const config = await resolveViteConfig(
3232
mergeConfig(
3333
{

packages/vitest/src/node/plugins/workspace.ts

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type { ResolvedConfig, TestProjectInlineConfiguration } from '../types/co
44
import { existsSync, readFileSync } from 'node:fs'
55
import { deepMerge } from '@vitest/utils'
66
import { basename, dirname, relative, resolve } from 'pathe'
7-
import { mergeConfig } from 'vite'
87
import { configDefaults } from '../../defaults'
98
import { generateScopedClassName } from '../../integrations/css/css-modules'
109
import { VitestFilteredOutProjectError } from '../errors'
@@ -112,32 +111,25 @@ export function WorkspaceVitestPlugin(
112111
},
113112
}
114113

115-
// if this project defines a browser configuration, respect --browser flag
116-
// otherwise if we always override the configuration, every project will run in browser mode
117-
if (project.vitest._options.browser && viteConfig.test?.browser) {
118-
viteConfig.test.browser = mergeConfig(
119-
viteConfig.test.browser,
120-
project.vitest._options.browser,
121-
)
122-
}
123-
124-
(config.test as ResolvedConfig).defines = defines
114+
;(config.test as ResolvedConfig).defines = defines
125115

116+
const isUserBrowserEnabled = viteConfig.test?.browser?.enabled
117+
const isBrowserEnabled = isUserBrowserEnabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled)
126118
// keep project names to potentially filter it out
127119
const workspaceNames = [name]
128-
if (viteConfig.test?.browser?.enabled) {
129-
if (viteConfig.test.browser.name && !viteConfig.test.browser.instances?.length) {
130-
const browser = viteConfig.test.browser.name
131-
// vitest injects `instances` in this case later on
132-
workspaceNames.push(name ? `${name} (${browser})` : browser)
133-
}
120+
const browser = viteConfig.test!.browser || {}
121+
if (isBrowserEnabled && browser.name && !browser.instances?.length) {
122+
// vitest injects `instances` in this case later on
123+
workspaceNames.push(name ? `${name} (${browser.name})` : browser.name)
124+
}
134125

135-
viteConfig.test.browser.instances?.forEach((instance) => {
136-
// every instance is a potential project
137-
instance.name ??= name ? `${name} (${instance.browser})` : instance.browser
126+
viteConfig.test?.browser?.instances?.forEach((instance) => {
127+
// every instance is a potential project
128+
instance.name ??= name ? `${name} (${instance.browser})` : instance.browser
129+
if (isBrowserEnabled) {
138130
workspaceNames.push(instance.name)
139-
})
140-
}
131+
}
132+
})
141133

142134
const filters = project.vitest.config.project
143135
// if there is `--project=...` filter, check if any of the potential projects match
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
1-
export default {}
1+
import { defineConfig } from 'vitest/config'
2+
3+
export default defineConfig({
4+
test: {
5+
browser: {
6+
provider: 'playwright',
7+
instances: [{ browser: 'chromium' }],
8+
headless: true,
9+
},
10+
},
11+
})

test/cli/test/public-api.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ it.each([
1111
name: 'running in the browser',
1212
browser: {
1313
enabled: true,
14-
provider: 'playwright',
15-
instances: [{ browser: 'chromium' }],
16-
headless: true,
1714
},
1815
},
1916
] as UserConfig[])('passes down metadata when $name', { timeout: 60_000, retry: 1 }, async (config) => {

test/config/fixtures/bail/vitest.config.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ export default defineConfig({
2929
},
3030
browser: {
3131
headless: true,
32-
provider: 'webdriverio',
33-
instances: [
34-
{ browser: 'chrome' },
35-
],
3632
},
3733
},
3834
})

test/config/fixtures/browser-no-config/vitest.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config';
33
export default defineConfig({
44
test: {
55
browser: {
6-
enabled: false,
6+
headless: true,
77
},
88
},
99
})

0 commit comments

Comments
 (0)