Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(console): export helper to manually suppress error output #571

Merged
merged 5 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 29 additions & 2 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ route: '/reference/api'
- [`cleanup`](/reference/api#cleanup)
- [`addCleanup`](/reference/api#addcleanup)
- [`removeCleanup`](/reference/api#removecleanup)
- [`suppressErrorOutput`](/reference/api#manually-suppress-output)

---

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

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

### Disabling `console.error` filtering

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

Alternatively, you can change your test to import from `@testing-library/react-hooks/pure` (or any
of the [other non-pure imports](/installation#pure-imports)) instead of the regular imports.
Alternatively, you can change your test to import from `@testing-library/react-hooks` (or any of the
[other non-pure imports](/installation#pure-imports)) instead of the regular imports.

```diff
- import { renderHook, cleanup, act } from '@testing-library/react-hooks'
Expand All @@ -316,3 +321,25 @@ variable to `true` before importing `@testing-library/react-hooks` will also dis

> Please note that this may result in a significant amount of additional logging in your test
> output.

### Manually suppress output

If you are using [a pure import](/installation#pure-imports), you are running your tests in an
environment that does not support `beforeEach` and `afterEach`, or if the automatic suppression is
not available to you for some other reason, then you can use the `suppressErrorOutput` export to
manually start and top suppress the output:

```ts
import { renderHook, suppressErrorOutput } from '@testing-library/react-hooks/pure'

test('should handle thrown error', () => {
const restoreConsole = suppressErrorOutput()

try {
const { result } = renderHook(() => useCounter())
expect(result.error).toBeDefined()
} finally {
restoreConsole()
}
})
```
38 changes: 21 additions & 17 deletions src/core/console.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
import filterConsole from 'filter-console'

function suppressErrorOutput() {
if (process.env.RHTL_DISABLE_ERROR_FILTERING) {
return () => {}
}

return filterConsole(
[
/^The above error occurred in the <TestComponent> component:/, // error boundary output
/^Error: Uncaught .+/ // jsdom output
],
{
methods: ['error']
}
)
}

function enableErrorOutputSuppression() {
// Automatically registers console error suppression and restoration in supported testing frameworks
if (
typeof beforeEach === 'function' &&
typeof afterEach === 'function' &&
!process.env.RHTL_DISABLE_ERROR_FILTERING
) {
let restoreConsole: () => void
if (typeof beforeEach === 'function' && typeof afterEach === 'function') {
let restoreConsole!: () => void

beforeEach(() => {
restoreConsole = filterConsole(
[
/^The above error occurred in the <TestComponent> component:/, // error boundary output
/^Error: Uncaught .+/ // jsdom output
],
{
methods: ['error']
}
)
restoreConsole = suppressErrorOutput()
})

afterEach(() => restoreConsole?.())
afterEach(() => restoreConsole())
}
}

export { enableErrorOutputSuppression }
export { enableErrorOutputSuppression, suppressErrorOutput }
3 changes: 2 additions & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CreateRenderer, Renderer, RenderResult, RenderHookOptions } from '../ty

import { asyncUtils } from './asyncUtils'
import { cleanup, addCleanup, removeCleanup } from './cleanup'
import { suppressErrorOutput } from './console'

function resultContainer<TValue>() {
const results: Array<{ value?: TValue; error?: Error }> = []
Expand Down Expand Up @@ -81,4 +82,4 @@ function createRenderHook<
return renderHook
}

export { createRenderHook, cleanup, addCleanup, removeCleanup }
export { createRenderHook, cleanup, addCleanup, removeCleanup, suppressErrorOutput }
2 changes: 1 addition & 1 deletion src/dom/__tests__/errorSuppression.disabled.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ describe('error output suppression (disabled) tests', () => {

beforeAll(() => {
process.env.RHTL_DISABLE_ERROR_FILTERING = 'true'
require('..')
})

test('should not patch console.error', () => {
require('..')
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually wasn't testing anything. Requiring in the test wouldn't register the beforeEach and afterEach so it must be done before the test executes. I discovered this because with the refactoring, this left a uncovered branch that is now covered.

expect(console.error).toBe(originalConsoleError)
})
})
Expand Down
20 changes: 17 additions & 3 deletions src/dom/__tests__/errorSuppression.pure.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import { ReactHooksRenderer } from '../../types/react'

// This verifies that if pure imports are used
// then we DON'T auto-wire up the afterEach for folks
describe('error output suppression (pure) tests', () => {
const originalConsoleError = console.error

let suppressErrorOutput!: ReactHooksRenderer['suppressErrorOutput']

beforeAll(() => {
require('../pure')
suppressErrorOutput = (require('../pure') as ReactHooksRenderer).suppressErrorOutput
})

test('should not patch console.error', () => {
expect(console.error).toBe(originalConsoleError)
})
})

export {}
test('should manually patch console.error', () => {
const restore = suppressErrorOutput()

try {
expect(console.error).not.toBe(originalConsoleError)
} finally {
restore()
}

expect(console.error).toBe(originalConsoleError)
})
})
2 changes: 1 addition & 1 deletion src/dom/pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ const renderHook = createRenderHook(createDomRenderer)

export { renderHook, act }

export { cleanup, addCleanup, removeCleanup } from '../core'
export { cleanup, addCleanup, removeCleanup, suppressErrorOutput } from '../core'

export * from '../types/react'
2 changes: 1 addition & 1 deletion src/native/__tests__/errorSuppression.disabled.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ describe('error output suppression (disabled) tests', () => {

beforeAll(() => {
process.env.RHTL_DISABLE_ERROR_FILTERING = 'true'
require('..')
})

test('should not patch console.error', () => {
require('..')
expect(console.error).toBe(originalConsoleError)
})
})
Expand Down
20 changes: 17 additions & 3 deletions src/native/__tests__/errorSuppression.pure.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import { ReactHooksRenderer } from '../../types/react'

// This verifies that if pure imports are used
// then we DON'T auto-wire up the afterEach for folks
describe('error output suppression (pure) tests', () => {
const originalConsoleError = console.error

let suppressErrorOutput!: ReactHooksRenderer['suppressErrorOutput']

beforeAll(() => {
require('../pure')
suppressErrorOutput = (require('../pure') as ReactHooksRenderer).suppressErrorOutput
})

test('should not patch console.error', () => {
expect(console.error).toBe(originalConsoleError)
})
})

export {}
test('should manually patch console.error', () => {
const restore = suppressErrorOutput()

try {
expect(console.error).not.toBe(originalConsoleError)
} finally {
restore()
}

expect(console.error).toBe(originalConsoleError)
})
})
2 changes: 1 addition & 1 deletion src/native/pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ const renderHook = createRenderHook(createNativeRenderer)

export { renderHook, act }

export { cleanup, addCleanup, removeCleanup } from '../core'
export { cleanup, addCleanup, removeCleanup, suppressErrorOutput } from '../core'

export * from '../types/react'
4 changes: 2 additions & 2 deletions src/pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ function getRenderer() {
}
}

const { renderHook, act, cleanup, addCleanup, removeCleanup } = getRenderer()
const { renderHook, act, cleanup, addCleanup, removeCleanup, suppressErrorOutput } = getRenderer()

export { renderHook, act, cleanup, addCleanup, removeCleanup }
export { renderHook, act, cleanup, addCleanup, removeCleanup, suppressErrorOutput }

export * from './types/react'
2 changes: 1 addition & 1 deletion src/server/__tests__/errorSuppression.disabled.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ describe('error output suppression (disabled) tests', () => {

beforeAll(() => {
process.env.RHTL_DISABLE_ERROR_FILTERING = 'true'
require('..')
})

test('should not patch console.error', () => {
require('..')
expect(console.error).toBe(originalConsoleError)
})
})
Expand Down
20 changes: 17 additions & 3 deletions src/server/__tests__/errorSuppression.pure.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import { ReactHooksRenderer } from '../../types/react'

// This verifies that if pure imports are used
// then we DON'T auto-wire up the afterEach for folks
describe('error output suppression (pure) tests', () => {
const originalConsoleError = console.error

let suppressErrorOutput!: ReactHooksRenderer['suppressErrorOutput']

beforeAll(() => {
require('../pure')
suppressErrorOutput = (require('../pure') as ReactHooksRenderer).suppressErrorOutput
})

test('should not patch console.error', () => {
expect(console.error).toBe(originalConsoleError)
})
})

export {}
test('should manually patch console.error', () => {
const restore = suppressErrorOutput()

try {
expect(console.error).not.toBe(originalConsoleError)
} finally {
restore()
}

expect(console.error).toBe(originalConsoleError)
})
})
2 changes: 1 addition & 1 deletion src/server/pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@ const renderHook = createRenderHook(createServerRenderer)

export { renderHook, act }

export { cleanup, addCleanup, removeCleanup } from '../core'
export { cleanup, addCleanup, removeCleanup, suppressErrorOutput } from '../core'

export * from '../types/react'
1 change: 1 addition & 0 deletions src/types/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type ReactHooksRenderer = {
cleanup: () => void
addCleanup: (callback: CleanupCallback) => () => void
removeCleanup: (callback: CleanupCallback) => void
suppressErrorOutput: () => () => void
}

export * from '.'