Skip to content

Commit 1371ca6

Browse files
authored
fix(coverage): remove empty coverage folder on test failure too (#6547)
1 parent 8723242 commit 1371ca6

File tree

7 files changed

+140
-25
lines changed

7 files changed

+140
-25
lines changed

packages/coverage-istanbul/src/provider.ts

+16-6
Original file line numberDiff line numberDiff line change
@@ -307,13 +307,23 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
307307
const keepResults = !this.options.cleanOnRerun && this.ctx.config.watch
308308

309309
if (!keepResults) {
310-
this.coverageFiles = new Map()
311-
await fs.rm(this.coverageFilesDirectory, { recursive: true })
310+
await this.cleanAfterRun()
311+
}
312+
}
312313

313-
// Remove empty reports directory, e.g. when only text-reporter is used
314-
if (readdirSync(this.options.reportsDirectory).length === 0) {
315-
await fs.rm(this.options.reportsDirectory, { recursive: true })
316-
}
314+
private async cleanAfterRun() {
315+
this.coverageFiles = new Map()
316+
await fs.rm(this.coverageFilesDirectory, { recursive: true })
317+
318+
// Remove empty reports directory, e.g. when only text-reporter is used
319+
if (readdirSync(this.options.reportsDirectory).length === 0) {
320+
await fs.rm(this.options.reportsDirectory, { recursive: true })
321+
}
322+
}
323+
324+
async onTestFailure() {
325+
if (!this.options.reportOnFailure) {
326+
await this.cleanAfterRun()
317327
}
318328
}
319329

packages/coverage-v8/src/provider.ts

+16-6
Original file line numberDiff line numberDiff line change
@@ -279,13 +279,23 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
279279
const keepResults = !this.options.cleanOnRerun && this.ctx.config.watch
280280

281281
if (!keepResults) {
282-
this.coverageFiles = new Map()
283-
await fs.rm(this.coverageFilesDirectory, { recursive: true })
282+
await this.cleanAfterRun()
283+
}
284+
}
284285

285-
// Remove empty reports directory, e.g. when only text-reporter is used
286-
if (readdirSync(this.options.reportsDirectory).length === 0) {
287-
await fs.rm(this.options.reportsDirectory, { recursive: true })
288-
}
286+
private async cleanAfterRun() {
287+
this.coverageFiles = new Map()
288+
await fs.rm(this.coverageFilesDirectory, { recursive: true })
289+
290+
// Remove empty reports directory, e.g. when only text-reporter is used
291+
if (readdirSync(this.options.reportsDirectory).length === 0) {
292+
await fs.rm(this.options.reportsDirectory, { recursive: true })
293+
}
294+
}
295+
296+
async onTestFailure() {
297+
if (!this.options.reportOnFailure) {
298+
await this.cleanAfterRun()
289299
}
290300
}
291301

packages/vitest/src/node/core.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -974,8 +974,12 @@ export class Vitest {
974974
}
975975

