Skip to content

Commit

Permalink
feat(coverage): glob based coverage thresholds
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Nov 10, 2023
1 parent 7f50229 commit 03e55b9
Show file tree
Hide file tree
Showing 8 changed files with 262 additions and 128 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@
},
"patchedDependencies": {
"@types/chai@4.3.6": "patches/@types__chai@4.3.6.patch",
"@sinonjs/fake-timers@11.1.0": "patches/@sinonjs__fake-timers@11.1.0.patch"
"@sinonjs/fake-timers@11.1.0": "patches/@sinonjs__fake-timers@11.1.0.patch",
"magicast@0.3.1": "patches/magicast@0.3.1.patch"
}
},
"simple-git-hooks": {
Expand Down
77 changes: 44 additions & 33 deletions packages/coverage-istanbul/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,6 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
provider: 'istanbul',
reportsDirectory: resolve(ctx.config.root, config.reportsDirectory || coverageConfigDefaults.reportsDirectory),
reporter: this.resolveReporters(config.reporter || coverageConfigDefaults.reporter),
lines: config['100'] ? 100 : config.lines,
functions: config['100'] ? 100 : config.functions,
branches: config['100'] ? 100 : config.branches,
statements: config['100'] ? 100 : config.statements,
}

this.instrumenter = createInstrumenter({
Expand Down Expand Up @@ -170,35 +166,8 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
}).execute(context)
}

if (this.options.branches
|| this.options.functions
|| this.options.lines
|| this.options.statements) {
this.checkThresholds({
coverageMap,
thresholds: {
branches: this.options.branches,
functions: this.options.functions,
lines: this.options.lines,
statements: this.options.statements,
},
perFile: this.options.perFile,
})
}

if (this.options.thresholdAutoUpdate && allTestsRun) {
this.updateThresholds({
coverageMap,
thresholds: {
branches: this.options.branches,
functions: this.options.functions,
lines: this.options.lines,
statements: this.options.statements,
},
perFile: this.options.perFile,
configurationFile: this.ctx.server.config.configFile,
})
}
if (this.options.thresholds)
await this.handleThresholds({ coverageMap, allTestsRun, thresholds: this.options.thresholds })
}

async getCoverageMapForUncoveredFiles(coveredFiles: string[]) {
Expand Down Expand Up @@ -234,6 +203,48 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co

return coverageMap.data
}

async handleThresholds(options: { coverageMap: CoverageMap; allTestsRun?: boolean; thresholds: NonNullable<Options['thresholds']> }) {
const resolvedThresholds = this.resolveThresholds({
coverageMap: options.coverageMap,
thresholds: options.thresholds,
createCoverageMap: () => libCoverage.createCoverageMap({}),
})

for (const { thresholds, coverageMap, name } of resolvedThresholds) {
if (thresholds.branches
|| thresholds.functions
|| thresholds.lines
|| thresholds.statements) {
this.checkThresholds({
name,
coverageMap,
perFile: this.options.thresholds?.perFile,
thresholds: {
branches: thresholds.branches,
functions: thresholds.functions,
lines: thresholds.lines,
statements: thresholds.statements,
},
})
}

if (this.options.thresholds?.autoUpdate && options.allTestsRun) {
this.updateThresholds({
name,
coverageMap,
perFile: this.options.thresholds?.perFile,
thresholds: {
branches: thresholds?.branches,
functions: thresholds?.functions,
lines: thresholds?.lines,
statements: thresholds?.statements,
},
configurationFile: this.ctx.server.config.configFile,
})
}
}
}
}

async function mergeAndTransformCoverage(coverages: CoverageMapData[]) {
Expand Down
77 changes: 44 additions & 33 deletions packages/coverage-v8/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,6 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
provider: 'v8',
reporter: this.resolveReporters(config.reporter || coverageConfigDefaults.reporter),
reportsDirectory: resolve(ctx.config.root, config.reportsDirectory || coverageConfigDefaults.reportsDirectory),
lines: config['100'] ? 100 : config.lines,
functions: config['100'] ? 100 : config.functions,
branches: config['100'] ? 100 : config.branches,
statements: config['100'] ? 100 : config.statements,
}

this.testExclude = new _TestExclude({
Expand Down Expand Up @@ -156,35 +152,8 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
}).execute(context)
}

if (this.options.branches
|| this.options.functions
|| this.options.lines
|| this.options.statements) {
this.checkThresholds({
coverageMap,
thresholds: {
branches: this.options.branches,
functions: this.options.functions,
lines: this.options.lines,
statements: this.options.statements,
},
perFile: this.options.perFile,
})
}

