Skip to content

Commit

Permalink
feat(coverage): add allowExternal option (#3894)
Browse files Browse the repository at this point in the history
Co-authored-by: Ari Perkkiö <ari.perkkio@gmail.com>
  • Loading branch information
vojvodics and AriPerkkio committed Aug 21, 2023
1 parent 5704b34 commit c03faa2
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 2 deletions.
9 changes: 9 additions & 0 deletions docs/config/index.md
Expand Up @@ -985,6 +985,15 @@ Since Vitest 0.31.0, you can check your coverage report in Vitest UI: check [Vit

Generate coverage report even when tests fail.

#### coverage.allowExternal

- **Type:** `boolean`
- **Default:** `false`
- **Available for providers:** `'v8' | 'istanbul'`
- **CLI:** `--coverage.allowExternal`, `--coverage.allowExternal=false`

Collect coverage of files outside the [project `root`](https://vitest.dev/config/#root).

#### coverage.skipFull

- **Type:** `boolean`
Expand Down
2 changes: 2 additions & 0 deletions packages/coverage-istanbul/src/provider.ts
Expand Up @@ -23,6 +23,7 @@ interface TestExclude {
exclude?: string | string[]
extension?: string | string[]
excludeNodeModules?: boolean
relativePath?: boolean
}): {
shouldInstrument(filePath: string): boolean
glob(cwd: string): Promise<string[]>
Expand Down Expand Up @@ -79,6 +80,7 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
exclude: [...defaultExclude, ...defaultInclude, ...this.options.exclude],
excludeNodeModules: true,
extension: this.options.extension,
relativePath: !this.options.allowExternal,
})
}

Expand Down
2 changes: 2 additions & 0 deletions packages/coverage-v8/src/provider.ts
Expand Up @@ -30,6 +30,7 @@ interface TestExclude {
exclude?: string | string[]
extension?: string | string[]
excludeNodeModules?: boolean
relativePath?: boolean
}): {
shouldInstrument(filePath: string): boolean
glob(cwd: string): Promise<string[]>
Expand Down Expand Up @@ -79,6 +80,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
exclude: [...defaultExclude, ...defaultInclude, ...this.options.exclude],
excludeNodeModules: true,
extension: this.options.extension,
relativePath: !this.options.allowExternal,
})
}

Expand Down
1 change: 1 addition & 0 deletions packages/vitest/src/defaults.ts
Expand Up @@ -39,6 +39,7 @@ export const coverageConfigDefaults: ResolvedCoverageOptions = {
reportOnFailure: false,
reporter: [['text', {}], ['html', {}], ['clover', {}], ['json', {}]],
extension: ['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.tsx', '.jsx', '.vue', '.svelte'],
allowExternal: false,
}

export const fakeTimersDefaults = {
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/config.ts
Expand Up @@ -279,7 +279,7 @@ export function resolveConfig(
?? resolve(resolved.root, file),
),
)
resolved.coverage.exclude.push(...resolved.setupFiles.map(file => relative(resolved.root, file)))
resolved.coverage.exclude.push(...resolved.setupFiles.map(file => `${resolved.coverage.allowExternal ? '**/' : ''}${relative(resolved.root, file)}`))

