@@ -10,13 +10,13 @@ import type { ProcessPool, WorkspaceSpec } from './pool'
1010import type { TestModule } from './reporters/reported-tasks'
1111import type { TestSpecification } from './spec'
1212import type { ResolvedConfig , TestProjectConfiguration , UserConfig , VitestRunMode } from './types/config'
13- import type { CoverageProvider } from './types/coverage'
13+ import type { CoverageProvider , ResolvedCoverageOptions } from './types/coverage'
1414import type { Reporter } from './types/reporter'
1515import type { TestRunResult } from './types/tests'
1616import os from 'node:os'
1717import { getTasks , hasFailed , limitConcurrency } from '@vitest/runner/utils'
1818import { SnapshotManager } from '@vitest/snapshot/manager'
19- import { noop , toArray } from '@vitest/utils'
19+ import { deepClone , deepMerge , noop , toArray } from '@vitest/utils'
2020import { normalize , relative } from 'pathe'
2121import { version } from '../../package.json' with { type : 'json' }
2222import { WebSocketReporter } from '../api/setup'
@@ -94,7 +94,6 @@ export class Vitest {
9494 public readonly watcher : VitestWatcher
9595
9696 /** @internal */ configOverride : Partial < ResolvedConfig > = { }
97- /** @internal */ coverageProvider : CoverageProvider | null | undefined
9897 /** @internal */ filenamePattern ?: string [ ]
9998 /** @internal */ runningPromise ?: Promise < TestRunResult >
10099 /** @internal */ closingPromise ?: Promise < void >
@@ -117,6 +116,7 @@ export class Vitest {
117116 private _state ?: StateManager
118117 private _cache ?: VitestCache
119118 private _snapshot ?: SnapshotManager
119+ private _coverageProvider ?: CoverageProvider | null | undefined
120120
121121 constructor (
122122 public readonly mode : VitestRunMode ,
@@ -209,10 +209,10 @@ export class Vitest {
209209 this . pool = undefined
210210 this . closingPromise = undefined
211211 this . projects = [ ]
212- this . coverageProvider = undefined
213212 this . runningPromise = undefined
214213 this . coreWorkspaceProject = undefined
215214 this . specifications . clearCache ( )
215+ this . _coverageProvider = undefined
216216 this . _onUserTestsRerun = [ ]
217217
218218 this . _vite = server
@@ -312,6 +312,44 @@ export class Vitest {
312312 await Promise . all ( this . _onSetServer . map ( fn => fn ( ) ) )
313313 }
314314
315+ /** @internal */
316+ get coverageProvider ( ) : CoverageProvider | null | undefined {
317+ if ( this . configOverride . coverage ?. enabled === false ) {
318+ return null
319+ }
320+ return this . _coverageProvider
321+ }
322+
323+ public async enableCoverage ( ) : Promise < void > {
324+ this . configOverride . coverage = { } as any
325+ this . configOverride . coverage ! . enabled = true
326+ await this . createCoverageProvider ( )
327+ await this . coverageProvider ?. onEnabled ?.( )
328+ }
329+
330+ public disableCoverage ( ) : void {
331+ this . configOverride . coverage ??= { } as any
332+ this . configOverride . coverage ! . enabled = false
333+ }
334+
335+ private _coverageOverrideCache = new WeakMap < ResolvedCoverageOptions , ResolvedCoverageOptions > ( )
336+
337+ /** @internal */
338+ get _coverageOptions ( ) : ResolvedCoverageOptions {
339+ if ( ! this . configOverride . coverage ) {
340+ return this . config . coverage
341+ }
342+ if ( ! this . _coverageOverrideCache . has ( this . configOverride . coverage ) ) {
343+ const coverage = deepClone ( this . config . coverage )
344+ const options = deepMerge ( coverage , this . configOverride . coverage )
345+ this . _coverageOverrideCache . set (
346+ this . configOverride . coverage ,
347+ options ,
348+ )
349+ }
350+ return this . _coverageOverrideCache . get ( this . configOverride . coverage ) !
351+ }
352+
315353 /**
316354 * Inject new test projects into the workspace.
317355 * @param config Glob, config path or a custom config options.
@@ -399,12 +437,12 @@ export class Vitest {
399437 * Creates a coverage provider if `coverage` is enabled in the config.
400438 */
401439 public async createCoverageProvider ( ) : Promise < CoverageProvider | null > {
402- if ( this . coverageProvider ) {
403- return this . coverageProvider
440+ if ( this . _coverageProvider ) {
441+ return this . _coverageProvider
404442 }
405443 const coverageProvider = await this . initCoverageProvider ( )
406444 if ( coverageProvider ) {
407- await coverageProvider . clean ( this . config . coverage . clean )
445+ await coverageProvider . clean ( this . _coverageOptions . clean )
408446 }
409447 return coverageProvider || null
410448 }
@@ -444,18 +482,21 @@ export class Vitest {
444482 }
445483
446484 private async initCoverageProvider ( ) : Promise < CoverageProvider | null | undefined > {
447- if ( this . coverageProvider !== undefined ) {
485+ if ( this . _coverageProvider != null ) {
448486 return
449487 }
450- this . coverageProvider = await getCoverageProvider (
451- this . config . coverage as unknown as SerializedCoverageConfig ,
488+ const coverageConfig = ( this . configOverride . coverage
489+ ? this . getRootProject ( ) . serializedConfig . coverage
490+ : this . config . coverage ) as unknown as SerializedCoverageConfig
491+ this . _coverageProvider = await getCoverageProvider (
492+ coverageConfig ,
452493 this . runner ,
453494 )
454- if ( this . coverageProvider ) {
455- await this . coverageProvider . initialize ( this )
456- this . config . coverage = this . coverageProvider . resolveOptions ( )
495+ if ( this . _coverageProvider ) {
496+ await this . _coverageProvider . initialize ( this )
497+ this . config . coverage = this . _coverageProvider . resolveOptions ( )
457498 }
458- return this . coverageProvider
499+ return this . _coverageProvider
459500 }
460501
461502 /**
@@ -553,7 +594,7 @@ export class Vitest {
553594 async start ( filters ?: string [ ] ) : Promise < TestRunResult > {
554595 try {
555596 await this . initCoverageProvider ( )
556- await this . coverageProvider ?. clean ( this . config . coverage . clean )
597+ await this . coverageProvider ?. clean ( this . _coverageOptions . clean )
557598 }
558599 finally {
559600 await this . report ( 'onInit' , this )
@@ -602,7 +643,7 @@ export class Vitest {
602643 async init ( ) : Promise < void > {
603644 try {
604645 await this . initCoverageProvider ( )
605- await this . coverageProvider ?. clean ( this . config . coverage . clean )
646+ await this . coverageProvider ?. clean ( this . _coverageOptions . clean )
606647 }
607648 finally {
608649 await this . report ( 'onInit' , this )
@@ -677,7 +718,6 @@ export class Vitest {
677718 * @param allTestsRun Indicates whether all tests were run. This only matters for coverage.
678719 */
679720 public async rerunTestSpecifications ( specifications : TestSpecification [ ] , allTestsRun = false ) : Promise < TestRunResult > {
680- this . configOverride . testNamePattern = undefined
681721 const files = specifications . map ( spec => spec . moduleId )
682722 await Promise . all ( [
683723 this . report ( 'onWatcherRerun' , files , 'rerun test' ) ,
@@ -709,7 +749,7 @@ export class Vitest {
709749 this . snapshot . clear ( )
710750 this . state . clearErrors ( )
711751
712- if ( ! this . isFirstRun && this . config . coverage . cleanOnRerun ) {
752+ if ( ! this . isFirstRun && this . _coverageOptions . cleanOnRerun ) {
713753 await this . coverageProvider ?. clean ( )
714754 }
715755
@@ -1111,7 +1151,7 @@ export class Vitest {
11111151 if ( this . state . getCountOfFailedTests ( ) > 0 ) {
11121152 await this . coverageProvider ?. onTestFailure ?.( )
11131153
1114- if ( ! this . config . coverage . reportOnFailure ) {
1154+ if ( ! this . _coverageOptions . reportOnFailure ) {
11151155 return
11161156 }
11171157 }
0 commit comments