976976
private async reportCoverage(coverage: unknown, allTestsRun: boolean) {
977-
if (!this.config.coverage.reportOnFailure && this.state.getCountOfFailedTests() > 0) {
978-
return
977+
if (this.state.getCountOfFailedTests() > 0) {
978+
await this.coverageProvider?.onTestFailure?.()
979+
980+
if (!this.config.coverage.reportOnFailure) {
981+
return
982+
}
979983
}
980984

981985
if (this.coverageProvider) {

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

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export interface CoverageProvider {
2626
/** Called with coverage results after a single test file has been run */
2727
onAfterSuiteRun: (meta: AfterSuiteRunMeta) => void | Promise<void>
2828

29+
/** Callback called when test run fails */
30+
onTestFailure?: () => void | Promise<void>
31+
2932
/** Callback to generate final coverage results */
3033
generateCoverage: (
3134
reportContext: ReportContext
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1-
import { existsSync, readdirSync } from 'node:fs'
2-
import { expect } from 'vitest'
3-
import { coverageTest, normalizeURL, runVitest, test } from '../utils'
1+
import { existsSync } from 'node:fs'
2+
import { beforeEach, expect } from 'vitest'
3+
import { captureStdout, coverageTest, normalizeURL, runVitest, test } from '../utils'
44
import { sum } from '../fixtures/src/math'
55

6+
beforeEach(() => {
7+
return captureStdout()
8+
})
9+
610
test('empty coverage directory is cleaned after tests', async () => {
711
await runVitest({
812
include: [normalizeURL(import.meta.url)],
13+
testNamePattern: 'passing test',
914
coverage: { reporter: 'text', all: false },
1015
})
1116

12-
if (existsSync('./coverage')) {
13-
if (readdirSync('./coverage').length !== 0) {
14-
throw new Error('Test case expected coverage directory to be empty')
15-
}
17+
expect(existsSync('./coverage')).toBe(false)
18+
})
19+
20+
test('empty coverage directory is cleaned after failing test run', async () => {
21+
const { exitCode } = await runVitest({
22+
include: [normalizeURL(import.meta.url)],
23+
testNamePattern: 'failing test',
24+
coverage: { reporter: 'text', all: false },
25+
}, { throwOnError: false })
1626

17-
throw new Error('Empty coverage directory was not cleaned')
18-
}
27+
expect(existsSync('./coverage')).toBe(false)
28+
expect(exitCode).toBe(1)
1929
})
2030

21-
coverageTest('cover some lines', () => {
31+
coverageTest('passing test', () => {
2232
expect(sum(2, 3)).toBe(5)
2333
})
34+
35+
coverageTest('failing test', () => {
36+
expect(sum(2, 3)).toBe(6)
37+
})
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { expect } from 'vitest'
2+
import { captureStdout, coverageTest, isV8Provider, normalizeURL, runVitest, test } from '../utils'
3+
import { sum } from '../fixtures/src/math'
4+
5+
test('report is not generated when tests fail', async () => {
6+
const stdout = captureStdout()
7+
8+
const { exitCode } = await runVitest({
9+
include: [normalizeURL(import.meta.url)],
10+
coverage: {
11+
all: false,
12+
include: ['**/fixtures/src/math.ts'],
13+
reporter: 'text',
14+
},
15+
}, { throwOnError: false })
16+
17+
expect(stdout()).toBe('')
18+
expect(exitCode).toBe(1)
19+
})
20+
21+
test('report is generated when tests fail and { reportOnFailure: true }', async () => {
22+
const stdout = captureStdout()
23+
24+
const { exitCode } = await runVitest({
25+
include: [normalizeURL(import.meta.url)],
26+
coverage: {
27+
all: false,
28+
include: ['**/fixtures/src/math.ts'],
29+
reporter: 'text',
30+
reportOnFailure: true,
31+
},
32+
}, { throwOnError: false })
33+
34+
if (isV8Provider()) {
35+
expect(stdout()).toMatchInlineSnapshot(`
36+
"----------|---------|----------|---------|---------|-------------------
37+
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
38+
----------|---------|----------|---------|---------|-------------------
39+
All files | 50 | 100 | 25 | 50 |
40+
math.ts | 50 | 100 | 25 | 50 | 6-7,10-11,14-15
41+
----------|---------|----------|---------|---------|-------------------
42+
"
43+
`)
44+
}
45+
else {
46+
expect(stdout()).toMatchInlineSnapshot(`
47+
"----------|---------|----------|---------|---------|-------------------
48+
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
49+
----------|---------|----------|---------|---------|-------------------
50+
All files | 25 | 100 | 25 | 25 |
51+
math.ts | 25 | 100 | 25 | 25 | 6-14
52+
----------|---------|----------|---------|---------|-------------------
53+
"
54+
`)
55+
}
56+
57+
expect(exitCode).toBe(1)
58+
})
59+
60+
coverageTest('failing test', () => {
61+
expect(sum(1, 2)).toBe(4)
62+
})

test/coverage-test/utils.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { normalize } from 'pathe'
55
import libCoverage from 'istanbul-lib-coverage'
66
import type { FileCoverageData } from 'istanbul-lib-coverage'
77
import type { TestFunction, UserConfig } from 'vitest'
8-
import { describe as vitestDescribe, test as vitestTest } from 'vitest'
8+
import { vi, describe as vitestDescribe, test as vitestTest } from 'vitest'
9+
import stripAnsi from 'strip-ansi'
910
import * as testUtils from '../test-utils'
1011

1112
export function test(name: string, fn: TestFunction, skip = false) {
@@ -103,3 +104,14 @@ export function isBrowser() {
103104
export function normalizeURL(importMetaURL: string) {
104105
return normalize(fileURLToPath(importMetaURL))
105106
}
107+
108+
export function captureStdout() {
109+
const spy = vi.fn()
110+
const original = process.stdout.write
111+
process.stdout.write = spy
112+
113+
return function collect() {
114+
process.stdout.write = original
115+
return stripAnsi(spy.mock.calls.map(call => call[0]).join(''))
116+
}
117+
}

0 commit comments

Comments
 (0)