Skip to content

Commit 36e5a27

Browse files
committed
fix: don't patch console.error for pure imports
Added RHTL_DISABLE_ERROR_FILTERING environment variable toggle to globally disable console filtering. Fixes #546
1 parent 68460e4 commit 36e5a27

11 files changed

+145
-4
lines changed

disable-error-filtering.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
process.env.RHTL_DISABLE_ERROR_FILTERING = true

docs/api-reference.md

+48-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ route: '/reference/api'
1212
- [`cleanup`](/reference/api#cleanup)
1313
- [`addCleanup`](/reference/api#addcleanup)
1414
- [`removeCleanup`](/reference/api#removecleanup)
15+
- [`console.error`](/reference/api#consoleerror)
1516

1617
---
1718

@@ -154,7 +155,7 @@ module.exports = {
154155
```
155156

156157
Alternatively, you can change your test to import from `@testing-library/react-hooks/pure` instead
157-
of the regular imports. This applys to any of our export methods documented in
158+
of the regular imports. This applies to any of our export methods documented in
158159
[Rendering](/installation#being-specific).
159160

160161
```diff
@@ -270,3 +271,49 @@ Interval checking is disabled if `interval` is not provided as a `falsy`.
270271
_Default: 1000_
271272

272273
The maximum amount of time in milliseconds (ms) to wait.
274+
275+
---
276+
277+
## `console.error`
278+
279+
In order to catch errors that are produced in all parts of the hook's lifecycle, the test harness
280+
used to wrap the hook call includes an
281+
[Error Boundary](https://reactjs.org/docs/error-boundaries.html) which causes a
282+
[significant amount of output noise](https://reactjs.org/docs/error-boundaries.html#component-stack-traces)
283+
in tests.
284+
285+
To keep test output clean, we patch `console.error` when `renderHook` is called to filter out the
286+
unnecessary logging and restore the original version during cleanup. This side-effect can affect
287+
tests that also patch `console.error` (e.g. to assert a specific error message get logged) by
288+
replacing their custom implementation as well.
289+
290+
### Disabling `console.error` filtering
291+
292+
Importing `@testing-library/react-hooks/disable-error-filtering.js` in test setup files disable the
293+
error filtering feature and not patch `console.error` in any way.
294+
295+
For example, in [Jest](https://jestjs.io/) this can be added to your
296+
[Jest config](https://jestjs.io/docs/configuration):
297+
298+
```js
299+
module.exports = {
300+
setupFilesAfterEnv: [
301+
'@testing-library/react-hooks/disable-error-filtering.js'
302+
// other setup files
303+
]
304+
}
305+
```
306+
307+
Alternatively, you can change your test to import from `@testing-library/react-hooks/pure` instead
308+
of the regular imports. This applies to any of our export methods documented in
309+
[Rendering](/installation#being-specific).
310+
311+
```diff
312+
- import { renderHook, cleanup, act } from '@testing-library/react-hooks'
313+
+ import { renderHook, cleanup, act } from '@testing-library/react-hooks/pure'
314+
```
315+
316+
If neither of these approaches are suitable, setting the `RHTL_DISABLE_ERROR_FILTERING` environment
317+
variable to `true` before importing `@testing-library/react-hooks` will also disable this feature.
318+
319+
> Please note that this may result is a significant amount of additional logging in you test output.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"native",
1919
"server",
2020
"pure",
21+
"disable-error-filtering.js",
2122
"dont-cleanup-after-each.js"
2223
],
2324
"author": "Michael Peyper <[email protected]>",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { renderHook } from '..'
2+
3+
describe('error output suppression (disabled) tests', () => {
4+
function useError(throwError?: boolean) {
5+
if (throwError) {
6+
throw new Error('expected')
7+
}
8+
return true
9+
}
10+
11+
const originalConsoleError = console.error
12+
const mockConsoleError = jest.fn()
13+
14+
beforeAll(() => {
15+
process.env.RHTL_DISABLE_ERROR_FILTERING = 'true'
16+
})
17+
18+
beforeEach(() => {
19+
console.error = mockConsoleError
20+
})
21+
22+
afterEach(() => {
23+
console.error = originalConsoleError
24+
})
25+
26+
test('should not suppress error output', () => {
27+
const { result } = renderHook(() => useError(true))
28+
29+
expect(result.error).toEqual(Error('expected'))
30+
expect(mockConsoleError).toBeCalledWith(
31+
expect.stringMatching(/^Error: Uncaught \[Error: expected\]/),
32+
expect.any(Error)
33+
)
34+
expect(mockConsoleError).toBeCalledWith(
35+
expect.stringMatching(/^The above error occurred in the <TestComponent> component:/)
36+
)
37+
expect(mockConsoleError).toBeCalledTimes(2)
38+
})
39+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { renderHook } from '../pure'
2+
3+
describe('error output suppression (pure) tests', () => {
4+
function useError(throwError?: boolean) {
5+
if (throwError) {
6+
throw new Error('expected')
7+
}
8+
return true
9+
}
10+
11+
const originalConsoleError = console.error
12+
const mockConsoleError = jest.fn()
13+
14+
beforeEach(() => {
15+
console.error = mockConsoleError
16+
})
17+
18+
afterEach(() => {
19+
console.error = originalConsoleError
20+
})
21+
22+
test('should not suppress error output', () => {
23+
const { result } = renderHook(() => useError(true))
24+
25+
expect(result.error).toEqual(Error('expected'))
26+
expect(mockConsoleError).toBeCalledWith(
27+
expect.stringMatching(/^Error: Uncaught \[Error: expected\]/),
28+
expect.any(Error)
29+
)
30+
expect(mockConsoleError).toBeCalledWith(
31+
expect.stringMatching(/^The above error occurred in the <TestComponent> component:/)
32+
)
33+
expect(mockConsoleError).toBeCalledTimes(2)
34+
})
35+
})

src/dom/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { autoRegisterCleanup } from '../core/cleanup'
2+
import { enableErrorOutputSuppression } from '../helpers/console'
23

34
autoRegisterCleanup()
5+
enableErrorOutputSuppression()
46

57
export * from './pure'

src/helpers/console.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
import filterConsole from 'filter-console'
22

3+
let errorOutputSuppressionEnabled = false
4+
5+
function enableErrorOutputSuppression() {
6+
errorOutputSuppressionEnabled = true
7+
}
8+
39
function suppressErrorOutput() {
10+
if (!errorOutputSuppressionEnabled || process.env.RHTL_DISABLE_ERROR_FILTERING) {
11+
return () => {}
12+
}
13+
414
// The error output from error boundaries is notoriously difficult to suppress. To save
515
// our users from having to work it out, we crudely suppress the output matching the patterns
616
// below. For more information, see these issues:
@@ -19,4 +29,4 @@ function suppressErrorOutput() {
1929
)
2030
}
2131

22-
export { suppressErrorOutput }
32+
export { enableErrorOutputSuppression, suppressErrorOutput }

src/helpers/promises.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ async function callAfter(callback: () => void, ms: number) {
77
callback()
88
}
99

10-
function isPromise<T>(value: unknown): boolean {
11-
return value !== undefined && typeof (value as PromiseLike<T>).then === 'function'
10+
function isPromise(value: unknown): boolean {
11+
return value !== undefined && typeof (value as PromiseLike<unknown>).then === 'function'
1212
}
1313

1414
export { resolveAfter, callAfter, isPromise }

src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { autoRegisterCleanup } from './core/cleanup'
2+
import { enableErrorOutputSuppression } from './helpers/console'
23

34
autoRegisterCleanup()
5+
enableErrorOutputSuppression()
46

57
export * from './pure'

src/native/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { autoRegisterCleanup } from '../core/cleanup'
2+
import { enableErrorOutputSuppression } from '../helpers/console'
23

34
autoRegisterCleanup()
5+
enableErrorOutputSuppression()
46

57
export * from './pure'

src/server/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { autoRegisterCleanup } from '../core/cleanup'
2+
import { enableErrorOutputSuppression } from '../helpers/console'
23

34
autoRegisterCleanup()
5+
enableErrorOutputSuppression()
46

57
export * from './pure'

0 commit comments

Comments
 (0)