From d8dac2065976161ced1b0ed3e2b4bd573f836bc8 Mon Sep 17 00:00:00 2001 From: Michael Peyper Date: Tue, 2 Mar 2021 02:31:42 +1100 Subject: [PATCH] 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 --- docs/api-reference.md | 31 ++++++++++++++- src/core/console.ts | 38 ++++++++++--------- src/core/index.ts | 3 +- .../errorSuppression.disabled.test.ts | 2 +- .../__tests__/errorSuppression.pure.test.ts | 20 ++++++++-- src/dom/pure.ts | 2 +- .../errorSuppression.disabled.test.ts | 2 +- .../__tests__/errorSuppression.pure.test.ts | 20 ++++++++-- src/native/pure.ts | 2 +- src/pure.ts | 4 +- .../errorSuppression.disabled.test.ts | 2 +- .../__tests__/errorSuppression.pure.test.ts | 20 ++++++++-- src/server/pure.ts | 2 +- src/types/react.ts | 1 + 14 files changed, 112 insertions(+), 37 deletions(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index d6ed7717..7416d1f7 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -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) --- @@ -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 @@ -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' @@ -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() + } +}) +``` diff --git a/src/core/console.ts b/src/core/console.ts index 1482cf2b..579dcf54 100644 --- a/src/core/console.ts +++ b/src/core/console.ts @@ -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 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 component:/, // error boundary output - /^Error: Uncaught .+/ // jsdom output - ], - { - methods: ['error'] - } - ) + restoreConsole = suppressErrorOutput() }) - afterEach(() => restoreConsole?.()) + afterEach(() => restoreConsole()) } } -export { enableErrorOutputSuppression } +export { enableErrorOutputSuppression, suppressErrorOutput } diff --git a/src/core/index.ts b/src/core/index.ts index 95d34a4b..ccd81b94 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -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() { const results: Array<{ value?: TValue; error?: Error }> = [] @@ -81,4 +82,4 @@ function createRenderHook< return renderHook } -export { createRenderHook, cleanup, addCleanup, removeCleanup } +export { createRenderHook, cleanup, addCleanup, removeCleanup, suppressErrorOutput } diff --git a/src/dom/__tests__/errorSuppression.disabled.test.ts b/src/dom/__tests__/errorSuppression.disabled.test.ts index 843a405e..e1921f09 100644 --- a/src/dom/__tests__/errorSuppression.disabled.test.ts +++ b/src/dom/__tests__/errorSuppression.disabled.test.ts @@ -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) }) }) diff --git a/src/dom/__tests__/errorSuppression.pure.test.ts b/src/dom/__tests__/errorSuppression.pure.test.ts index 1a3368b2..e60ec710 100644 --- a/src/dom/__tests__/errorSuppression.pure.test.ts +++ b/src/dom/__tests__/errorSuppression.pure.test.ts @@ -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) + }) +}) diff --git a/src/dom/pure.ts b/src/dom/pure.ts index 3958dd93..89b13c65 100644 --- a/src/dom/pure.ts +++ b/src/dom/pure.ts @@ -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' diff --git a/src/native/__tests__/errorSuppression.disabled.test.ts b/src/native/__tests__/errorSuppression.disabled.test.ts index 843a405e..e1921f09 100644 --- a/src/native/__tests__/errorSuppression.disabled.test.ts +++ b/src/native/__tests__/errorSuppression.disabled.test.ts @@ -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) }) }) diff --git a/src/native/__tests__/errorSuppression.pure.test.ts b/src/native/__tests__/errorSuppression.pure.test.ts index 1a3368b2..e60ec710 100644 --- a/src/native/__tests__/errorSuppression.pure.test.ts +++ b/src/native/__tests__/errorSuppression.pure.test.ts @@ -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) + }) +}) diff --git a/src/native/pure.ts b/src/native/pure.ts index 2ce742aa..d033a312 100644 --- a/src/native/pure.ts +++ b/src/native/pure.ts @@ -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' diff --git a/src/pure.ts b/src/pure.ts index 2113e9ed..ec72c6fe 100644 --- a/src/pure.ts +++ b/src/pure.ts @@ -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' diff --git a/src/server/__tests__/errorSuppression.disabled.test.ts b/src/server/__tests__/errorSuppression.disabled.test.ts index 843a405e..e1921f09 100644 --- a/src/server/__tests__/errorSuppression.disabled.test.ts +++ b/src/server/__tests__/errorSuppression.disabled.test.ts @@ -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) }) }) diff --git a/src/server/__tests__/errorSuppression.pure.test.ts b/src/server/__tests__/errorSuppression.pure.test.ts index 1a3368b2..e60ec710 100644 --- a/src/server/__tests__/errorSuppression.pure.test.ts +++ b/src/server/__tests__/errorSuppression.pure.test.ts @@ -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) + }) +}) diff --git a/src/server/pure.ts b/src/server/pure.ts index 3cd25a9d..1bacd008 100644 --- a/src/server/pure.ts +++ b/src/server/pure.ts @@ -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' diff --git a/src/types/react.ts b/src/types/react.ts index 56e2798a..348eb5af 100644 --- a/src/types/react.ts +++ b/src/types/react.ts @@ -26,6 +26,7 @@ export type ReactHooksRenderer = { cleanup: () => void addCleanup: (callback: CleanupCallback) => () => void removeCleanup: (callback: CleanupCallback) => void + suppressErrorOutput: () => () => void } export * from '.'