if (this.options.thresholdAutoUpdate && allTestsRun) {
this.updateThresholds({
coverageMap,
thresholds: {
branches: this.options.branches,
functions: this.options.functions,
lines: this.options.lines,
statements: this.options.statements,
},
perFile: this.options.perFile,
configurationFile: this.ctx.server.config.configFile,
})
}
if (this.options.thresholds)
await this.handleThresholds({ coverageMap, allTestsRun, thresholds: this.options.thresholds })
}

private async getUntestedFiles(testedFiles: string[]): Promise<Profiler.ScriptCoverage[]> {
Expand Down Expand Up @@ -278,6 +247,48 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
const sourceMapStore = libSourceMaps.createSourceMapStore()
return sourceMapStore.transformCoverage(mergedCoverage)
}

async handleThresholds(options: { coverageMap: CoverageMap; allTestsRun?: boolean; thresholds: NonNullable<Options['thresholds']> }) {
const resolvedThresholds = this.resolveThresholds({
coverageMap: options.coverageMap,
thresholds: options.thresholds,
createCoverageMap: () => libCoverage.createCoverageMap({}),
})

for (const { thresholds, coverageMap, name } of resolvedThresholds) {
if (thresholds.branches
|| thresholds.functions
|| thresholds.lines
|| thresholds.statements) {
this.checkThresholds({
name,
coverageMap,
perFile: options.thresholds.perFile,
thresholds: {
branches: thresholds.branches,
functions: thresholds.functions,
lines: thresholds.lines,
statements: thresholds.statements,
},
})
}

if (this.options.thresholds?.autoUpdate && options.allTestsRun) {
this.updateThresholds({
name,
coverageMap,
perFile: options.thresholds.perFile,
thresholds: {
branches: thresholds?.branches,
functions: thresholds?.functions,
lines: thresholds?.lines,
statements: thresholds?.statements,
},
configurationFile: this.ctx.server.config.configFile,
})
}
}
}
}

function mergeCoverageMaps(...coverageMaps: (CoverageMap | CoverageMapData)[]) {
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
"debug": "^4.3.4",
"local-pkg": "^0.4.3",
"magic-string": "^0.30.5",
"magicast": "^0.3.1",
"pathe": "^1.1.1",
"picocolors": "^1.0.0",
"std-env": "^3.3.3",
Expand Down
88 changes: 44 additions & 44 deletions packages/vitest/src/types/coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,40 +157,27 @@ export interface BaseCoverageOptions {
skipFull?: boolean

/**
* Check thresholds per file.
* See `lines`, `functions`, `branches` and `statements` for the actual thresholds.
* Configurations for thresholds
*
* @default false
*/
perFile?: boolean

/**
* Threshold for lines
*
* @default undefined
*/
lines?: number

/**
* Threshold for functions
*
* @default undefined
*/
functions?: number

/**
* Threshold for branches
* @example
*
* @default undefined
*/
branches?: number

/**
* Threshold for statements
* ```ts
* {
* // Thresholds for all files
* functions: 95,
* branches: 70,
* perFile: true,
* autoUpdate: true,
*
* @default undefined
* // Thresholds for utilities
* 'src/utils/**.ts': {
* lines: 100,
* statements: 95,
* }
* }
* ```
*/
statements?: number
thresholds?: Thresholds | ({ [glob: string]: Pick<Thresholds, 'statements' | 'functions' | 'branches' | 'lines'> } & Thresholds)

/**
* Watermarks for statements, lines, branches and functions.
Expand All @@ -204,13 +191,6 @@ export interface BaseCoverageOptions {
lines?: [number, number]
}

/**
* Update threshold values automatically when current coverage is higher than earlier thresholds
*
* @default false
*/
thresholdAutoUpdate?: boolean

/**
* Generate coverage report even when tests fail.
*
Expand All @@ -224,13 +204,6 @@ export interface BaseCoverageOptions {
* @default false
*/
allowExternal?: boolean

/**
* Shortcut for `{ lines: 100, functions: 100, branches: 100, statements: 100 }`
*
* @default false
*/
100?: boolean
}

export interface CoverageIstanbulOptions extends BaseCoverageOptions {
Expand All @@ -248,3 +221,30 @@ export interface CustomProviderOptions extends Pick<BaseCoverageOptions, FieldsW
/** Name of the module or path to a file to load the custom provider from */
customProviderModule: string
}

interface Thresholds {
/** Set global thresholds to `100` */
100?: boolean

/** Check thresholds per file. */
perFile?: boolean

/**
* Update threshold values automatically when current coverage is higher than earlier thresholds
*
* @default false
*/
autoUpdate?: boolean

/** Thresholds for statements */
statements?: number

/** Thresholds for functions */
functions?: number

/** Thresholds for branches */
branches?: number

/** Thresholds for lines */
lines?: number
}
Loading

0 comments on commit 03e55b9

Please sign in to comment.