Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 804d9ac

Browse files
authored
fix: only suppress console.error for non-pure imports (#549)
* fix: only suppress console.error for non-pure imports * refactor: remove unused promise util * chore: fix tests for error suppression to * docs: update docs to with more detail on side effects * refactor: only add suppression in beforeEach block and move restoration to afterEach * chore: refactor error suppression tests to require in setup so hooks can actually be registered * chore: added additional tests to ensure pure imports don't add side effects * refactor: clean up unnecessary additional types in internal console suppression function * docs: remove link in API reference docs Fixes #546
1 parent 9af1343 commit 804d9ac

34 files changed

+551
-53
lines changed

‎disable-error-filtering.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
process.env.RHTL_DISABLE_ERROR_FILTERING = true

‎docs/api-reference.md

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,8 @@ module.exports = {
153153
}
154154
```
155155

156-
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-
[Rendering](/installation#being-specific).
156+
Alternatively, you can change your test to import from `@testing-library/react-hooks/pure` (or any
157+
of the [other non-pure imports](/installation#pure-imports)) instead of the regular imports.
159158

160159
```diff
161160
- import { renderHook, cleanup, act } from '@testing-library/react-hooks'
@@ -270,3 +269,49 @@ Interval checking is disabled if `interval` is not provided as a `falsy`.
270269
_Default: 1000_
271270

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

‎docs/installation.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ npm install --save-dev @testing-library/react-hooks
1717
yarn add --dev @testing-library/react-hooks
1818
```
1919

20-
### Peer Dependencies
20+
### Peer dependencies
2121

2222
`react-hooks-testing-library` does not come bundled with a version of
2323
[`react`](https://www.npmjs.com/package/react) to allow you to install the specific version you want
@@ -92,7 +92,31 @@ import { renderHook, act } from '@testing-library/react-hooks/native' // will us
9292
import { renderHook, act } from '@testing-library/react-hooks/server' // will use react-dom/server
9393
```
9494
95-
## Testing Framework
95+
## Pure imports
96+
97+
Importing from any of the previously mentioned imports will cause some side effects in the test
98+
environment:
99+
100+
1. `cleanup` is automatically called in an `afterEach` block
101+
2. `console.error` is patched to hide some React errors
102+
103+
The specifics of these side effects are discussed in more detail in the
104+
[API reference](/reference/api).
105+
106+
If you want to ensure the imports are free of side-effects, you can use the `pure` imports instead,
107+
which can be accessed by appending `/pure` to the end of any of the other imports:
108+
109+
```ts
110+
import { renderHook, act } from '@testing-library/react-hooks/pure'
111+
112+
import { renderHook, act } from '@testing-library/react-hooks/dom/pure'
113+
114+
import { renderHook, act } from '@testing-library/react-hooks/native/pure'
115+
116+
import { renderHook, act } from '@testing-library/react-hooks/server/pure'
117+
```
118+
119+
## Testing framework
96120

97121
In order to run tests, you will probably want to be using a test framework. If you have not already
98122
got one, we recommend using [Jest](https://jestjs.io/), but this library should work without issues

‎package.json

Lines changed: 1 addition & 0 deletions
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 <mpeyper7@gmail.com>",

‎src/core/console.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import filterConsole from 'filter-console'
2+
3+
function enableErrorOutputSuppression() {
4+
// Automatically registers console error suppression and restoration in supported testing frameworks
5+
if (
6+
typeof beforeEach === 'function' &&
7+
typeof afterEach === 'function' &&
8+
!process.env.RHTL_DISABLE_ERROR_FILTERING
9+
) {
10+
let restoreConsole: () => void
11+
12+
beforeEach(() => {
13+
restoreConsole = filterConsole(
14+
[
15+
/^Theaboveerroroccurredinthe<TestComponent>component:/, // error boundary output
16+
/^Error:Uncaught.+/ // jsdom output
17+
],
18+
{
19+
methods: ['error']
20+
}
21+
)
22+
})
23+
24+
afterEach(() => restoreConsole?.())
25+
}
26+
}
27+
28+
export { enableErrorOutputSuppression }

‎src/dom/__tests__/autoCleanup.disabled.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ import { ReactHooksRenderer } from '../../types/react'
66
// then we DON'T auto-wire up the afterEach for folks
77
describe('skip auto cleanup (disabled) tests', () => {
88
let cleanupCalled = false
9-
let renderHook: (arg0: ()=>void)=>void
9+
let renderHook: ReactHooksRenderer['renderHook']
1010

1111
beforeAll(() => {
1212
process.env.RHTL_SKIP_AUTO_CLEANUP = 'true'
13-
// eslint-disable-next-line @typescript-eslint/no-var-requires
1413
renderHook = (require('..') as ReactHooksRenderer).renderHook
1514
})
1615

‎src/dom/__tests__/autoCleanup.noAfterEach.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@ import { useEffect } from 'react'
22

33
import { ReactHooksRenderer } from '../../types/react'
44

5-
// This verifies that if RHTL_SKIP_AUTO_CLEANUP is set
5+
// This verifies that if afterEach is unavailable
66
// then we DON'T auto-wire up the afterEach for folks
77
describe('skip auto cleanup (no afterEach) tests', () => {
88
let cleanupCalled = false
9-
let renderHook: (arg0: ()=>void)=>void
9+
let renderHook: ReactHooksRenderer['renderHook']
1010

1111
beforeAll(() => {
1212
// @ts-expect-error Turning off AfterEach -- ignore Jest LifeCycle Type
13-
// eslint-disable-next-line no-global-assign
1413
afterEach = false
15-
// eslint-disable-next-line @typescript-eslint/no-var-requires
1614
renderHook = (require('..') as ReactHooksRenderer).renderHook
1715
})
1816

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useEffect } from 'react'
2+
3+
import { ReactHooksRenderer } from '../../types/react'
4+
5+
// This verifies that if pure imports are used
6+
// then we DON'T auto-wire up the afterEach for folks
7+
describe('skip auto cleanup (pure) tests', () => {
8+
let cleanupCalled = false
9+
let renderHook: ReactHooksRenderer['renderHook']
10+
11+
beforeAll(() => {
12+
renderHook = (require('../pure') as ReactHooksRenderer).renderHook
13+
})
14+
15+
test('first', () => {
16+
const hookWithCleanup = () => {
17+
useEffect(() => {
18+
return () => {
19+
cleanupCalled = true
20+
}
21+
})
22+
}
23+
renderHook(() => hookWithCleanup())
24+
})
25+
26+
test('second', () => {
27+
expect(cleanupCalled).toBe(false)
28+
})
29+
})

‎src/dom/__tests__/errorHook.test.ts

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState, useEffect } from 'react'
2-
import { renderHook } from '..'
2+
import { renderHook,act } from '..'
33

44
describe('error hook tests', () => {
55
function useError(throwError?: boolean) {
@@ -109,15 +109,15 @@ describe('error hook tests', () => {
109109
})
110110

111111
describe('effect', () => {
112-
test('should raise effect error', () => {
112+
test('this one - should raise effect error', () => {
113113
const { result } = renderHook(() => useEffectError(true))
114114

115115
expect(() => {
116116
expect(result.current).not.toBe(undefined)
117117
}).toThrow(Error('expected'))
118118
})
119119

120-
test('should capture effect error', () => {
120+
test('this one - should capture effect error', () => {
121121
const { result } = renderHook(() => useEffectError(true))
122122
expect(result.error).toEqual(Error('expected'))
123123
})
@@ -142,4 +142,51 @@ describe('error hook tests', () => {
142142
expect(result.error).toBe(undefined)
143143
})
144144
})
145+
146+
describe('error output suppression', () => {
147+
test('should allow console.error to be mocked', async () => {
148+
const consoleError = console.error
149+
console.error = jest.fn()
150+
151+
try {
152+
const { rerender, unmount } = renderHook(
153+
(stage) => {
154+
useEffect(() => {
155+
console.error(`expected in effect`)
156+
return () => {
157+
console.error(`expected in unmount`)
158+
}
159+
}, [])
160+
console.error(`expected in ${stage}`)
161+
},
162+
{
163+
initialProps: 'render'
164+
}
165+
)
166+
167+
act(() => {
168+
console.error('expected in act')
169+
})
170+
171+
await act(async () => {
172+
await new Promise((resolve) => setTimeout(resolve, 100))
173+
console.error('expected in async act')
174+
})
175+
176+
rerender('rerender')
177+
178+
unmount()
179+
180+
expect(console.error).toBeCalledWith('expected in render')
181+
expect(console.error).toBeCalledWith('expected in effect')
182+
expect(console.error).toBeCalledWith('expected in act')
183+
expect(console.error).toBeCalledWith('expected in async act')
184+
expect(console.error).toBeCalledWith('expected in rerender')
185+
expect(console.error).toBeCalledWith('expected in unmount')
186+
expect(console.error).toBeCalledTimes(6)
187+
} finally {
188+
console.error = consoleError
189+
}
190+
})
191+
})
145192
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This verifies that if RHTL_DISABLE_ERROR_FILTERING is set
2+
// then we DON'T auto-wire up the afterEach for folks
3+
describe('error output suppression (disabled) tests', () => {
4+
const originalConsoleError = console.error
5+
6+
beforeAll(() => {
7+
process.env.RHTL_DISABLE_ERROR_FILTERING = 'true'
8+
})
9+
10+
test('should not patch console.error', () => {
11+
require('..')
12+
expect(console.error).toBe(originalConsoleError)
13+
})
14+
})
15+
16+
export {}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /