Skip to content

Commit 35e313f

Browse files
authored
fix(reporters): check the test result again when tests are rerunning (#8063)
1 parent 9cbfc23 commit 35e313f

File tree

3 files changed

+99
-19
lines changed

3 files changed

+99
-19
lines changed

packages/vitest/src/node/core.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -548,10 +548,6 @@ export class Vitest {
548548
await this._reportFileTask(file)
549549
}
550550

551-
if (hasFailed(files)) {
552-
process.exitCode = 1
553-
}
554-
555551
this._checkUnhandledErrors(errors)
556552
await this._testRun.end(specifications, errors).catch(noop)
557553
await this.initCoverageProvider()
@@ -632,22 +628,14 @@ export class Vitest {
632628

633629
// if run with --changed, don't exit if no tests are found
634630
if (!files.length) {
635-
const throwAnError = !this.config.watch || !(this.config.changed || this.config.related?.length)
636-
637631
await this._testRun.start([])
638632
const coverage = await this.coverageProvider?.generateCoverage?.({ allTestsRun: true })
639633

640-
// set exit code before calling `onTestRunEnd` so the lifecycle is consistent
641-
if (throwAnError) {
642-
const exitCode = this.config.passWithNoTests ? 0 : 1
643-
process.exitCode = exitCode
644-
}
645-
646634
await this._testRun.end([], [], coverage)
647635
// Report coverage for uncovered files
648636
await this.reportCoverage(coverage, true)
649637

650-
if (throwAnError) {
638+
if (!this.config.watch || !(this.config.changed || this.config.related?.length)) {
651639
throw new FilesNotFoundError(this.mode)
652640
}
653641
}
@@ -784,10 +772,6 @@ export class Vitest {
784772

785773
const files = this.state.getFiles()
786774

787-
if (hasFailed(files)) {
788-
process.exitCode = 1
789-
}
790-
791775
this.cache.results.updateResults(files)
792776
try {
793777
await this.cache.results.writeToCache()

packages/vitest/src/node/test-run.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { Vitest } from './core'
1313
import type { TestProject } from './project'
1414
import type { ReportedHookContext, TestCase, TestCollection, TestModule } from './reporters/reported-tasks'
1515
import type { TestSpecification } from './spec'
16+
import type { TestRunEndReason } from './types/reporter'
1617
import assert from 'node:assert'
1718
import { createHash } from 'node:crypto'
1819
import { copyFile, mkdir } from 'node:fs/promises'
@@ -89,14 +90,18 @@ export class TestRun {
8990
const modules = specifications.map(spec => spec.testModule).filter(s => s != null)
9091
const files = modules.map(m => m.task)
9192

92-
const state = this.vitest.isCancelling
93+
const state: TestRunEndReason = this.vitest.isCancelling
9394
? 'interrupted'
9495
// by this point, the run will be marked as failed if there are any errors,
9596
// should it be done by testRun.end?
96-
: process.exitCode
97+
: this.hasFailed(modules)
9798
? 'failed'
9899
: 'passed'
99100

101+
if (state !== 'passed') {
102+
process.exitCode = 1
103+
}
104+
100105
try {
101106
await Promise.all([
102107
this.vitest.report('onTestRunEnd', modules, [...errors] as SerializedError[], state),
@@ -111,6 +116,14 @@ export class TestRun {
111116
}
112117
}
113118

119+
private hasFailed(modules: TestModule[]) {
120+
if (!modules.length) {
121+
return !this.vitest.config.passWithNoTests
122+
}
123+
124+
return modules.some(m => !m.ok())
125+
}
126+
114127
private async reportEvent(id: string, event: TaskUpdateEvent, data: TaskEventData | undefined) {
115128
const task = this.vitest.state.idMap.get(id)
116129
const entity = task && this.vitest.state.getReportedEntity(task)

test/reporters/tests/test-run.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
TestSpecification,
99
TestSuite,
1010
UserConfig,
11+
Vitest,
1112
} from 'vitest/node'
1213
import { rmSync } from 'node:fs'
1314
import { resolve, sep } from 'node:path'
@@ -1011,6 +1012,88 @@ describe('type checking', () => {
10111012
})
10121013
})
10131014

1015+
describe('test run result', () => {
1016+
test('test run is interrupted', async () => {
1017+
let vitest: Vitest
1018+
let reason: TestRunEndReason | undefined
1019+
1020+
await runInlineTests({
1021+
'example.test.js': `
1022+
test('basic', () => new Promise(() => {}))
1023+
`,
1024+
}, {
1025+
globals: true,
1026+
reporters: [
1027+
{
1028+
onInit(ctx) {
1029+
vitest = ctx
1030+
},
1031+
async onTestModuleCollected() {
1032+
await vitest.cancelCurrentRun('keyboard-input')
1033+
},
1034+
onTestRunEnd(_, __, reason_) {
1035+
reason = reason_
1036+
},
1037+
},
1038+
],
1039+
})
1040+
1041+
expect(reason).toBe('interrupted')
1042+
})
1043+
1044+
test('test run failed, but passed afterwards', async () => {
1045+
let reason: TestRunEndReason | undefined
1046+
1047+
const { fs } = await runInlineTests({
1048+
'example.test.js': `
1049+
test('basic', () => {
1050+
expect(1).toBe(2)
1051+
})
1052+
`,
1053+
}, {
1054+
globals: true,
1055+
watch: true,
1056+
reporters: [
1057+
{
1058+
onTestRunEnd(_, __, reason_) {
1059+
reason = reason_
1060+
},
1061+
},
1062+
],
1063+
})
1064+
1065+
expect(reason).toBe('failed')
1066+
1067+
fs.editFile('./example.test.js', c => c.replace('toBe(2)', 'toBe(1)'))
1068+
1069+
await expect.poll(() => reason).toBe('passed')
1070+
})
1071+
1072+
test('test run passed', async () => {
1073+
let reason: TestRunEndReason | undefined
1074+
1075+
await runInlineTests({
1076+
'example.test.js': `
1077+
test('basic', () => {
1078+
expect(1).toBe(1)
1079+
})
1080+
`,
1081+
}, {
1082+
globals: true,
1083+
watch: true,
1084+
reporters: [
1085+
{
1086+
onTestRunEnd(_, __, reason_) {
1087+
reason = reason_
1088+
},
1089+
},
1090+
],
1091+
})
1092+
1093+
expect(reason).toBe('passed')
1094+
})
1095+
})
1096+
10141097
interface ReporterOptions {
10151098
printTestRunEvents?: boolean
10161099
roots?: string[]

0 commit comments

Comments
 (0)