Skip to content

Commit 8859c1b

Browse files
Allow the display of messages without failing (#48)
Co-authored-by: Valentin Hervieu <[email protected]>
1 parent 3ed9387 commit 8859c1b

File tree

8 files changed

+87
-14
lines changed

8 files changed

+87
-14
lines changed

README.md

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ Utility to make jest tests fail when `console.error()`, `console.warn()`, etc. a
44

55
[![version][version-badge]][package] [![MIT License][license-badge]][license] [![PRs Welcome][prs-badge]][prs]
66

7-
87
## What problem is this solving?
98

109
Jest doesn't fail the tests when there is a `console.error`. In large codebase, we can end up with the test output overloaded by a lot of errors, warnings, etc..
@@ -115,14 +114,41 @@ Use this to make a test fail when a `console.warn()` is logged.
115114
- Type: `boolean`
116115
- Default: `true`
117116
117+
### allowMessage
118+
119+
```ts
120+
// signature
121+
type allowMessage = (
122+
message: string,
123+
methodName: 'assert' | 'debug' | 'error' | 'info' | 'log' | 'warn',
124+
context: { group: string; groups: string[] }
125+
) => boolean
126+
```
127+
128+
This function is called for every console method supported by this utility.
129+
If `true` is returned, the message will show in the console and the test won't fail.
130+
131+
Example:
132+
133+
```ts
134+
failOnConsole({
135+
allowMessage: (errorMessage) => {
136+
if (/An expected error/.test(errorMessage)) {
137+
return true
138+
}
139+
return false
140+
},
141+
})
142+
```
143+
118144
### silenceMessage
119145
120146
```ts
121147
// signature
122148
type silenceMessage = (
123149
message: string,
124150
methodName: 'assert' | 'debug' | 'error' | 'info' | 'log' | 'warn',
125-
context: { group: string, groups: string[] }
151+
context: { group: string; groups: string[] }
126152
) => boolean
127153
```
128154
@@ -162,7 +188,7 @@ failOnConsole({
162188
if (ignoreNameList.includes(testName)) {
163189
return true
164190
}
165-
191+
166192
return false
167193
},
168194
})

index.d.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
declare namespace init {
2+
type ConsoleMethodName = 'assert' | 'debug' | 'error' | 'info' | 'log' | 'warn'
23
type InitOptions = {
34
/**
45
* This function lets you define a custom error message. The methodName is the method
56
* that caused the error, bold is a function that lets you bold subsets of your message.
67
* example: (methodName, bold) => `console.${methodName} is not ${bold('allowed')}`
78
*/
8-
errorMessage?: (
9-
methodName: 'assert' | 'debug' | 'error' | 'info' | 'log' | 'warn',
10-
bold: (string: string) => string
11-
) => string
9+
errorMessage?: (methodName: ConsoleMethodName, bold: (string: string) => string) => string
1210

1311
/** @default false */
1412
shouldFailOnAssert?: boolean
@@ -29,21 +27,31 @@ declare namespace init {
2927
shouldFailOnWarn?: boolean
3028

3129
/**
32-
* This function is called for every console warn/error.
30+
* This function is called for every console methods.
3331
* If true is returned, the message will not show in the console
3432
* and the test won't fail.
3533
*/
3634
silenceMessage?: (
3735
message: string,
38-
methodName: 'assert' | 'debug' | 'error' | 'info' | 'log' | 'warn',
39-
context: { group: string, groups: string[] }
36+
methodName: ConsoleMethodName,
37+
context: { group: string; groups: string[] }
4038
) => boolean
4139

4240
/**
4341
* This function is called for every test setup and teardown to determine if the test should
4442
* skip console checks from this package or not.
4543
*/
4644
skipTest?: (args: { testName: string; testPath: string }) => boolean
45+
46+
/**
47+
* This function is called for every console methods.
48+
* If true is returned, the message will not cause the tests to fail and will be logged to the console.
49+
*/
50+
allowMessage?: (
51+
message: string,
52+
methodName: ConsoleMethodName,
53+
context: { group: string; groups: string[] }
54+
) => boolean
4755
}
4856
}
4957

index.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const init = ({
2323
shouldFailOnWarn = true,
2424
skipTest,
2525
silenceMessage,
26+
allowMessage,
2627
} = {}) => {
2728
const flushUnexpectedConsoleCalls = (methodName, unexpectedConsoleCallStacks) => {
2829
if (unexpectedConsoleCallStacks.length > 0) {
@@ -50,17 +51,27 @@ const init = ({
5051

5152
const patchConsoleMethod = (methodName) => {
5253
const unexpectedConsoleCallStacks = []
54+
const originalMethod = console[methodName]
5355

5456
const captureMessage = (format, ...args) => {
5557
const message = util.format(format, ...args)
58+
const context = { group: groups[groups.length - 1], groups }
5659

5760
if (
58-
silenceMessage &&
59-
silenceMessage(message, methodName, { group: groups[groups.length - 1], groups })
61+
typeof silenceMessage === 'function' &&
62+
silenceMessage(message, methodName, context)
6063
) {
6164
return
6265
}
6366

67+
if (
68+
typeof allowMessage === 'function' &&
69+
allowMessage(message, methodName, context)
70+
) {
71+
originalMethod(format, ...args)
72+
return
73+
}
74+
6475
// Capture the call stack now so we can warn about it later.
6576
// The call stack has helpful information for the test author.
6677
// Don't throw yet though b'c it might be accidentally caught and suppressed.
@@ -98,8 +109,6 @@ const init = ({
98109

99110
const newMethod = methods[methodName] || captureMessage
100111

101-
let originalMethod = console[methodName]
102-
103112
const canSkipTest = () => {
104113
const currentTestState = expect.getState()
105114
const testName = currentTestState.currentTestName
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = () => {
2+
console.error('my error message that I do not control')
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const consoleError = require('.')
2+
3+
describe('console.error display message but not fail (allowMessage)', () => {
4+
it('does not throw', () => {
5+
expect(consoleError).not.toThrow()
6+
})
7+
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
setupFilesAfterEnv: ['./jest.setup.js'],
3+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const failOnConsole = require('../../..')
2+
3+
failOnConsole({
4+
shouldFailOnError: true,
5+
allowMessage: (msg, methodName) => {
6+
return methodName === 'error' && /my error message that I do not control/.test(msg)
7+
},
8+
})

tests/index.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,13 @@ describe('jest-fail-on-console', () => {
7676

7777
expect(stderr).toEqual(expect.stringContaining(passString('silence-by-nested-group')))
7878
})
79+
80+
it('does not error if message is silenced with `allowMessage`', async () => {
81+
const { stderr, stdout } = await runFixture('allow-message')
82+
83+
expect(stdout).toContain('console.error');
84+
expect(stdout).toContain('my error message that I do not control');
85+
86+
expect(stderr).toEqual(expect.stringContaining(passString('allow-message')))
87+
})
7988
})

0 commit comments

Comments
 (0)