Skip to content

Commit f9e1cb4

Browse files
authored
feat(reporter): --silent=passed-only to log failed tasks only (#7530)
1 parent 5eba60a commit f9e1cb4

File tree

11 files changed

+202
-17
lines changed

11 files changed

+202
-17
lines changed

docs/config/index.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -1089,11 +1089,13 @@ Default timeout to wait for close when Vitest shuts down, in milliseconds
10891089

10901090
### silent<NonProjectOption />
10911091

1092-
- **Type:** `boolean`
1092+
- **Type:** `boolean | 'passed-only'`
10931093
- **Default:** `false`
10941094
- **CLI:** `--silent`, `--silent=false`
10951095

1096-
Silent console output from tests
1096+
Silent console output from tests.
1097+
1098+
Use `'passed-only'` to see logs from failing tests only. Logs from failing tests are printed after a test has finished.
10971099

10981100
### setupFiles
10991101

docs/guide/cli-generated.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ Set to true to exit if port is already in use, instead of automatically trying t
7373

7474
### silent
7575

76-
- **CLI:** `--silent`
76+
- **CLI:** `--silent [value]`
7777
- **Config:** [silent](/config/#silent)
7878

79-
Silent console output from tests
79+
Silent console output from tests. Use `'passed-only'` to see logs from failing tests only.
8080

8181
### hideSkippedTests
8282

packages/vitest/src/node/cli/cli-config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ export const cliOptionsConfig: VitestCLIOptions = {
152152
subcommands: apiConfig(defaultPort),
153153
},
154154
silent: {
155-
description: 'Silent console output from tests',
155+
description: 'Silent console output from tests. Use `\'passed-only\'` to see logs from failing tests only.',
156+
argument: '[value]',
156157
},
157158
hideSkippedTests: {
158159
description: 'Hide logs for skipped tests',

packages/vitest/src/node/reporters/base.ts

+36-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { File, Task, TaskResultPack } from '@vitest/runner'
22
import type { ErrorWithDiff, UserConsoleLog } from '../../types/general'
33
import type { Vitest } from '../core'
44
import type { Reporter } from '../types/reporter'
5+
import type { TestCase, TestModule, TestResult, TestSuite } from './reported-tasks'
56
import { performance } from 'node:perf_hooks'
67
import { getFullName, getSuites, getTestName, getTests, hasFailed } from '@vitest/runner/utils'
78
import { toArray } from '@vitest/utils'
@@ -66,6 +67,32 @@ export abstract class BaseReporter implements Reporter {
6667
}
6768
}
6869

70+
onTestCaseResult(testCase: TestCase): void {
71+
if (testCase.result().state === 'failed') {
72+
this.logFailedTask(testCase.task)
73+
}
74+
}
75+
76+
onTestSuiteResult(testSuite: TestSuite): void {
77+
if (testSuite.state() === 'failed') {
78+
this.logFailedTask(testSuite.task)
79+
}
80+
}
81+
82+
onTestModuleEnd(testModule: TestModule): void {
83+
if (testModule.state() === 'failed') {
84+
this.logFailedTask(testModule.task)
85+
}
86+
}
87+
88+
private logFailedTask(task: Task) {
89+
if (this.ctx.config.silent === 'passed-only') {
90+
for (const log of task.logs || []) {
91+
this.onUserConsoleLog(log, 'failed')
92+
}
93+
}
94+
}
95+
6996
onTaskUpdate(packs: TaskResultPack[]): void {
7097
for (const pack of packs) {
7198
const task = this.ctx.state.idMap.get(pack[0])
@@ -272,8 +299,8 @@ export abstract class BaseReporter implements Reporter {
272299
this.start = performance.now()
273300
}
274301

275-
onUserConsoleLog(log: UserConsoleLog): void {
276-
if (!this.shouldLog(log)) {
302+
onUserConsoleLog(log: UserConsoleLog, taskState?: TestResult['state']): void {
303+
if (!this.shouldLog(log, taskState)) {
277304
return
278305
}
279306

@@ -334,10 +361,15 @@ export abstract class BaseReporter implements Reporter {
334361
this.log(c.yellow('Test removed...') + (trigger ? c.dim(` [ ${this.relative(trigger)} ]\n`) : ''))
335362
}
336363

337-
shouldLog(log: UserConsoleLog): boolean {
338-
if (this.ctx.config.silent) {
364+
shouldLog(log: UserConsoleLog, taskState?: TestResult['state']): boolean {
365+
if (this.ctx.config.silent === true) {
339366
return false
340367
}
368+
369+
if (this.ctx.config.silent === 'passed-only' && taskState !== 'failed') {
370+
return false
371+
}
372+
341373
const shouldLog = this.ctx.config.onConsoleLog?.(log.content, log.type)
342374
if (shouldLog === false) {
343375
return shouldLog

packages/vitest/src/node/reporters/default.ts

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class DefaultReporter extends BaseReporter {
4242
}
4343

4444
onTestModuleEnd(module: TestModule): void {
45+
super.onTestModuleEnd(module)
4546
this.summary?.onTestModuleEnd(module)
4647
}
4748

@@ -50,6 +51,7 @@ export class DefaultReporter extends BaseReporter {
5051
}
5152

5253
onTestCaseResult(test: TestCase): void {
54+
super.onTestCaseResult(test)
5355
this.summary?.onTestCaseResult(test)
5456
}
5557

packages/vitest/src/node/reporters/dot.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,16 @@ export class DotReporter extends BaseReporter {
7070
}
7171

7272
onTestCaseResult(test: TestCase): void {
73+
super.onTestCaseResult(test)
74+
7375
this.finishedTests.add(test.id)
7476
this.tests.set(test.id, test.result().state || 'skipped')
7577
this.renderer?.schedule()
7678
}
7779

78-
onTestModuleEnd(): void {
80+
onTestModuleEnd(testModule: TestModule): void {
81+
super.onTestModuleEnd(testModule)
82+
7983
if (!this.isTTY) {
8084
return
8185
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -454,9 +454,11 @@ export interface InlineConfig {
454454
/**
455455
* Silent mode
456456
*
457+
* Use `'passed-only'` to see logs from failing tests only.
458+
*
457459
* @default false
458460
*/
459-
silent?: boolean
461+
silent?: boolean | 'passed-only'
460462

461463
/**
462464
* Hide logs for skipped tests
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { beforeAll, describe, expect, test } from "vitest"
2+
3+
console.log("Log from failed file")
4+
5+
test("passed test #1", () => {
6+
console.log("Log from passed test")
7+
})
8+
9+
test("failed test #1", () => {
10+
console.log("Log from failed test")
11+
expect(1).toBe(2)
12+
})
13+
14+
describe("failed suite #1", () => {
15+
beforeAll(() => {
16+
console.log("Log from failed suite")
17+
})
18+
19+
test("passed test #2", () => {
20+
console.log("Log from passed test")
21+
})
22+
23+
test("failed test #2", () => {
24+
console.log("Log from failed test")
25+
expect(2).toBe(3)
26+
})
27+
})
28+
29+
describe("passed suite #2", () => {
30+
beforeAll(() => {
31+
console.log("Log from passed suite")
32+
})
33+
34+
test("passed test #3", () => {
35+
console.log("Log from passed test")
36+
})
37+
})

test/reporters/tests/github-actions.test.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import { runVitest } from '../../test-utils'
55

66
test(GithubActionsReporter, async () => {
77
let { stdout, stderr } = await runVitest(
8-
{ reporters: new GithubActionsReporter(), root: './fixtures' },
9-
['some-failing.test.ts'],
8+
{ reporters: new GithubActionsReporter(), root: './fixtures', include: ['**/some-failing.test.ts'] },
109
)
1110
stdout = stdout.replace(resolve(__dirname, '..').replace(/:/g, '%3A'), '__TEST_DIR__')
1211
expect(stdout).toMatchInlineSnapshot(`

test/reporters/tests/json.test.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe('json reporter', async () => {
1111
const { stdout } = await runVitest({
1212
reporters: 'json',
1313
root,
14+
include: ['**/json-fail-import.test.ts', '**/json-fail.test.ts'],
1415
includeTaskLocation: true,
1516
}, ['json-fail'])
1617

@@ -126,11 +127,11 @@ describe('json reporter', async () => {
126127
})
127128

128129
it.each([
129-
['passed', 'all-passing-or-skipped'],
130-
['passed', 'all-skipped'],
131-
['failed', 'some-failing'],
130+
['passed', 'all-passing-or-skipped.test.ts'],
131+
['passed', 'all-skipped.test.ts'],
132+
['failed', 'some-failing.test.ts'],
132133
])('resolves to "%s" status for test file "%s"', async (expected, file) => {
133-
const { stdout } = await runVitest({ reporters: 'json', root }, [file])
134+
const { stdout } = await runVitest({ reporters: 'json', root, include: [`**/${file}`] })
134135

135136
const data = JSON.parse(stdout)
136137

test/reporters/tests/silent.test.ts

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { expect, test } from 'vitest'
2+
import { DefaultReporter } from 'vitest/reporters'
3+
import { runVitest } from '../../test-utils'
4+
5+
test('{ silent: true } hides all console logs', async () => {
6+
const { stdout } = await runVitest({
7+
include: ['./fixtures/console-some-failing.test.ts'],
8+
silent: true,
9+
reporters: [new LogReporter()],
10+
})
11+
12+
expect(stdout).not.toContain('stdout')
13+
expect(stdout).not.toContain('Log from')
14+
expect(stdout).toContain('Test Files 1 failed | 1 passed')
15+
})
16+
17+
test('default value of silence shows all console logs', async () => {
18+
const { stdout } = await runVitest({
19+
include: ['./fixtures/console-some-failing.test.ts'],
20+
reporters: [new LogReporter()],
21+
})
22+
23+
expect(stdout.match(/stdout/g)).toHaveLength(8)
24+
25+
expect(stdout).toContain(`\
26+
stdout | fixtures/console-some-failing.test.ts
27+
Log from failed file
28+
29+
stdout | fixtures/console-some-failing.test.ts > passed test #1
30+
Log from passed test
31+
32+
stdout | fixtures/console-some-failing.test.ts > failed test #1
33+
Log from failed test
34+
35+
stdout | fixtures/console-some-failing.test.ts > failed suite #1
36+
Log from failed suite
37+
38+
stdout | fixtures/console-some-failing.test.ts > failed suite #1 > passed test #2
39+
Log from passed test
40+
41+
stdout | fixtures/console-some-failing.test.ts > failed suite #1 > failed test #2
42+
Log from failed test
43+
44+
stdout | fixtures/console-some-failing.test.ts > passed suite #2
45+
Log from passed suite
46+
47+
stdout | fixtures/console-some-failing.test.ts > passed suite #2 > passed test #3
48+
Log from passed test`,
49+
)
50+
})
51+
52+
test('{ silent: "passed-only" } shows all console logs from failed tests only', async () => {
53+
const { stdout } = await runVitest({
54+
include: ['./fixtures/console-some-failing.test.ts'],
55+
silent: 'passed-only',
56+
reporters: [new LogReporter()],
57+
})
58+
59+
expect(stdout).toContain(`\
60+
stdout | fixtures/console-some-failing.test.ts > failed test #1
61+
Log from failed test
62+
63+
stdout | fixtures/console-some-failing.test.ts > failed suite #1 > failed test #2
64+
Log from failed test
65+
66+
stdout | fixtures/console-some-failing.test.ts > failed suite #1
67+
Log from failed suite
68+
69+
stdout | fixtures/console-some-failing.test.ts
70+
Log from failed file`,
71+
)
72+
73+
expect(stdout).not.toContain('Log from passed')
74+
expect(stdout.match(/stdout/g)).toHaveLength(4)
75+
})
76+
77+
test('{ silent: "passed-only" } logs are filtered by custom onConsoleLog', async () => {
78+
const { stdout } = await runVitest({
79+
include: ['./fixtures/console-some-failing.test.ts'],
80+
silent: 'passed-only',
81+
onConsoleLog(log) {
82+
if (log.includes('suite')) {
83+
return true
84+
}
85+
86+
return false
87+
},
88+
reporters: [new LogReporter()],
89+
})
90+
91+
expect(stdout).toContain(`\
92+
stdout | fixtures/console-some-failing.test.ts > failed suite #1
93+
Log from failed suite`,
94+
)
95+
96+
expect(stdout).not.toContain('Log from passed')
97+
expect(stdout).not.toContain('Log from failed test')
98+
expect(stdout).not.toContain('Log from failed file')
99+
expect(stdout.match(/stdout/g)).toHaveLength(1)
100+
})
101+
102+
class LogReporter extends DefaultReporter {
103+
isTTY = true
104+
onTaskUpdate() {}
105+
}

0 commit comments

Comments
 (0)