resolved.forceRerunTriggers = [
...resolved.forceRerunTriggers,
Expand Down
8 changes: 8 additions & 0 deletions packages/vitest/src/types/coverage.ts
Expand Up @@ -78,6 +78,7 @@ type FieldsWithDefaultValues =
| 'exclude'
| 'extension'
| 'reportOnFailure'
| 'allowExternal'

export type ResolvedCoverageOptions<T extends Provider = Provider> =
& CoverageOptions<T>
Expand Down Expand Up @@ -216,6 +217,13 @@ export interface BaseCoverageOptions {
* @default false
*/
reportOnFailure?: boolean

/**
* Collect coverage of files outside the project `root`.
*
* @default false
*/
allowExternal?: boolean
}

export interface CoverageIstanbulOptions extends BaseCoverageOptions {
Expand Down
20 changes: 20 additions & 0 deletions test/coverage-test/coverage-report-tests/allow-external.test.ts
@@ -0,0 +1,20 @@
import fs from 'node:fs'
import { expect, test } from 'vitest'

const allowExternal = import.meta.env.VITE_COVERAGE_ALLOW_EXTERNAL

test.skipIf(!allowExternal)('{ allowExternal: true } includes files outside project root', async () => {
expect(fs.existsSync('./coverage/test-utils/fixtures/math.ts.html')).toBe(true)

// Files inside project root should always be included
expect(fs.existsSync('./coverage/coverage-test/src/utils.ts.html')).toBe(true)
})

test.skipIf(allowExternal)('{ allowExternal: false } excludes files outside project root', async () => {
expect(fs.existsSync('./coverage/test-utils/fixtures/math.ts.html')).toBe(false)
expect(fs.existsSync('./test-utils/fixtures/math.ts.html')).toBe(false)
expect(fs.existsSync('./fixtures/math.ts.html')).toBe(false)

// Files inside project root should always be included
expect(fs.existsSync('./coverage/utils.ts.html')).toBe(true)
})
12 changes: 12 additions & 0 deletions test/coverage-test/option-tests/allow-external.test.ts
@@ -0,0 +1,12 @@
import { expect, test } from 'vitest'

import { multiply } from '../src/utils'
import * as ExternalMath from '../../test-utils/fixtures/math'

test('calling files outside project root', () => {
expect(ExternalMath.sum(2, 3)).toBe(5)
})

test('multiply - add some files to report', () => {
expect(multiply(2, 3)).toBe(6)
})
3 changes: 2 additions & 1 deletion test/coverage-test/package.json
Expand Up @@ -2,11 +2,12 @@
"name": "@vitest/test-coverage",
"private": true,
"scripts": {
"test": "pnpm test:v8 && pnpm test:istanbul && pnpm test:custom && pnpm test:browser && pnpm test:types",
"test": "pnpm test:v8 && pnpm test:istanbul && pnpm test:custom && pnpm test:browser && pnpm test:options && pnpm test:types",
"test:v8": "node ./testing.mjs --provider v8",
"test:custom": "node ./testing.mjs --provider custom",
"test:istanbul": "node ./testing.mjs --provider istanbul",
"test:browser": "node ./testing.mjs --browser --provider istanbul",
"test:options": "node ./testing-options.mjs",
"test:types": "vitest typecheck --run --reporter verbose"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions test/coverage-test/test/configuration-options.test-d.ts
Expand Up @@ -103,6 +103,7 @@ test('provider module', () => {
reporter: [['html', {}], ['json', { file: 'string' }]],
reportsDirectory: 'string',
reportOnFailure: true,
allowExternal: true,
}
},
clean(_: boolean) {},
Expand Down
69 changes: 69 additions & 0 deletions test/coverage-test/testing-options.mjs
@@ -0,0 +1,69 @@
import { startVitest } from 'vitest/node'

/** @type {Record<string, Partial<import('vitest/config').UserConfig['test']>>[]} */
const testCases = [
{
testConfig: {
name: 'allowExternal: true',
include: ['option-tests/allow-external.test.ts'],
coverage: {
allowExternal: true,
include: ['**/src/**', '**/test-utils/fixtures/**'],
reporter: 'html',
},
},
assertionConfig: {
include: ['coverage-report-tests/allow-external.test.ts'],
env: { VITE_COVERAGE_ALLOW_EXTERNAL: true },
},
},
{
testConfig: {
name: 'allowExternal: false',
include: ['option-tests/allow-external.test.ts'],
coverage: {
allowExternal: false,
include: ['**/src/**', '**/test-utils/fixtures/**'],
reporter: 'html',
},
},
assertionConfig: {
include: ['coverage-report-tests/allow-external.test.ts'],
},
},
]

for (const provider of ['v8', 'istanbul']) {
for (const { testConfig, assertionConfig } of testCases) {
// Run test case
await startVitest('test', ['option-tests/'], {
config: false,
watch: false,
...testConfig,
name: `${provider} - ${testConfig.name}`,
coverage: {
enabled: true,
clean: true,
provider,
...testConfig.coverage,
},
})

checkExit()

// Check generated coverage report
await startVitest('test', ['coverage-report-tests'], {
config: false,
watch: false,
...assertionConfig,
name: `${provider} - assert ${testConfig.name}`,
})

checkExit()
}
}

function checkExit() {
if (process.exitCode)
process.exit(process.exitCode)
}
7 changes: 7 additions & 0 deletions test/test-utils/fixtures/math.ts
@@ -0,0 +1,7 @@
export function sum(a: number, b: number) {
return a + b
}

export function multiply(a: number, b: number) {
return a * b
}

0 comments on commit c03faa2

Please sign in to comment.