Skip to content

Commit 99e016b

Browse files
BattjmoAriPerkkio
andauthored
feat(coverage): autoUpdate to support percentage formatting (#8456)
Co-authored-by: Ari Perkkiö <[email protected]>
1 parent 606cb9e commit 99e016b

File tree

7 files changed

+73
-8
lines changed

7 files changed

+73
-8
lines changed

docs/config/index.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1416,14 +1416,31 @@ Check thresholds per file.
14161416

14171417
##### coverage.thresholds.autoUpdate
14181418

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

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

1427+
You can also pass a function for formatting the updated threshold values:
1428+
1429+
<!-- eslint-skip -->
1430+
```ts
1431+
{
1432+
coverage: {
1433+
thresholds: {
1434+
// Update thresholds without decimals
1435+
autoUpdate: (newThreshold) => Math.floor(newThreshold),
1436+
1437+
// 95.85 -> 95
1438+
functions: 95,
1439+
}
1440+
}
1441+
}
1442+
```
1443+
14271444
##### coverage.thresholds.100
14281445

14291446
- **Type:** `boolean`

docs/guide/cli-generated.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ Check thresholds per file. See `--coverage.thresholds.lines`, `--coverage.thresh
191191

192192
### coverage.thresholds.autoUpdate
193193

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

197197
Update threshold values: "lines", "functions", "branches" and "statements" to configuration file when current coverage is above the configured thresholds (default: `false`)

packages/vitest/src/node/cli/cli-config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,17 @@ export const cliOptionsConfig: VitestCLIOptions = {
246246
autoUpdate: {
247247
description:
248248
'Update threshold values: "lines", "functions", "branches" and "statements" to configuration file when current coverage is above the configured thresholds (default: `false`)',
249+
argument: '<boolean|function>',
250+
subcommands: null,
251+
transform(value) {
252+
if (value === 'true' || value === 'yes' || value === true) {
253+
return true
254+
}
255+
if (value === 'false' || value === 'no' || value === false) {
256+
return false
257+
}
258+
return value
259+
},
249260
},
250261
lines: {
251262
description:

packages/vitest/src/node/coverage.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,13 +578,16 @@ export class BaseCoverageProvider<Options extends ResolvedCoverageOptions<'istan
578578

579579
updatedThresholds = true
580580

581+
const thresholdFormatter = typeof this.options.thresholds?.autoUpdate === 'function' ? this.options.thresholds?.autoUpdate : (value: number) => value
582+
581583
for (const [threshold, newValue] of thresholdsToUpdate) {
584+
const formattedValue = thresholdFormatter(newValue)
582585
if (name === GLOBAL_THRESHOLDS_KEY) {
583-
config.test.coverage.thresholds[threshold] = newValue
586+
config.test.coverage.thresholds[threshold] = formattedValue
584587
}
585588
else {
586589
const glob = config.test.coverage.thresholds[name as Threshold] as ResolvedThreshold['thresholds']
587-
glob[threshold] = newValue
590+
glob[threshold] = formattedValue
588591
}
589592
}
590593
}

packages/vitest/src/node/types/coverage.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,11 @@ interface Thresholds {
279279

280280
/**
281281
* Update threshold values automatically when current coverage is higher than earlier thresholds
282+
* Also can accept a function to format the new threshold values
282283
*
283284
* @default false
284285
*/
285-
autoUpdate?: boolean
286+
autoUpdate?: boolean | ((newThreshold: number) => number)
286287

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

test/core/test/cli-test.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ test('fails when an array is passed down for a single value', async () => {
127127
.toThrowErrorMatchingInlineSnapshot(`[Error: Expected a single value for option "--coverage.provider <name>", received ["v8", "istanbul"]]`)
128128
})
129129

130+
test('coverage autoUpdate accepts boolean values from CLI', async () => {
131+
expect(getCLIOptions('--coverage.thresholds.autoUpdate true').coverage.thresholds.autoUpdate).toBe(true)
132+
expect(getCLIOptions('--coverage.thresholds.autoUpdate false').coverage.thresholds.autoUpdate).toBe(false)
133+
expect(getCLIOptions('--coverage.thresholds.autoUpdate yes').coverage.thresholds.autoUpdate).toBe(true)
134+
expect(getCLIOptions('--coverage.thresholds.autoUpdate no').coverage.thresholds.autoUpdate).toBe(false)
135+
})
136+
130137
test('bench only options', async () => {
131138
expect(() =>
132139
parseArguments('--compare file.json').matchedCommand?.checkUnknownOptions(),

test/coverage-test/test/threshold-auto-update.unit.test.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { CoverageMap } from 'istanbul-lib-coverage'
22
import { createCoverageSummary } from 'istanbul-lib-coverage'
33
import { parseModule } from 'magicast'
44

5-
import { expect, test } from 'vitest'
5+
import { expect, test, vi } from 'vitest'
66
import { defineConfig } from 'vitest/config'
77
import { BaseCoverageProvider } from 'vitest/coverage'
88

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

133-
async function updateThresholds(configurationFile: ReturnType<typeof parseModule>) {
133+
test('formats values with custom formatter', async () => {
134+
const config = parseModule(`export default ${initialConfig}`)
135+
136+
const autoUpdate = vi.fn().mockImplementation(value => value + 10_000)
137+
const updatedConfig = await updateThresholds(config, { thresholds: { autoUpdate } })
138+
139+
expect(updatedConfig).toMatchInlineSnapshot(`
140+
"export default {
141+
"test": {
142+
"coverage": {
143+
"thresholds": {
144+
"lines": 10050,
145+
"branches": 10060,
146+
"functions": 10070,
147+
"statements": 10080
148+
}
149+
}
150+
}
151+
}"
152+
`)
153+
154+
const calls = autoUpdate.mock.calls.flatMap(call => call[0])
155+
156+
expect(calls.sort()).toEqual([50, 60, 70, 80])
157+
})
158+
159+
async function updateThresholds(configurationFile: ReturnType<typeof parseModule>, _coverageOptions: Partial<(InstanceType<typeof BaseCoverageProvider>)['options']> = {}) {
134160
const summaryData = { total: 0, covered: 0, skipped: 0 }
135161
const thresholds = [{
136162
name: 'global',
@@ -151,7 +177,7 @@ async function updateThresholds(configurationFile: ReturnType<typeof parseModule
151177
provider._initialize({
152178
config: { coverage: { } },
153179
logger: { log: () => {} },
154-
_coverageOptions: {},
180+
_coverageOptions,
155181
} as any)
156182

157183
provider.updateThresholds({

0 commit comments

Comments
 (0)