Skip to content

Commit c03faa2

Browse files
feat(coverage): add allowExternal option (#3894)
Co-authored-by: Ari Perkkiö <ari.perkkio@gmail.com>
1 parent 5704b34 commit c03faa2

File tree

12 files changed

+134
-2
lines changed

12 files changed

+134
-2
lines changed

docs/config/index.md

+9
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,15 @@ Since Vitest 0.31.0, you can check your coverage report in Vitest UI: check [Vit
985985

986986
Generate coverage report even when tests fail.
987987

988+
#### coverage.allowExternal
989+
990+
- **Type:** `boolean`
991+
- **Default:** `false`
992+
- **Available for providers:** `'v8' | 'istanbul'`
993+
- **CLI:** `--coverage.allowExternal`, `--coverage.allowExternal=false`
994+
995+
Collect coverage of files outside the [project `root`](https://vitest.dev/config/#root).
996+
988997
#### coverage.skipFull
989998

990999
- **Type:** `boolean`

packages/coverage-istanbul/src/provider.ts

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface TestExclude {
2323
exclude?: string | string[]
2424
extension?: string | string[]
2525
excludeNodeModules?: boolean
26+
relativePath?: boolean
2627
}): {
2728
shouldInstrument(filePath: string): boolean
2829
glob(cwd: string): Promise<string[]>
@@ -79,6 +80,7 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
7980
exclude: [...defaultExclude, ...defaultInclude, ...this.options.exclude],
8081
excludeNodeModules: true,
8182
extension: this.options.extension,
83+
relativePath: !this.options.allowExternal,
8284
})
8385
}
8486

packages/coverage-v8/src/provider.ts

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ interface TestExclude {
3030
exclude?: string | string[]
3131
extension?: string | string[]
3232
excludeNodeModules?: boolean
33+
relativePath?: boolean
3334
}): {
3435
shouldInstrument(filePath: string): boolean
3536
glob(cwd: string): Promise<string[]>
@@ -79,6 +80,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
7980
exclude: [...defaultExclude, ...defaultInclude, ...this.options.exclude],
8081
excludeNodeModules: true,
8182
extension: this.options.extension,
83+
relativePath: !this.options.allowExternal,
8284
})
8385
}
8486

packages/vitest/src/defaults.ts

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const coverageConfigDefaults: ResolvedCoverageOptions = {
3939
reportOnFailure: false,
4040
reporter: [['text', {}], ['html', {}], ['clover', {}], ['json', {}]],
4141
extension: ['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.tsx', '.jsx', '.vue', '.svelte'],
42+
allowExternal: false,
4243
}
4344

4445
export const fakeTimersDefaults = {

packages/vitest/src/node/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ export function resolveConfig(
279279
?? resolve(resolved.root, file),
280280
),
281281
)
282-
resolved.coverage.exclude.push(...resolved.setupFiles.map(file => relative(resolved.root, file)))
282+
resolved.coverage.exclude.push(...resolved.setupFiles.map(file => `${resolved.coverage.allowExternal ? '**/' : ''}${relative(resolved.root, file)}`))
283283

