Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1450,14 +1450,31 @@ Check thresholds per file.

##### coverage.thresholds.autoUpdate

- **Type:** `boolean`
- **Type:** `boolean | function`
- **Default:** `false`
- **Available for providers:** `'v8' | 'istanbul'`
- **CLI:** `--coverage.thresholds.autoUpdate=<boolean>`

Update all threshold values `lines`, `functions`, `branches` and `statements` to configuration file when current coverage is better than the configured thresholds.
This option helps to maintain thresholds when coverage is improved.

You can also pass a function for formatting the updated threshold values:

<!-- eslint-skip -->
```ts
{
coverage: {
thresholds: {
// Update thresholds without decimals
autoUpdate: (newThreshold) => Math.floor(newThreshold),

// 95.85 -> 95
functions: 95,
}
}
}
```

##### coverage.thresholds.100

- **Type:** `boolean`
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/cli-generated.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ Check thresholds per file. See `--coverage.thresholds.lines`, `--coverage.thresh

### coverage.thresholds.autoUpdate

- **CLI:** `--coverage.thresholds.autoUpdate`
- **CLI:** `--coverage.thresholds.autoUpdate <boolean|function>`
- **Config:** [coverage.thresholds.autoUpdate](/config/#coverage-thresholds-autoupdate)

Update threshold values: "lines", "functions", "branches" and "statements" to configuration file when current coverage is above the configured thresholds (default: `false`)
Expand Down
11 changes: 11 additions & 0 deletions packages/vitest/src/node/cli/cli-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,17 @@ export const cliOptionsConfig: VitestCLIOptions = {
autoUpdate: {
description:
'Update threshold values: "lines", "functions", "branches" and "statements" to configuration file when current coverage is above the configured thresholds (default: `false`)',
argument: '<boolean|function>',
subcommands: null,
transform(value) {
if (value === 'true' || value === 'yes' || value === true) {
return true
}
if (value === 'false' || value === 'no' || value === false) {
return false
}
return value
},
},
lines: {
description:
Expand Down
7 changes: 5 additions & 2 deletions packages/vitest/src/node/coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,13 +578,16 @@ export class BaseCoverageProvider<Options extends ResolvedCoverageOptions<'istan

updatedThresholds = true

const thresholdFormatter = typeof this.options.thresholds?.autoUpdate === 'function' ? this.options.thresholds?.autoUpdate : (value: number) => value

for (const [threshold, newValue] of thresholdsToUpdate) {
const formattedValue = thresholdFormatter(newValue)
if (name === GLOBAL_THRESHOLDS_KEY) {
config.test.coverage.thresholds[threshold] = newValue
config.test.coverage.thresholds[threshold] = formattedValue
}
else {
const glob = config.test.coverage.thresholds[name as Threshold] as ResolvedThreshold['thresholds']
glob[threshold] = newValue
glob[threshold] = formattedValue
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/vitest/src/node/types/coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,11 @@ interface Thresholds {

/**
* Update threshold values automatically when current coverage is higher than earlier thresholds
* Also can accept a function to format the new threshold values
*
* @default false
*/
autoUpdate?: boolean
autoUpdate?: boolean | ((newThreshold: number) => number)

/** Thresholds for statements */
statements?: number
Expand Down
7 changes: 7 additions & 0 deletions test/core/test/cli-test.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ test('fails when an array is passed down for a single value', async () => {
.toThrowErrorMatchingInlineSnapshot(`[Error: Expected a single value for option "--coverage.provider <name>", received ["v8", "istanbul"]]`)
})

test('coverage autoUpdate accepts boolean values from CLI', async () => {
expect(getCLIOptions('--coverage.thresholds.autoUpdate true').coverage.thresholds.autoUpdate).toBe(true)
expect(getCLIOptions('--coverage.thresholds.autoUpdate false').coverage.thresholds.autoUpdate).toBe(false)
expect(getCLIOptions('--coverage.thresholds.autoUpdate yes').coverage.thresholds.autoUpdate).toBe(true)
expect(getCLIOptions('--coverage.thresholds.autoUpdate no').coverage.thresholds.autoUpdate).toBe(false)
})

test('bench only options', async () => {
expect(() =>
parseArguments('--compare file.json').matchedCommand?.checkUnknownOptions(),
Expand Down
32 changes: 29 additions & 3 deletions test/coverage-test/test/threshold-auto-update.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { CoverageMap } from 'istanbul-lib-coverage'
import { createCoverageSummary } from 'istanbul-lib-coverage'
import { parseModule } from 'magicast'

import { expect, test } from 'vitest'
import { expect, test, vi } from 'vitest'
import { defineConfig } from 'vitest/config'
import { BaseCoverageProvider } from 'vitest/coverage'

Expand Down Expand Up @@ -130,7 +130,33 @@ export default config
await expect(updateThresholds(config)).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Failed to update coverage thresholds. Configuration file is too complex.]`)
})

async function updateThresholds(configurationFile: ReturnType<typeof parseModule>) {
test('formats values with custom formatter', async () => {
const config = parseModule(`export default ${initialConfig}`)

const autoUpdate = vi.fn().mockImplementation(value => value + 10_000)
const updatedConfig = await updateThresholds(config, { thresholds: { autoUpdate } })

expect(updatedConfig).toMatchInlineSnapshot(`
"export default {
"test": {
"coverage": {
"thresholds": {
"lines": 10050,
"branches": 10060,
"functions": 10070,
"statements": 10080
}
}
}
}"
`)

const calls = autoUpdate.mock.calls.flatMap(call => call[0])

expect(calls.sort()).toEqual([50, 60, 70, 80])
})

async function updateThresholds(configurationFile: ReturnType<typeof parseModule>, _coverageOptions: Partial<(InstanceType<typeof BaseCoverageProvider>)['options']> = {}) {
const summaryData = { total: 0, covered: 0, skipped: 0 }
const thresholds = [{
name: 'global',
Expand All @@ -151,7 +177,7 @@ async function updateThresholds(configurationFile: ReturnType<typeof parseModule
provider._initialize({
config: { coverage: { } },
logger: { log: () => {} },
_coverageOptions: {},
_coverageOptions,
} as any)

provider.updateThresholds({
Expand Down
Loading