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) => {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy