From 9400026b4173d65a38abfe9a481685551a46a668 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Tue, 19 Nov 2019 16:55:11 -0500 Subject: [PATCH 1/4] feat: add "cleanup" function --- src/cleanup.js | 6 ++++++ src/index.js | 16 +++++++++------- src/render.js | 2 ++ typings/index.d.ts | 1 + 4 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 src/cleanup.js diff --git a/src/cleanup.js b/src/cleanup.js new file mode 100644 index 000000000..0b4a82c89 --- /dev/null +++ b/src/cleanup.js @@ -0,0 +1,6 @@ +export default function cleanup() { + cleanup.queue.forEach(fn => fn()); + cleanup.queue.clear(); +} + +cleanup.queue = new Set(); diff --git a/src/index.js b/src/index.js index 42547250c..f4263745c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,16 +1,18 @@ // @flow import act from './act'; -import render from './render'; -import shallow from './shallow'; -import flushMicrotasksQueue from './flushMicrotasksQueue'; +import cleanup from './cleanup'; import debug from './debug'; import fireEvent from './fireEvent'; +import flushMicrotasksQueue from './flushMicrotasksQueue'; +import render from './render'; +import shallow from './shallow'; import waitForElement from './waitForElement'; -export { render }; -export { shallow }; -export { flushMicrotasksQueue }; +export { act }; +export { cleanup }; export { debug }; export { fireEvent }; +export { flushMicrotasksQueue }; +export { render }; +export { shallow }; export { waitForElement }; -export { act }; diff --git a/src/render.js b/src/render.js index c560dedc8..42e5ea2f0 100644 --- a/src/render.js +++ b/src/render.js @@ -7,6 +7,7 @@ import { queryByAPI } from './helpers/queryByAPI'; import a11yAPI from './helpers/a11yAPI'; import debugShallow from './helpers/debugShallow'; import debugDeep from './helpers/debugDeep'; +import cleanup from './cleanup'; type Options = { wrapper?: React.ComponentType, @@ -34,6 +35,7 @@ export default function render( const update = updateWithAct(renderer, wrap); const instance = renderer.root; + cleanup.queue.add(renderer.unmount); return { ...getByAPI(instance), ...queryByAPI(instance), diff --git a/typings/index.d.ts b/typings/index.d.ts index 66374d7d7..d9d83c4b4 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -135,6 +135,7 @@ export declare const shallow:

( instance: ReactTestInstance | React.ReactElement

) => { output: React.ReactElement

}; export declare const flushMicrotasksQueue: () => Promise; +export declare const cleanup: () => void; export declare const debug: DebugAPI; export declare const fireEvent: FireEventAPI; export declare const waitForElement: WaitForElementFunction; From 45ca61c6253035405c1328369b2778b53c947852 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Tue, 19 Nov 2019 16:55:24 -0500 Subject: [PATCH 2/4] docs: add "cleanup" section --- docs/API.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/API.md b/docs/API.md index b758e315a..550d4a94e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -115,6 +115,45 @@ toJSON(): ReactTestRendererJSON | null Get the rendered component JSON representation, e.g. for snapshot testing. +## `cleanup` + +```ts +const cleanup: () => void +``` + +Unmounts React trees that were mounted with `render`. + +For example, if you're using the `jest` testing framework, then you would need to use the `afterEach` hook like so: + +```jsx +import { cleanup, render } from 'react-native-testing-library' +import { View } from 'react-native' + +afterEach(cleanup) + +it('renders a view', () => { + render() + // ... +}) +``` + +The `afterEach(cleanup)` call also works in `describe` blocks: + +```jsx +describe('when logged in', () => { + afterEach(cleanup) + + it('renders the user', () => { + render() + // ... + }); +}) +``` + +Failing to call `cleanup` when you've called `render` could result in a memory leak and tests which are not "idempotent" (which can lead to difficult to debug errors in your tests). + +The alternative to `cleanup` is balancing every `render` with an `unmount` method call. + ## `fireEvent` ```ts From f08f132a202eaaf88019dab7c40c855105afd2af Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Tue, 19 Nov 2019 17:15:34 -0500 Subject: [PATCH 3/4] test: "cleanup" function --- src/__tests__/cleanup.test.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/__tests__/cleanup.test.js diff --git a/src/__tests__/cleanup.test.js b/src/__tests__/cleanup.test.js new file mode 100644 index 000000000..d704880a3 --- /dev/null +++ b/src/__tests__/cleanup.test.js @@ -0,0 +1,24 @@ +// @flow +/* eslint-disable react/no-multi-comp */ +import React from 'react'; +import { View } from 'react-native'; +import { cleanup, render } from '..'; + +class Test extends React.Component<*> { + componentWillUnmount() { + if (this.props.onUnmount) { + this.props.onUnmount(); + } + } + render() { + return ; + } +} + +test('cleanup', () => { + const fn = jest.fn(); + render(); + expect(fn).not.toHaveBeenCalled(); + cleanup(); + expect(fn).toHaveBeenCalled(); +}); From 88b0363da6e389bf7698adeac2a338b060ef7b59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 6 Dec 2019 17:21:33 +0100 Subject: [PATCH 4/4] don't expose cleanup queue --- src/__tests__/cleanup.test.js | 5 ++++- src/cleanup.js | 13 ++++++++++--- src/render.js | 5 +++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/__tests__/cleanup.test.js b/src/__tests__/cleanup.test.js index d704880a3..7ce5bfc46 100644 --- a/src/__tests__/cleanup.test.js +++ b/src/__tests__/cleanup.test.js @@ -17,8 +17,11 @@ class Test extends React.Component<*> { test('cleanup', () => { const fn = jest.fn(); + + render(); render(); expect(fn).not.toHaveBeenCalled(); + cleanup(); - expect(fn).toHaveBeenCalled(); + expect(fn).toHaveBeenCalledTimes(2); }); diff --git a/src/cleanup.js b/src/cleanup.js index 0b4a82c89..46c084266 100644 --- a/src/cleanup.js +++ b/src/cleanup.js @@ -1,6 +1,13 @@ +// @flow +let cleanupQueue = new Set(); + export default function cleanup() { - cleanup.queue.forEach(fn => fn()); - cleanup.queue.clear(); + cleanupQueue.forEach(fn => fn()); + cleanupQueue.clear(); } -cleanup.queue = new Set(); +export function addToCleanupQueue( + fn: (nextElement?: React$Element) => void +) { + cleanupQueue.add(fn); +} diff --git a/src/render.js b/src/render.js index 42e5ea2f0..cb46f726e 100644 --- a/src/render.js +++ b/src/render.js @@ -2,12 +2,12 @@ import * as React from 'react'; import TestRenderer, { type ReactTestRenderer } from 'react-test-renderer'; // eslint-disable-line import/no-extraneous-dependencies import act from './act'; +import { addToCleanupQueue } from './cleanup'; import { getByAPI } from './helpers/getByAPI'; import { queryByAPI } from './helpers/queryByAPI'; import a11yAPI from './helpers/a11yAPI'; import debugShallow from './helpers/debugShallow'; import debugDeep from './helpers/debugDeep'; -import cleanup from './cleanup'; type Options = { wrapper?: React.ComponentType, @@ -35,7 +35,8 @@ export default function render( const update = updateWithAct(renderer, wrap); const instance = renderer.root; - cleanup.queue.add(renderer.unmount); + addToCleanupQueue(renderer.unmount); + return { ...getByAPI(instance), ...queryByAPI(instance),