[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix: apply browser CLI options only if the project has the browser set in the config already (#7984)

authored by

Vladimir and committed by
GitHub
(May 19, 2025, 5:56 PM +0200) 703581b6 330f9a40

+363 -187
+21 -18
test/coverage-test/utils.ts
··· 35 35 config: 'fixtures/configs/vitest.config.ts', 36 36 pool: 'threads', 37 37 ...config, 38 - env: { 39 - COVERAGE_TEST: 'true', 40 - ...config.env, 41 - }, 42 - coverage: { 43 - enabled: true, 44 - reporter: [], 45 - ...config.coverage, 46 - provider: provider === 'v8-ast-aware' ? 'v8' : provider, 47 - experimentalAstAwareRemapping: provider === 'v8-ast-aware', 48 - customProviderModule: provider === 'custom' ? 'fixtures/custom-provider' : undefined, 49 - }, 50 - browser: { 51 - enabled: process.env.COVERAGE_BROWSER === 'true', 52 - headless: true, 53 - instances: [{ browser: 'chromium' }], 54 - provider: 'playwright', 55 - ...config.browser, 38 + browser: config.browser, 39 + }, [], 'test', { 40 + test: { 41 + env: { 42 + COVERAGE_TEST: 'true', 43 + ...config.env, 44 + }, 45 + coverage: { 46 + enabled: true, 47 + reporter: [], 48 + ...config.coverage, 49 + provider: provider === 'v8-ast-aware' ? 'v8' : provider, 50 + experimentalAstAwareRemapping: provider === 'v8-ast-aware', 51 + customProviderModule: provider === 'custom' ? 'fixtures/custom-provider' : undefined, 52 + }, 53 + browser: { 54 + enabled: process.env.COVERAGE_BROWSER === 'true', 55 + headless: true, 56 + instances: [{ browser: 'chromium' }], 57 + provider: 'playwright', 58 + }, 56 59 }, 57 60 }) 58 61
+2 -2
test/test-utils/index.ts
··· 27 27 } 28 28 29 29 export async function runVitest( 30 - config: UserConfig, 30 + cliOptions: UserConfig, 31 31 cliFilters: string[] = [], 32 32 mode: VitestRunMode = 'test', 33 33 viteOverrides: ViteUserConfig = {}, ··· 72 72 let ctx: Vitest | undefined 73 73 let thrown = false 74 74 try { 75 - const { reporters, ...rest } = config 75 + const { reporters, ...rest } = cliOptions 76 76 77 77 ctx = await startVitest(mode, cliFilters, { 78 78 watch: false,
-3
test/cli/test/public-api.test.ts
··· 11 11 name: 'running in the browser', 12 12 browser: { 13 13 enabled: true, 14 - provider: 'playwright', 15 - instances: [{ browser: 'chromium' }], 16 - headless: true, 17 14 }, 18 15 }, 19 16 ] as UserConfig[])('passes down metadata when $name', { timeout: 60_000, retry: 1 }, async (config) => {
+8 -3
test/config/test/bail.test.ts
··· 16 16 browser: { 17 17 enabled: true, 18 18 provider: 'playwright', 19 + headless: true, 19 20 fileParallelism: false, 20 21 instances: [ 21 22 { browser: 'chromium' }, ··· 26 27 browser: { 27 28 enabled: true, 28 29 provider: 'playwright', 30 + headless: true, 29 31 fileParallelism: true, 30 32 instances: [ 31 33 { browser: 'chromium' }, ··· 72 74 const { exitCode, stdout, ctx } = await runVitest({ 73 75 root: './fixtures/bail', 74 76 bail: 1, 75 - ...config, 76 - env: { 77 - THREADS: process.env.THREADS, 77 + }, [], 'test', { 78 + test: { 79 + ...config, 80 + env: { 81 + THREADS: process.env.THREADS, 82 + }, 78 83 }, 79 84 }) 80 85
+167 -25
test/config/test/browser-configs.test.ts
··· 14 14 } 15 15 16 16 test('assigns names as browsers', async () => { 17 - const { projects } = await vitest({ 17 + const { projects } = await vitest({}, { 18 18 browser: { 19 19 enabled: true, 20 20 headless: true, ··· 33 33 }) 34 34 35 35 test('filters projects', async () => { 36 - const { projects } = await vitest({ 37 - project: 'chromium', 36 + const { projects } = await vitest({ project: 'chromium' }, { 38 37 browser: { 39 38 enabled: true, 40 39 instances: [ ··· 50 49 }) 51 50 52 51 test('filters projects with a wildcard', async () => { 53 - const { projects } = await vitest({ 54 - project: 'chrom*', 52 + const { projects } = await vitest({ project: 'chrom*' }, { 55 53 browser: { 56 54 enabled: true, 57 55 instances: [ ··· 67 65 }) 68 66 69 67 test('assignes names as browsers in a custom project', async () => { 70 - const { projects } = await vitest({ 68 + const { projects } = await vitest({}, { 71 69 projects: [ 72 70 { 73 71 test: { ··· 95 93 }) 96 94 97 95 test('inherits browser options', async () => { 98 - const { projects } = await vitest({ 96 + const { projects } = await vitest({}, { 99 97 setupFiles: ['/test/setup.ts'], 100 98 provide: { 101 99 browser: true, ··· 183 181 }) 184 182 185 183 test('coverage provider v8 works correctly in browser mode if instances are filtered', async () => { 186 - const { projects } = await vitest({ 187 - project: 'chromium', 188 - coverage: { 189 - enabled: true, 190 - provider: 'v8', 184 + const { projects } = await vitest( 185 + { 186 + project: 'chromium', 191 187 }, 192 - browser: { 193 - enabled: true, 194 - provider: 'playwright', 195 - instances: [ 196 - { browser: 'chromium' }, 197 - { browser: 'firefox' }, 198 - { browser: 'webkit' }, 199 - ], 188 + { 189 + coverage: { 190 + enabled: true, 191 + provider: 'v8', 192 + }, 193 + browser: { 194 + enabled: true, 195 + provider: 'playwright', 196 + instances: [ 197 + { browser: 'chromium' }, 198 + { browser: 'firefox' }, 199 + { browser: 'webkit' }, 200 + ], 201 + }, 200 202 }, 201 - }) 203 + ) 202 204 expect(projects.map(p => p.name)).toEqual([ 203 205 'chromium', 204 206 ]) 205 207 }) 206 208 207 209 test('coverage provider v8 works correctly in workspaced browser mode if instances are filtered', async () => { 208 - const { projects } = await vitest({ 209 - project: 'browser (chromium)', 210 + const { projects } = await vitest({ project: 'browser (chromium)' }, { 210 211 projects: [ 211 212 { 212 213 test: { ··· 234 235 }) 235 236 236 237 test('filter for the global browser project includes all browser instances', async () => { 237 - const { projects } = await vitest({ 238 - project: 'myproject', 238 + const { projects } = await vitest({ project: 'myproject' }, { 239 239 projects: [ 240 240 { 241 241 test: { ··· 532 532 browser: true, 533 533 ui: true, 534 534 }, 535 + }) 536 + }) 537 + 538 + test('correctly resolves extended project', async () => { 539 + const { stdout } = await getCliConfig({ 540 + browser: { 541 + provider: 'playwright', 542 + headless: true, 543 + instances: [ 544 + { browser: 'chromium' }, 545 + ], 546 + }, 547 + projects: [ 548 + { 549 + extends: true, 550 + test: { 551 + name: 'node', 552 + }, 553 + }, 554 + { 555 + extends: true, 556 + test: { 557 + name: 'browser', 558 + browser: { 559 + enabled: true, 560 + }, 561 + }, 562 + }, 563 + ], 564 + }, []) 565 + 566 + const config = JSON.parse(stdout) 567 + 568 + expect(config.workspace).toHaveLength(2) 569 + expect(config.workspace[0]).toEqual({ 570 + name: 'node', 571 + headless: true, 572 + browser: false, 573 + ui: false, 574 + parent: null, 575 + }) 576 + 577 + expect(config.workspace[1]).toEqual({ 578 + name: 'browser (chromium)', 579 + headless: true, 580 + browser: true, 581 + ui: false, 582 + parent: { 583 + name: 'browser', 584 + headless: true, 585 + browser: true, 586 + ui: false, 587 + }, 588 + }) 589 + }) 590 + 591 + test('correctly overrides extended project', async () => { 592 + const { stdout } = await getCliConfig({ 593 + browser: { 594 + provider: 'playwright', 595 + headless: true, 596 + instances: [ 597 + { browser: 'chromium' }, 598 + ], 599 + }, 600 + projects: [ 601 + { 602 + extends: true, 603 + test: { 604 + name: 'node', 605 + }, 606 + }, 607 + { 608 + extends: true, 609 + test: { 610 + name: 'browser', 611 + browser: { 612 + enabled: true, 613 + }, 614 + }, 615 + }, 616 + ], 617 + }, ['--browser.headless=false']) 618 + 619 + const config = JSON.parse(stdout) 620 + 621 + expect(config.workspace).toHaveLength(2) 622 + expect(config.workspace[0]).toEqual({ 623 + name: 'node', 624 + headless: false, 625 + browser: false, 626 + ui: true, 627 + parent: null, 628 + }) 629 + 630 + expect(config.workspace[1]).toEqual({ 631 + name: 'browser (chromium)', 632 + headless: false, 633 + browser: true, 634 + ui: true, 635 + parent: { 636 + name: 'browser', 637 + headless: false, 638 + browser: true, 639 + ui: true, 640 + }, 641 + }) 642 + }) 643 + 644 + test('CLI options override the config if --browser.enabled is passed down manually', async () => { 645 + const { stdout } = await getCliConfig({ 646 + browser: { 647 + enabled: false, 648 + provider: 'playwright', 649 + headless: true, 650 + instances: [ 651 + { browser: 'chromium' }, 652 + ], 653 + }, 654 + }, ['--browser.headless=false', '--browser.enabled']) 655 + 656 + const config = JSON.parse(stdout) 657 + expect(config).toEqual({ 658 + browser: { 659 + headless: false, 660 + browser: true, 661 + ui: true, 662 + }, 663 + workspace: [ 664 + { 665 + name: 'chromium', 666 + headless: false, 667 + browser: true, 668 + ui: true, 669 + parent: { 670 + name: '', 671 + headless: false, 672 + browser: true, 673 + ui: true, 674 + }, 675 + }, 676 + ], 535 677 }) 536 678 }) 537 679 })
+81 -67
test/config/test/failures.test.ts
··· 90 90 continue 91 91 } 92 92 93 - const { stderr } = await runVitest({ 94 - [option]: true, 95 - fileParallelism: false, 96 - browser: { 97 - enabled: true, 98 - provider, 99 - name, 93 + const { stderr } = await runVitest({}, { 94 + test: { 95 + [option]: true, 96 + fileParallelism: false, 97 + browser: { 98 + enabled: true, 99 + provider, 100 + name, 101 + }, 100 102 }, 101 103 }) 102 104 ··· 134 136 continue 135 137 } 136 138 137 - const { stderr } = await runVitest({ 138 - coverage: { 139 - enabled: true, 140 - }, 141 - browser: { 142 - enabled: true, 143 - provider, 144 - name, 139 + const { stderr } = await runVitest({}, { 140 + test: { 141 + coverage: { 142 + enabled: true, 143 + }, 144 + browser: { 145 + enabled: true, 146 + provider, 147 + name, 148 + }, 145 149 }, 146 150 }) 147 151 ··· 183 187 continue 184 188 } 185 189 186 - const { stderr } = await runVitest({ 187 - coverage: { 188 - enabled: true, 189 - }, 190 - browser: { 191 - enabled: true, 192 - provider, 193 - instances: [{ browser: name }], 190 + const { stderr } = await runVitest({}, { 191 + test: { 192 + coverage: { 193 + enabled: true, 194 + }, 195 + browser: { 196 + enabled: true, 197 + provider, 198 + instances: [{ browser: name }], 199 + }, 194 200 }, 195 201 }) 196 202 ··· 231 237 }) 232 238 233 239 test('v8 coverage provider throws when using chromium and other non-chromium browser', async () => { 234 - const { stderr } = await runVitest({ 235 - coverage: { 236 - enabled: true, 237 - }, 238 - browser: { 239 - enabled: true, 240 - headless: true, 241 - provider: 'playwright', 242 - instances: [ 243 - { browser: 'chromium' }, 244 - { browser: 'firefox' }, 245 - { browser: 'webkit' }, 246 - ], 240 + const { stderr } = await runVitest({}, { 241 + test: { 242 + coverage: { 243 + enabled: true, 244 + }, 245 + browser: { 246 + enabled: true, 247 + headless: true, 248 + provider: 'playwright', 249 + instances: [ 250 + { browser: 'chromium' }, 251 + { browser: 'firefox' }, 252 + { browser: 'webkit' }, 253 + ], 254 + }, 247 255 }, 248 256 }) 249 257 ··· 480 488 }) 481 489 482 490 test('browser.instances throws an error if no custom name is provided, but the config name is inherited', async () => { 483 - const { stderr } = await runVitest({ 484 - name: 'custom', 485 - browser: { 486 - enabled: true, 487 - provider: 'playwright', 488 - instances: [ 489 - { browser: 'firefox' }, 490 - { browser: 'firefox' }, 491 - ], 491 + const { stderr } = await runVitest({}, { 492 + test: { 493 + name: 'custom', 494 + browser: { 495 + enabled: true, 496 + provider: 'playwright', 497 + instances: [ 498 + { browser: 'firefox' }, 499 + { browser: 'firefox' }, 500 + ], 501 + }, 492 502 }, 493 503 }) 494 504 expect(stderr).toMatch('Cannot define a nested project for a firefox browser. The project name "custom (firefox)" was already defined. If you have multiple instances for the same browser, make sure to define a custom "name". All projects should have unique names. Make sure your configuration is correct.') 495 505 }) 496 506 497 507 test('throws an error if name conflicts with a workspace name', async () => { 498 - const { stderr } = await runVitest({ 499 - projects: [ 500 - { test: { name: '1 (firefox)' } }, 501 - { 502 - test: { 503 - browser: { 504 - enabled: true, 505 - provider: 'playwright', 506 - instances: [ 507 - { browser: 'firefox' }, 508 - ], 508 + const { stderr } = await runVitest({}, { 509 + test: { 510 + projects: [ 511 + { test: { name: '1 (firefox)' } }, 512 + { 513 + test: { 514 + browser: { 515 + enabled: true, 516 + provider: 'playwright', 517 + instances: [ 518 + { browser: 'firefox' }, 519 + ], 520 + }, 509 521 }, 510 522 }, 511 - }, 512 - ], 523 + ], 524 + }, 513 525 }) 514 526 expect(stderr).toMatch('Cannot define a nested project for a firefox browser. The project name "1 (firefox)" was already defined. If you have multiple instances for the same browser, make sure to define a custom "name". All projects should have unique names. Make sure your configuration is correct.') 515 527 }) 516 528 517 529 test('throws an error if several browsers are headed in nonTTY mode', async () => { 518 - const { stderr } = await runVitest({ 519 - browser: { 520 - enabled: true, 521 - provider: 'playwright', 522 - headless: false, 523 - instances: [ 524 - { browser: 'chromium' }, 525 - { browser: 'firefox' }, 526 - ], 530 + const { stderr } = await runVitest({}, { 531 + test: { 532 + browser: { 533 + enabled: true, 534 + provider: 'playwright', 535 + headless: false, 536 + instances: [ 537 + { browser: 'chromium' }, 538 + { browser: 'firefox' }, 539 + ], 540 + }, 527 541 }, 528 542 }) 529 543 expect(stderr).toContain('Found multiple projects that run browser tests in headed mode: "chromium", "firefox"')
+7 -3
test/core/test/cli-test.test.ts
··· 270 270 const { options, args } = parseArguments('--browser=firefox', false) 271 271 272 272 expect(args).toEqual([]) 273 - expect(options).toEqual({ browser: { enabled: true, name: 'firefox' } }) 273 + expect(options).toEqual({ browser: { name: 'firefox' } }) 274 274 }) 275 275 276 276 test('clearScreen', async () => { ··· 293 293 clearScreen: viteClearScreen, 294 294 } 295 295 const vitestConfig = getCLIOptions(vitestClearScreen) 296 - const config = resolveConfig({ logger: undefined, mode: 'test' } as any, vitestConfig, viteConfig) 296 + const config = resolveConfig({ 297 + logger: undefined, 298 + mode: 'test', 299 + _cliOptions: {}, 300 + } as any, vitestConfig, viteConfig) 297 301 return config.clearScreen 298 302 }) 299 303 expect(results).toMatchInlineSnapshot(` ··· 374 378 filter: [], 375 379 options: { 376 380 'coverage': { enabled: true }, 377 - 'browser': { enabled: true, name: 'chrome' }, 381 + 'browser': { name: 'chrome' }, 378 382 '--': [], 379 383 'color': true, 380 384 },
+11 -9
packages/vitest/src/node/core.ts
··· 5 5 import type { defineWorkspace } from 'vitest/config' 6 6 import type { SerializedCoverageConfig } from '../runtime/config' 7 7 import type { ArgumentsType, ProvidedContext, UserConsoleLog } from '../types/general' 8 + import type { CliOptions } from './cli/cli-api' 8 9 import type { ProcessPool, WorkspaceSpec } from './pool' 9 10 import type { TestSpecification } from './spec' 10 11 import type { ResolvedConfig, TestProjectConfiguration, UserConfig, VitestRunMode } from './types/config' ··· 97 98 resolvedProjects: TestProject[] = [] 98 99 /** @internal */ _browserLastPort = defaultBrowserPort 99 100 /** @internal */ _browserSessions = new BrowserSessions() 100 - /** @internal */ _options: UserConfig = {} 101 + /** @internal */ _cliOptions: CliOptions = {} 101 102 /** @internal */ reporters: Reporter[] = [] 102 103 /** @internal */ vitenode: ViteNodeServer = undefined! 103 104 /** @internal */ runner: ViteNodeRunner = undefined! ··· 118 119 119 120 constructor( 120 121 public readonly mode: VitestRunMode, 122 + cliOptions: UserConfig, 121 123 options: VitestOptions = {}, 122 124 ) { 125 + this._cliOptions = cliOptions 123 126 this.logger = new Logger(this, options.stdout, options.stderr) 124 127 this.packageInstaller = options.packageInstaller || new VitestPackageInstaller() 125 128 this.specifications = new VitestSpecifications(this) ··· 192 195 } 193 196 194 197 /** @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) 197 200 } 198 201 199 202 /** @internal */ 200 - async _setServer(options: UserConfig, server: ViteDevServer, cliOptions: UserConfig) { 201 - this._options = options 203 + async _setServer(options: UserConfig, server: ViteDevServer) { 202 204 this.watcher.unregisterWatcher() 203 205 clearTimeout(this._rerunTimer) 204 206 this.restartsCount += 1 ··· 274 276 } 275 277 catch { } 276 278 277 - const projects = await this.resolveProjects(cliOptions) 279 + const projects = await this.resolveProjects(this._cliOptions) 278 280 this.resolvedProjects = projects 279 281 this.projects = projects 280 282 ··· 287 289 })) 288 290 })) 289 291 290 - if (options.browser?.enabled) { 292 + if (this._cliOptions.browser?.enabled) { 291 293 const browserProjects = this.projects.filter(p => p.config.browser.enabled) 292 294 if (!browserProjects.length) { 293 295 throw new Error(`Vitest received --browser flag, but no project had a browser configuration.`) ··· 327 329 const currentNames = new Set(this.projects.map(p => p.name)) 328 330 const projects = await resolveProjects( 329 331 this, 330 - this._options, 332 + this._cliOptions, 331 333 undefined, 332 334 Array.isArray(config) ? config : [config], 333 335 currentNames, ··· 1335 1337 * Check if the project with a given name should be included. 1336 1338 */ 1337 1339 matchesProjectFilter(name: string): boolean { 1338 - const projects = this._config?.project || this._options?.project 1340 + const projects = this._config?.project || this._cliOptions?.project 1339 1341 // no filters applied, any project can be included 1340 1342 if (!projects || !projects.length) { 1341 1343 return true
+5 -3
packages/vitest/src/node/create.ts
··· 6 6 import type { VitestOptions } from './core' 7 7 import type { VitestRunMode } from './types/config' 8 8 import { resolve } from 'node:path' 9 - import { slash } from '@vitest/utils' 9 + import { deepClone, slash } from '@vitest/utils' 10 10 import { findUp } from 'find-up' 11 11 import { mergeConfig } from 'vite' 12 12 import { configFiles } from '../constants' ··· 20 20 viteOverrides: ViteUserConfig = {}, 21 21 vitestOptions: VitestOptions = {}, 22 22 ): Promise<Vitest> { 23 - const ctx = new Vitest(mode, vitestOptions) 23 + const ctx = new Vitest(mode, deepClone(options), vitestOptions) 24 24 const root = slash(resolve(options.root || process.cwd())) 25 25 26 26 const configPath ··· 32 32 33 33 options.config = configPath 34 34 35 + const { browser: _removeBrowser, ...restOptions } = options 36 + 35 37 const config: ViteInlineConfig = { 36 38 configFile: configPath, 37 39 configLoader: options.configLoader, 38 40 // this will make "mode": "test" | "benchmark" inside defineConfig 39 41 mode: options.mode || mode, 40 - plugins: await VitestPlugin(options, ctx), 42 + plugins: await VitestPlugin(restOptions, ctx), 41 43 } 42 44 43 45 const server = await createViteServer(
+11 -1
test/cli/fixtures/public-api/vitest.config.ts
··· 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 + })
-4
test/config/fixtures/bail/vitest.config.ts
··· 29 29 }, 30 30 browser: { 31 31 headless: true, 32 - provider: 'webdriverio', 33 - instances: [ 34 - { browser: 'chrome' }, 35 - ], 36 32 }, 37 33 }, 38 34 })
+1 -1
test/config/fixtures/browser-no-config/vitest.config.ts
··· 3 3 export default defineConfig({ 4 4 test: { 5 5 browser: { 6 - enabled: false, 6 + headless: true, 7 7 }, 8 8 }, 9 9 })
-4
packages/vitest/src/node/cli/cac.ts
··· 283 283 argv.includeTaskLocation ??= true 284 284 } 285 285 286 - // running "vitest --browser.headless" 287 - if (typeof argv.browser === 'object' && !('enabled' in argv.browser)) { 288 - argv.browser.enabled = true 289 - } 290 286 if (typeof argv.typecheck?.only === 'boolean') { 291 287 argv.typecheck.enabled ??= true 292 288 }
+1 -1
packages/vitest/src/node/cli/cli-config.ts
··· 362 362 return { enabled: browser === 'yes' } 363 363 } 364 364 if (typeof browser === 'string') { 365 - return { enabled: true, name: browser } 365 + return { name: browser } 366 366 } 367 367 return browser 368 368 },
+17 -5
packages/vitest/src/node/config/resolveConfig.ts
··· 1 1 import type { ResolvedConfig as ResolvedViteConfig } from 'vite' 2 2 import type { Vitest } from '../core' 3 3 import type { BenchmarkBuiltinReporters } from '../reporters' 4 + import type { ResolvedBrowserOptions } from '../types/browser' 4 5 import type { 5 6 ApiConfig, 6 7 ResolvedConfig, ··· 13 14 import { resolveModule } from 'local-pkg' 14 15 import { normalize, relative, resolve } from 'pathe' 15 16 import c from 'tinyrainbow' 17 + import { mergeConfig } from 'vite' 16 18 import { 17 19 defaultBrowserPort, 18 20 defaultInspectPort, ··· 205 207 resolved.minWorkers = resolveInlineWorkerOption(resolved.minWorkers) 206 208 } 207 209 208 - resolved.browser ??= {} as any 209 - 210 210 // run benchmark sequentially by default 211 211 resolved.fileParallelism ??= mode !== 'benchmark' 212 212 ··· 238 238 } 239 239 } 240 240 241 + // apply browser CLI options only if the config already has the browser config and not disabled manually 242 + if ( 243 + vitest._cliOptions.browser 244 + && resolved.browser 245 + // if enabled is set to `false`, but CLI overrides it, then always override it 246 + && (resolved.browser.enabled !== false || vitest._cliOptions.browser.enabled) 247 + ) { 248 + resolved.browser = mergeConfig( 249 + resolved.browser, 250 + vitest._cliOptions.browser, 251 + ) as ResolvedBrowserOptions 252 + } 253 + 254 + resolved.browser ??= {} as any 241 255 const browser = resolved.browser 242 256 243 - // if browser was enabled via CLI and it's configured by the user, then validate the input 244 - if (browser.enabled && viteConfig.test?.browser) { 257 + if (browser.enabled) { 245 258 if (!browser.name && !browser.instances) { 246 259 throw new Error(`Vitest Browser Mode requires "browser.name" (deprecated) or "browser.instances" options, none were set.`) 247 260 } ··· 807 820 ) 808 821 } 809 822 810 - resolved.browser ??= {} as any 811 823 resolved.browser.enabled ??= false 812 824 resolved.browser.headless ??= isCI 813 825 resolved.browser.isolate ??= true
+14 -13
packages/vitest/src/node/plugins/index.ts
··· 1 1 import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite' 2 2 import type { ResolvedConfig, UserConfig } from '../types/config' 3 3 import { 4 + deepClone, 4 5 deepMerge, 5 6 notNullish, 6 7 toArray, ··· 27 28 28 29 export async function VitestPlugin( 29 30 options: UserConfig = {}, 30 - ctx: Vitest = new Vitest('test'), 31 + vitest: Vitest = new Vitest('test', deepClone(options)), 31 32 ): Promise<VitePlugin[]> { 32 33 const userConfig = deepMerge({}, options) as UserConfig 33 34 34 35 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) 37 38 } 38 39 39 40 return [ ··· 143 144 }, 144 145 } 145 146 146 - if (ctx.configOverride.project) { 147 + if (vitest.configOverride.project) { 147 148 // 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 149 150 } 150 151 151 152 config.customLogger = createViteLogger( 152 - ctx.logger, 153 + vitest.logger, 153 154 viteConfig.logLevel || 'warn', 154 155 { 155 156 allowClearScreen: false, ··· 207 208 name: string, 208 209 filename: string, 209 210 ) => { 210 - const root = ctx.config.root || options.root || process.cwd() 211 + const root = vitest.config.root || options.root || process.cwd() 211 212 return generateScopedClassName( 212 213 classNameStrategy, 213 214 name, ··· 258 259 }) 259 260 260 261 const originalName = options.name 261 - if (options.browser?.enabled && options.browser?.instances) { 262 + if (options.browser?.instances) { 262 263 options.browser.instances.forEach((instance) => { 263 264 instance.name ??= originalName ? `${originalName} (${instance.browser})` : instance.browser 264 265 }) ··· 274 275 console.log('[debug] watcher is ready') 275 276 }) 276 277 } 277 - await ctx._setServer(options, server, userConfig) 278 + await vitest._setServer(options, server) 278 279 if (options.api && options.watch) { 279 - (await import('../../api/setup')).setup(ctx) 280 + (await import('../../api/setup')).setup(vitest) 280 281 } 281 282 282 283 // #415, in run mode we don't need the watcher, close it would improve the performance ··· 287 288 }, 288 289 }, 289 290 SsrReplacerPlugin(), 290 - ...CSSEnablerPlugin(ctx), 291 - CoverageTransform(ctx), 292 - VitestCoreResolver(ctx), 291 + ...CSSEnablerPlugin(vitest), 292 + CoverageTransform(vitest), 293 + VitestCoreResolver(vitest), 293 294 options.ui ? await UIPlugin() : null, 294 295 ...MocksPlugins(), 295 296 VitestOptimizer(),
+2 -2
packages/vitest/src/node/plugins/publicConfig.ts
··· 3 3 UserConfig as ViteUserConfig, 4 4 } from 'vite' 5 5 import type { ResolvedConfig, UserConfig } from '../types/config' 6 - import { slash } from '@vitest/utils' 6 + import { deepClone, slash } from '@vitest/utils' 7 7 import { findUp } from 'find-up' 8 8 import { resolve } from 'pathe' 9 9 import { mergeConfig, resolveConfig as resolveViteConfig } from 'vite' ··· 27 27 : await findUp(configFiles, { cwd: root } as any) 28 28 options.config = configPath 29 29 30 - const vitest = new Vitest('test') 30 + const vitest = new Vitest('test', deepClone(options)) 31 31 const config = await resolveViteConfig( 32 32 mergeConfig( 33 33 {
+15 -23
packages/vitest/src/node/plugins/workspace.ts
··· 4 4 import { existsSync, readFileSync } from 'node:fs' 5 5 import { deepMerge } from '@vitest/utils' 6 6 import { basename, dirname, relative, resolve } from 'pathe' 7 - import { mergeConfig } from 'vite' 8 7 import { configDefaults } from '../../defaults' 9 8 import { generateScopedClassName } from '../../integrations/css/css-modules' 10 9 import { VitestFilteredOutProjectError } from '../errors' ··· 116 115 }, 117 116 } 118 117 119 - // if this project defines a browser configuration, respect --browser flag 120 - // otherwise if we always override the configuration, every project will run in browser mode 121 - if (project.vitest._options.browser && viteConfig.test?.browser) { 122 - viteConfig.test.browser = mergeConfig( 123 - viteConfig.test.browser, 124 - project.vitest._options.browser, 125 - ) 126 - } 118 + ;(config.test as ResolvedConfig).defines = defines 127 119 128 - (config.test as ResolvedConfig).defines = defines 129 - 120 + const isUserBrowserEnabled = viteConfig.test?.browser?.enabled 121 + const isBrowserEnabled = isUserBrowserEnabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled) 130 122 // keep project names to potentially filter it out 131 123 const workspaceNames = [name] 132 - if (viteConfig.test?.browser?.enabled) { 133 - if (viteConfig.test.browser.name && !viteConfig.test.browser.instances?.length) { 134 - const browser = viteConfig.test.browser.name 135 - // vitest injects `instances` in this case later on 136 - workspaceNames.push(name ? `${name} (${browser})` : browser) 137 - } 138 - 139 - viteConfig.test.browser.instances?.forEach((instance) => { 140 - // every instance is a potential project 141 - instance.name ??= name ? `${name} (${instance.browser})` : instance.browser 142 - workspaceNames.push(instance.name) 143 - }) 124 + const browser = viteConfig.test!.browser || {} 125 + if (isBrowserEnabled && browser.name && !browser.instances?.length) { 126 + // vitest injects `instances` in this case later on 127 + workspaceNames.push(name ? `${name} (${browser.name})` : browser.name) 144 128 } 129 + 130 + viteConfig.test?.browser?.instances?.forEach((instance) => { 131 + // every instance is a potential project 132 + instance.name ??= name ? `${name} (${instance.browser})` : instance.browser 133 + if (isBrowserEnabled) { 134 + workspaceNames.push(instance.name) 135 + } 136 + }) 145 137 146 138 const filters = project.vitest.config.project 147 139 // if there is `--project=...` filter, check if any of the potential projects match