From 5932a7f988fdb269ac1ae704172bc7cfbf6ed721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ari=20Perkki=C3=B6?= Date: Mon, 12 Aug 2024 17:05:53 +0300 Subject: [PATCH] feat(coverage): add `--exclude-after-remap` (#6309) --- docs/config/index.md | 12 ++++ packages/coverage-istanbul/src/provider.ts | 8 ++- packages/coverage-v8/src/provider.ts | 4 ++ packages/vitest/src/defaults.ts | 1 + packages/vitest/src/node/types/coverage.ts | 12 ++++ .../test/exclude-after-remap.test.ts | 71 +++++++++++++++++++ 6 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 test/coverage-test/test/exclude-after-remap.test.ts diff --git a/docs/config/index.md b/docs/config/index.md index d62f1ca06f55..fc4de4cb9eaf 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -1313,6 +1313,18 @@ Generate coverage report even when tests fail. Collect coverage of files outside the [project `root`](#root). +#### coverage.excludeAfterRemap 2.1.0 {#coverage-exclude-after-remap} + +- **Type:** `boolean` +- **Default:** `false` +- **Available for providers:** `'v8' | 'istanbul'` +- **CLI:** `--coverage.excludeAfterRemap`, `--coverage.excludeAfterRemap=false` + +Apply exclusions again after coverage has been remapped to original sources. +This is useful when your source files are transpiled and may contain source maps of non-source files. + +Use this option when you are seeing files that show up in report even if they match your `coverage.exclude` patterns. + #### coverage.skipFull - **Type:** `boolean` diff --git a/packages/coverage-istanbul/src/provider.ts b/packages/coverage-istanbul/src/provider.ts index 98ed3df75bb1..0dd865631633 100644 --- a/packages/coverage-istanbul/src/provider.ts +++ b/packages/coverage-istanbul/src/provider.ts @@ -109,9 +109,7 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co lines: config.thresholds['100'] ? 100 : config.thresholds.lines, branches: config.thresholds['100'] ? 100 : config.thresholds.branches, functions: config.thresholds['100'] ? 100 : config.thresholds.functions, - statements: config.thresholds['100'] - ? 100 - : config.thresholds.statements, + statements: config.thresholds['100'] ? 100 : config.thresholds.statements, }, } @@ -292,6 +290,10 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co coverageMap.merge(await transformCoverage(uncoveredCoverage)) } + if (this.options.excludeAfterRemap) { + coverageMap.filter(filename => this.testExclude.shouldInstrument(filename)) + } + return coverageMap } diff --git a/packages/coverage-v8/src/provider.ts b/packages/coverage-v8/src/provider.ts index 532b9d437e08..b6b0f8708b73 100644 --- a/packages/coverage-v8/src/provider.ts +++ b/packages/coverage-v8/src/provider.ts @@ -253,6 +253,10 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage coverageMap.merge(await transformCoverage(converted)) } + if (this.options.excludeAfterRemap) { + coverageMap.filter(filename => this.testExclude.shouldInstrument(filename)) + } + return coverageMap } diff --git a/packages/vitest/src/defaults.ts b/packages/vitest/src/defaults.ts index f95ad627a036..07bc35a37910 100644 --- a/packages/vitest/src/defaults.ts +++ b/packages/vitest/src/defaults.ts @@ -75,6 +75,7 @@ export const coverageConfigDefaults: ResolvedCoverageOptions = { '.marko', ], allowExternal: false, + excludeAfterRemap: false, ignoreEmptyLines: true, processingConcurrency: Math.min( 20, diff --git a/packages/vitest/src/node/types/coverage.ts b/packages/vitest/src/node/types/coverage.ts index a5d18dd152e2..993ed20cee92 100644 --- a/packages/vitest/src/node/types/coverage.ts +++ b/packages/vitest/src/node/types/coverage.ts @@ -251,6 +251,18 @@ export interface BaseCoverageOptions { */ allowExternal?: boolean + /** + * Apply exclusions again after coverage has been remapped to original sources. + * This is useful when your source files are transpiled and may contain source maps + * of non-source files. + * + * Use this option when you are seeing files that show up in report even if they + * match your `coverage.exclude` patterns. + * + * @default false + */ + excludeAfterRemap?: boolean + /** * Concurrency limit used when processing the coverage results. * Defaults to `Math.min(20, os.availableParallelism?.() ?? os.cpus().length)` diff --git a/test/coverage-test/test/exclude-after-remap.test.ts b/test/coverage-test/test/exclude-after-remap.test.ts new file mode 100644 index 000000000000..087567484564 --- /dev/null +++ b/test/coverage-test/test/exclude-after-remap.test.ts @@ -0,0 +1,71 @@ +import { expect } from 'vitest' +import { coverageTest, normalizeURL, readCoverageMap, runVitest, test } from '../utils.js' +import * as transpiled from '../fixtures/src/pre-bundle/bundle.js' + +test('{ excludeAfterRemap: true } should exclude files that come up after remapping', async () => { + await runVitest({ + include: [normalizeURL(import.meta.url)], + coverage: { + include: ['fixtures/src/**'], + exclude: ['fixtures/src/pre-bundle/second.ts'], + excludeAfterRemap: true, + reporter: 'json', + all: false, + }, + }) + + const coverageMap = await readCoverageMap() + const files = coverageMap.files() + + expect(files).toMatchInlineSnapshot(` + [ + "/fixtures/src/pre-bundle/first.ts", + ] + `) +}) + +test('{ excludeAfterRemap: false } should not exclude files that come up after remapping', async () => { + await runVitest({ + include: [normalizeURL(import.meta.url)], + coverage: { + include: ['fixtures/src/**'], + exclude: ['fixtures/src/pre-bundle/second.ts'], + reporter: 'json', + all: false, + }, + }) + + const coverageMap = await readCoverageMap() + const files = coverageMap.files() + + expect(files).toMatchInlineSnapshot(` + [ + "/fixtures/src/pre-bundle/first.ts", + "/fixtures/src/pre-bundle/second.ts", + ] + `) +}) + +test('{ excludeAfterRemap: true } should exclude uncovered files that come up after remapping', async () => { + await runVitest({ + include: ['fixtures/test/math.test.ts'], + coverage: { + include: ['fixtures/src/pre-bundle/**'], + exclude: ['fixtures/src/pre-bundle/second.ts'], + excludeAfterRemap: true, + reporter: 'json', + all: true, + }, + }) + + const coverageMap = await readCoverageMap() + const files = coverageMap.files() + + expect(files).contains('/fixtures/src/pre-bundle/first.ts') + expect(files).not.contains('/fixtures/src/pre-bundle/second.ts') +}) + +coverageTest('run bundled sources', () => { + expect(transpiled.first.covered()).toBe('First') + expect(transpiled.second.covered()).toBe('Second') +})