Skip to content

Commit d8dac20

Browse files
authored
feat: export helper to manually suppress error output (#571)
* feat(console): export helper to manually suppress error output Fixes #564 * docs: added docs for `suppressErrorOutput` * fix(types): fix type of `suppressErrorOutput` to make result callable to restore console * test: add tests for `suppressErrorOutput` export * test: fix error suppression disabled tests for other renderers
1 parent 7f6e4bb commit d8dac20

14 files changed

+112
-37
lines changed

Diff for: docs/api-reference.md

+29-2
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+
- [`suppressErrorOutput`](/reference/api#manually-suppress-output)
1516

1617
---
1718

@@ -286,6 +287,10 @@ to filter out the unnecessary logging and restore the original version during cl
286287
side-effect can affect tests that also patch `console.error` (e.g. to assert a specific error
287288
message get logged) by replacing their custom implementation as well.
288289

290+
> Please note that this is done automatically if the testing framework you're using supports the
291+
> `beforeEach` and `afterEach` global (like Jest, mocha and Jasmine). If not, you will need to do
292+
> [manual suppression](/reference/api#manually-suppress-output) around the test run.
293+
289294
### Disabling `console.error` filtering
290295

291296
Importing `@testing-library/react-hooks/disable-error-filtering.js` in test setup files disable the
@@ -303,8 +308,8 @@ module.exports = {
303308
}
304309
```
305310

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

309314
```diff
310315
- import { renderHook, cleanup, act } from '@testing-library/react-hooks'
@@ -316,3 +321,25 @@ variable to `true` before importing `@testing-library/react-hooks` will also dis
316321
317322
> Please note that this may result in a significant amount of additional logging in your test
318323
> output.
324+
325+
### Manually suppress output
326+
327+
If you are using [a pure import](/installation#pure-imports), you are running your tests in an
328+
environment that does not support `beforeEach` and `afterEach`, or if the automatic suppression is
329+
not available to you for some other reason, then you can use the `suppressErrorOutput` export to
330+
manually start and top suppress the output:
331+
332+
```ts
333+
import { renderHook, suppressErrorOutput } from '@testing-library/react-hooks/pure'
334+
335+
test('should handle thrown error', () => {
336+
const restoreConsole = suppressErrorOutput()
337+
338+
try {
339+
const { result } = renderHook(() => useCounter())
340+
expect(result.error).toBeDefined()
341+
} finally {
342+
restoreConsole()
343+
}
344+
})
345+
```

Diff for: src/core/console.ts

+21-17
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
import filterConsole from 'filter-console'
22

3+
function suppressErrorOutput() {
4+
if (process.env.RHTL_DISABLE_ERROR_FILTERING) {
5+
return () => {}
6+
}
7+
8+
return filterConsole(
9+
[
10+
/^The above error occurred in the <TestComponent> component:/, // error boundary output
11+
/^Error: Uncaught .+/ // jsdom output
12+
],
13+
{
14+
methods: ['error']
15+
}
16+
)
17+
}
18+
319
function enableErrorOutputSuppression() {
420
// 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
21+
if (typeof beforeEach === 'function' && typeof afterEach === 'function') {
22+
let restoreConsole!: () => void
1123

1224
beforeEach(() => {
13-
restoreConsole = filterConsole(
14-
[
15-
/^The above error occurred in the <TestComponent> component:/, // error boundary output
16-
/^Error: Uncaught .+/ // jsdom output
17-
],
18-
{
19-
methods: ['error']
20-
}
21-
)
25+
restoreConsole = suppressErrorOutput()
2226
})
2327

24-
afterEach(() => restoreConsole?.())
28+
afterEach(() => restoreConsole())
2529
}
2630
}
2731

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

Diff for: src/core/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CreateRenderer, Renderer, RenderResult, RenderHookOptions } from '../ty
22

33
import { asyncUtils } from './asyncUtils'
44
import { cleanup, addCleanup, removeCleanup } from './cleanup'
5+
import { suppressErrorOutput } from './console'
56

67
function resultContainer<TValue>() {
78
const results: Array<{ value?: TValue; error?: Error }> = []
@@ -81,4 +82,4 @@ function createRenderHook<
8182
return renderHook
8283
}
8384

84-
export { createRenderHook, cleanup, addCleanup, removeCleanup }
85+
export { createRenderHook, cleanup, addCleanup, removeCleanup, suppressErrorOutput }

Diff for: src/dom/__tests__/errorSuppression.disabled.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ describe('error output suppression (disabled) tests', () => {
55

66
beforeAll(() => {
77
process.env.RHTL_DISABLE_ERROR_FILTERING = 'true'
8+
require('..')
89
})
910

1011
test('should not patch console.error', () => {
11-
require('..')
1212
expect(console.error).toBe(originalConsoleError)
1313
})
1414
})

Diff for: src/dom/__tests__/errorSuppression.pure.test.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
1+
import { ReactHooksRenderer } from '../../types/react'
2+
13
// This verifies that if pure imports are used
24
// then we DON'T auto-wire up the afterEach for folks
35
describe('error output suppression (pure) tests', () => {
46
const originalConsoleError = console.error
57

8+
let suppressErrorOutput!: ReactHooksRenderer['suppressErrorOutput']
9+
610
beforeAll(() => {
7-
require('../pure')
11+
suppressErrorOutput = (require('../pure') as ReactHooksRenderer).suppressErrorOutput
812
})
913

1014
test('should not patch console.error', () => {
1115
expect(console.error).toBe(originalConsoleError)
1216
})
13-
})
1417

15-
export {}
18+
test('should manually patch console.error', () => {
19+
const restore = suppressErrorOutput()
20+
21+
try {
22+
expect(console.error).not.toBe(originalConsoleError)
23+
} finally {
24+
restore()
25+
}
26+
27+
expect(console.error).toBe(originalConsoleError)
28+
})
29+
})

Diff for: src/dom/pure.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@ const renderHook = createRenderHook(createDomRenderer)
3737

3838
export { renderHook, act }
3939

40-
export { cleanup, addCleanup, removeCleanup } from '../core'
40+
export { cleanup, addCleanup, removeCleanup, suppressErrorOutput } from '../core'
4141

4242
export * from '../types/react'

Diff for: src/native/__tests__/errorSuppression.disabled.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ describe('error output suppression (disabled) tests', () => {
55

66
beforeAll(() => {
77
process.env.RHTL_DISABLE_ERROR_FILTERING = 'true'
8+
require('..')
89
})
910

1011
test('should not patch console.error', () => {
11-
require('..')
1212
expect(console.error).toBe(originalConsoleError)
1313
})
1414
})

Diff for: src/native/__tests__/errorSuppression.pure.test.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
1+
import { ReactHooksRenderer } from '../../types/react'
2+
13
// This verifies that if pure imports are used
24
// then we DON'T auto-wire up the afterEach for folks
35
describe('error output suppression (pure) tests', () => {
46
const originalConsoleError = console.error
57

8+
let suppressErrorOutput!: ReactHooksRenderer['suppressErrorOutput']
9+
610
beforeAll(() => {
7-
require('../pure')
11+
suppressErrorOutput = (require('../pure') as ReactHooksRenderer).suppressErrorOutput
812
})
913

1014
test('should not patch console.error', () => {
1115
expect(console.error).toBe(originalConsoleError)
1216
})
13-
})
1417

15-
export {}
18+
test('should manually patch console.error', () => {
19+
const restore = suppressErrorOutput()
20+
21+
try {
22+
expect(console.error).not.toBe(originalConsoleError)
23+
} finally {
24+
restore()
25+
}
26+
27+
expect(console.error).toBe(originalConsoleError)
28+
})
29+
})

