···11import type { ResolvedConfig as ResolvedViteConfig } from 'vite'
22import type { Vitest } from '../core'
33import type { BenchmarkBuiltinReporters } from '../reporters'
44+import type { ResolvedBrowserOptions } from '../types/browser'
45import type {
56 ApiConfig,
67 ResolvedConfig,
···1314import { resolveModule } from 'local-pkg'
1415import { normalize, relative, resolve } from 'pathe'
1516import c from 'tinyrainbow'
1717+import { mergeConfig } from 'vite'
1618import {
1719 defaultBrowserPort,
1820 defaultInspectPort,
···205207 resolved.minWorkers = resolveInlineWorkerOption(resolved.minWorkers)
206208 }
207209208208- resolved.browser ??= {} as any
209209-210210 // run benchmark sequentially by default
211211 resolved.fileParallelism ??= mode !== 'benchmark'
212212···238238 }
239239 }
240240241241+ // apply browser CLI options only if the config already has the browser config and not disabled manually
242242+ if (
243243+ vitest._cliOptions.browser
244244+ && resolved.browser
245245+ // if enabled is set to `false`, but CLI overrides it, then always override it
246246+ && (resolved.browser.enabled !== false || vitest._cliOptions.browser.enabled)
247247+ ) {
248248+ resolved.browser = mergeConfig(
249249+ resolved.browser,
250250+ vitest._cliOptions.browser,
251251+ ) as ResolvedBrowserOptions
252252+ }
253253+254254+ resolved.browser ??= {} as any
241255 const browser = resolved.browser
242256243243- // if browser was enabled via CLI and it's configured by the user, then validate the input
244244- if (browser.enabled && viteConfig.test?.browser) {
257257+ if (browser.enabled) {
245258 if (!browser.name && !browser.instances) {
246259 throw new Error(`Vitest Browser Mode requires "browser.name" (deprecated) or "browser.instances" options, none were set.`)
247260 }
···807820 )
808821 }
809822810810- resolved.browser ??= {} as any
811823 resolved.browser.enabled ??= false
812824 resolved.browser.headless ??= isCI
813825 resolved.browser.isolate ??= true
+14-13
packages/vitest/src/node/plugins/index.ts
···11import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite'
22import type { ResolvedConfig, UserConfig } from '../types/config'
33import {
44+ deepClone,
45 deepMerge,
56 notNullish,
67 toArray,
···27282829export async function VitestPlugin(
2930 options: UserConfig = {},
3030- ctx: Vitest = new Vitest('test'),
3131+ vitest: Vitest = new Vitest('test', deepClone(options)),
3132): Promise<VitePlugin[]> {
3233 const userConfig = deepMerge({}, options) as UserConfig
33343435 async function UIPlugin() {
3535- await ctx.packageInstaller.ensureInstalled('@vitest/ui', options.root || process.cwd(), ctx.version)
3636- return (await import('@vitest/ui')).default(ctx)
3636+ await vitest.packageInstaller.ensureInstalled('@vitest/ui', options.root || process.cwd(), vitest.version)
3737+ return (await import('@vitest/ui')).default(vitest)
3738 }
38393940 return [
···143144 },
144145 }
145146146146- if (ctx.configOverride.project) {
147147+ if (vitest.configOverride.project) {
147148 // project filter was set by the user, so we need to filter the project
148148- options.project = ctx.configOverride.project
149149+ options.project = vitest.configOverride.project
149150 }
150151151152 config.customLogger = createViteLogger(
152152- ctx.logger,
153153+ vitest.logger,
153154 viteConfig.logLevel || 'warn',
154155 {
155156 allowClearScreen: false,
···207208 name: string,
208209 filename: string,
209210 ) => {
210210- const root = ctx.config.root || options.root || process.cwd()
211211+ const root = vitest.config.root || options.root || process.cwd()
211212 return generateScopedClassName(
212213 classNameStrategy,
213214 name,
···258259 })
259260260261 const originalName = options.name
261261- if (options.browser?.enabled && options.browser?.instances) {
262262+ if (options.browser?.instances) {
262263 options.browser.instances.forEach((instance) => {
263264 instance.name ??= originalName ? `${originalName} (${instance.browser})` : instance.browser
264265 })
···274275 console.log('[debug] watcher is ready')
275276 })
276277 }
277277- await ctx._setServer(options, server, userConfig)
278278+ await vitest._setServer(options, server)
278279 if (options.api && options.watch) {
279279- (await import('../../api/setup')).setup(ctx)
280280+ (await import('../../api/setup')).setup(vitest)
280281 }
281282282283 // #415, in run mode we don't need the watcher, close it would improve the performance
···287288 },
288289 },
289290 SsrReplacerPlugin(),
290290- ...CSSEnablerPlugin(ctx),
291291- CoverageTransform(ctx),
292292- VitestCoreResolver(ctx),
291291+ ...CSSEnablerPlugin(vitest),
292292+ CoverageTransform(vitest),
293293+ VitestCoreResolver(vitest),
293294 options.ui ? await UIPlugin() : null,
294295 ...MocksPlugins(),
295296 VitestOptimizer(),
+2-2
packages/vitest/src/node/plugins/publicConfig.ts
···33 UserConfig as ViteUserConfig,
44} from 'vite'
55import type { ResolvedConfig, UserConfig } from '../types/config'
66-import { slash } from '@vitest/utils'
66+import { deepClone, slash } from '@vitest/utils'
77import { findUp } from 'find-up'
88import { resolve } from 'pathe'
99import { mergeConfig, resolveConfig as resolveViteConfig } from 'vite'
···2727 : await findUp(configFiles, { cwd: root } as any)
2828 options.config = configPath
29293030- const vitest = new Vitest('test')
3030+ const vitest = new Vitest('test', deepClone(options))
3131 const config = await resolveViteConfig(
3232 mergeConfig(
3333 {
+15-23
packages/vitest/src/node/plugins/workspace.ts
···44import { existsSync, readFileSync } from 'node:fs'
55import { deepMerge } from '@vitest/utils'
66import { basename, dirname, relative, resolve } from 'pathe'
77-import { mergeConfig } from 'vite'
87import { configDefaults } from '../../defaults'
98import { generateScopedClassName } from '../../integrations/css/css-modules'
109import { VitestFilteredOutProjectError } from '../errors'
···116115 },
117116 }
118117119119- // if this project defines a browser configuration, respect --browser flag
120120- // otherwise if we always override the configuration, every project will run in browser mode
121121- if (project.vitest._options.browser && viteConfig.test?.browser) {
122122- viteConfig.test.browser = mergeConfig(
123123- viteConfig.test.browser,
124124- project.vitest._options.browser,
125125- )
126126- }
118118+ ;(config.test as ResolvedConfig).defines = defines
127119128128- (config.test as ResolvedConfig).defines = defines
129129-120120+ const isUserBrowserEnabled = viteConfig.test?.browser?.enabled
121121+ const isBrowserEnabled = isUserBrowserEnabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled)
130122 // keep project names to potentially filter it out
131123 const workspaceNames = [name]
132132- if (viteConfig.test?.browser?.enabled) {
133133- if (viteConfig.test.browser.name && !viteConfig.test.browser.instances?.length) {
134134- const browser = viteConfig.test.browser.name
135135- // vitest injects `instances` in this case later on
136136- workspaceNames.push(name ? `${name} (${browser})` : browser)
137137- }
138138-139139- viteConfig.test.browser.instances?.forEach((instance) => {
140140- // every instance is a potential project
141141- instance.name ??= name ? `${name} (${instance.browser})` : instance.browser
142142- workspaceNames.push(instance.name)
143143- })
124124+ const browser = viteConfig.test!.browser || {}
125125+ if (isBrowserEnabled && browser.name && !browser.instances?.length) {
126126+ // vitest injects `instances` in this case later on
127127+ workspaceNames.push(name ? `${name} (${browser.name})` : browser.name)
144128 }
129129+130130+ viteConfig.test?.browser?.instances?.forEach((instance) => {
131131+ // every instance is a potential project
132132+ instance.name ??= name ? `${name} (${instance.browser})` : instance.browser
133133+ if (isBrowserEnabled) {
134134+ workspaceNames.push(instance.name)
135135+ }
136136+ })
145137146138 const filters = project.vitest.config.project
147139 // if there is `--project=...` filter, check if any of the potential projects match