284284
resolved.forceRerunTriggers = [
285285
...resolved.forceRerunTriggers,

packages/vitest/src/types/coverage.ts

+8
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type FieldsWithDefaultValues =
7878
| 'exclude'
7979
| 'extension'
8080
| 'reportOnFailure'
81+
| 'allowExternal'
8182

8283
export type ResolvedCoverageOptions<T extends Provider = Provider> =
8384
& CoverageOptions<T>
@@ -216,6 +217,13 @@ export interface BaseCoverageOptions {
216217
* @default false
217218
*/
218219
reportOnFailure?: boolean
220+
221+
/**
222+
* Collect coverage of files outside the project `root`.
223+
*
224+
* @default false
225+
*/
226+
allowExternal?: boolean
219227
}
220228

221229
export interface CoverageIstanbulOptions extends BaseCoverageOptions {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import fs from 'node:fs'
2+
import { expect, test } from 'vitest'
3+
4+
const allowExternal = import.meta.env.VITE_COVERAGE_ALLOW_EXTERNAL
5+
6+
test.skipIf(!allowExternal)('{ allowExternal: true } includes files outside project root', async () => {
7+
expect(fs.existsSync('./coverage/test-utils/fixtures/math.ts.html')).toBe(true)
8+
9+
// Files inside project root should always be included
10+
expect(fs.existsSync('./coverage/coverage-test/src/utils.ts.html')).toBe(true)
11+
})
12+
13+
test.skipIf(allowExternal)('{ allowExternal: false } excludes files outside project root', async () => {
14+
expect(fs.existsSync('./coverage/test-utils/fixtures/math.ts.html')).toBe(false)
15+
expect(fs.existsSync('./test-utils/fixtures/math.ts.html')).toBe(false)
16+
expect(fs.existsSync('./fixtures/math.ts.html')).toBe(false)
17+
18+
// Files inside project root should always be included
19+
expect(fs.existsSync('./coverage/utils.ts.html')).toBe(true)
20+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { expect, test } from 'vitest'
2+
3+
import { multiply } from '../src/utils'
4+
import * as ExternalMath from '../../test-utils/fixtures/math'
5+
6+
test('calling files outside project root', () => {
7+
expect(ExternalMath.sum(2, 3)).toBe(5)
8+
})
9+
10+
test('multiply - add some files to report', () => {
11+
expect(multiply(2, 3)).toBe(6)
12+
})

test/coverage-test/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
"name": "@vitest/test-coverage",
33
"private": true,
44
"scripts": {
5-
"test": "pnpm test:v8 && pnpm test:istanbul && pnpm test:custom && pnpm test:browser && pnpm test:types",
5+
"test": "pnpm test:v8 && pnpm test:istanbul && pnpm test:custom && pnpm test:browser && pnpm test:options && pnpm test:types",
66
"test:v8": "node ./testing.mjs --provider v8",
77
"test:custom": "node ./testing.mjs --provider custom",
88
"test:istanbul": "node ./testing.mjs --provider istanbul",
99
"test:browser": "node ./testing.mjs --browser --provider istanbul",
10+
"test:options": "node ./testing-options.mjs",
1011
"test:types": "vitest typecheck --run --reporter verbose"
1112
},
1213
"devDependencies": {

test/coverage-test/test/configuration-options.test-d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ test('provider module', () => {
103103
reporter: [['html', {}], ['json', { file: 'string' }]],
104104
reportsDirectory: 'string',
105105
reportOnFailure: true,
106+
allowExternal: true,
106107
}
107108
},
108109
clean(_: boolean) {},
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { startVitest } from 'vitest/node'
2+
3+
/** @type {Record<string, Partial<import('vitest/config').UserConfig['test']>>[]} */
4+
const testCases = [
5+
{
6+
testConfig: {
7+
name: 'allowExternal: true',
8+
include: ['option-tests/allow-external.test.ts'],
9+
coverage: {
10+
allowExternal: true,
11+
include: ['**/src/**', '**/test-utils/fixtures/**'],
12+
reporter: 'html',
13+
},
14+
},
15+
assertionConfig: {
16+
include: ['coverage-report-tests/allow-external.test.ts'],
17+
env: { VITE_COVERAGE_ALLOW_EXTERNAL: true },
18+
},
19+
},
20+
{
21+
testConfig: {
22+
name: 'allowExternal: false',
23+
include: ['option-tests/allow-external.test.ts'],
24+
coverage: {
25+
allowExternal: false,
26+
include: ['**/src/**', '**/test-utils/fixtures/**'],
27+
reporter: 'html',
28+
},
29+
},
30+
assertionConfig: {
31+
include: ['coverage-report-tests/allow-external.test.ts'],
32+
},
33+
},
34+
]
35+
36+
for (const provider of ['v8', 'istanbul']) {
37+
for (const { testConfig, assertionConfig } of testCases) {
38+
// Run test case
39+
await startVitest('test', ['option-tests/'], {
40+
config: false,
41+
watch: false,
42+
...testConfig,
43+
name: `${provider} - ${testConfig.name}`,
44+
coverage: {
45+
enabled: true,
46+
clean: true,
47+
provider,
48+
...testConfig.coverage,
49+
},
50+
})
51+
52+
checkExit()
53+
54+
// Check generated coverage report
55+
await startVitest('test', ['coverage-report-tests'], {
56+
config: false,
57+
watch: false,
58+
...assertionConfig,
59+
name: `${provider} - assert ${testConfig.name}`,
60+
})
61+
62+
checkExit()
63+
}
64+
}
65+
66+
function checkExit() {
67+
if (process.exitCode)
68+
process.exit(process.exitCode)
69+
}

test/test-utils/fixtures/math.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function sum(a: number, b: number) {
2+
return a + b
3+
}
4+
5+
export function multiply(a: number, b: number) {
6+
return a * b
7+
}

0 commit comments

Comments
 (0)