Diff for: src/native/pure.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ const renderHook = createRenderHook(createNativeRenderer)
3636

3737
export { renderHook, act }
3838

39-
export { cleanup, addCleanup, removeCleanup } from '../core'
39+
export { cleanup, addCleanup, removeCleanup, suppressErrorOutput } from '../core'
4040

4141
export * from '../types/react'

Diff for: src/pure.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ function getRenderer() {
3232
}
3333
}
3434

35-
const { renderHook, act, cleanup, addCleanup, removeCleanup } = getRenderer()
35+
const { renderHook, act, cleanup, addCleanup, removeCleanup, suppressErrorOutput } = getRenderer()
3636

37-
export { renderHook, act, cleanup, addCleanup, removeCleanup }
37+
export { renderHook, act, cleanup, addCleanup, removeCleanup, suppressErrorOutput }
3838

3939
export * from './types/react'

Diff for: src/server/__tests__/errorSuppression.disabled.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ describe('error output suppression (disabled) tests', () => {
55

66
beforeAll(() => {
77
process.env.RHTL_DISABLE_ERROR_FILTERING = 'true'
8+
require('..')
89
})
910

1011
test('should not patch console.error', () => {
11-
require('..')
1212
expect(console.error).toBe(originalConsoleError)
1313
})
1414
})

Diff for: src/server/__tests__/errorSuppression.pure.test.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
1+
import { ReactHooksRenderer } from '../../types/react'
2+
13
// This verifies that if pure imports are used
24
// then we DON'T auto-wire up the afterEach for folks
35
describe('error output suppression (pure) tests', () => {
46
const originalConsoleError = console.error
57

8+
let suppressErrorOutput!: ReactHooksRenderer['suppressErrorOutput']
9+
610
beforeAll(() => {
7-
require('../pure')
11+
suppressErrorOutput = (require('../pure') as ReactHooksRenderer).suppressErrorOutput
812
})
913

1014
test('should not patch console.error', () => {
1115
expect(console.error).toBe(originalConsoleError)
1216
})
13-
})
1417

15-
export {}
18+
test('should manually patch console.error', () => {
19+
const restore = suppressErrorOutput()
20+
21+
try {
22+
expect(console.error).not.toBe(originalConsoleError)
23+
} finally {
24+
restore()
25+
}
26+
27+
expect(console.error).toBe(originalConsoleError)
28+
})
29+
})

Diff for: src/server/pure.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,6 @@ const renderHook = createRenderHook(createServerRenderer)
6161

6262
export { renderHook, act }
6363

64-
export { cleanup, addCleanup, removeCleanup } from '../core'
64+
export { cleanup, addCleanup, removeCleanup, suppressErrorOutput } from '../core'
6565

6666
export * from '../types/react'

Diff for: src/types/react.ts

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type ReactHooksRenderer = {
2626
cleanup: () => void
2727
addCleanup: (callback: CleanupCallback) => () => void
2828
removeCleanup: (callback: CleanupCallback) => void
29+
suppressErrorOutput: () => () => void
2930
}
3031

3132
export * from '.'

0 commit comments

Comments
 (0)