Skip to content

Commit 4c77f4a

Browse files
aleclarsonthymikee
authored andcommitted
feat: add "cleanup" function (#237)
* feat: add "cleanup" function * docs: add "cleanup" section * test: "cleanup" function * don't expose cleanup queue Co-authored-by: Alec Larson <alec.stanford.larson@gmail.com> Co-authored-by: Michał Pierzchała <thymikee@gmail.com>
1 parent 69d1480 commit 4c77f4a

File tree

6 files changed

+92
-7
lines changed

6 files changed

+92
-7
lines changed

docs/API.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,45 @@ toJSON(): ReactTestRendererJSON | null
115115

116116
Get the rendered component JSON representation, e.g. for snapshot testing.
117117

118+
## `cleanup`
119+
120+
```ts
121+
const cleanup: () => void
122+
```
123+
124+
Unmounts React trees that were mounted with `render`.
125+
126+
For example, if you're using the `jest` testing framework, then you would need to use the `afterEach` hook like so:
127+
128+
```jsx
129+
import { cleanup, render } from 'react-native-testing-library'
130+
import { View } from 'react-native'
131+
132+
afterEach(cleanup)
133+
134+
it('renders a view', () => {
135+
render(<View />)
136+
// ...
137+
})
138+
```
139+
140+
The `afterEach(cleanup)` call also works in `describe` blocks:
141+
142+
```jsx
143+
describe('when logged in', () => {
144+
afterEach(cleanup)
145+
146+
it('renders the user', () => {
147+
render(<SiteHeader />)
148+
// ...
149+
});
150+
})
151+
```
152+
153+
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).
154+
155+
The alternative to `cleanup` is balancing every `render` with an `unmount` method call.
156+
118157
## `fireEvent`
119158

120159
```ts

src/__tests__/cleanup.test.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @flow
2+
/* eslint-disable react/no-multi-comp */
3+
import React from 'react';
4+
import { View } from 'react-native';
5+
import { cleanup, render } from '..';
6+
7+
class Test extends React.Component<*> {
8+
componentWillUnmount() {
9+
if (this.props.onUnmount) {
10+
this.props.onUnmount();
11+
}
12+
}
13+
render() {
14+
return <View />;
15+
}
16+
}
17+
18+
test('cleanup', () => {
19+
const fn = jest.fn();
20+
21+
render(<Test onUnmount={fn} />);
22+
render(<Test onUnmount={fn} />);
23+
expect(fn).not.toHaveBeenCalled();
24+
25+
cleanup();
26+
expect(fn).toHaveBeenCalledTimes(2);
27+
});

src/cleanup.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @flow
2+
let cleanupQueue = new Set();
3+
4+
export default function cleanup() {
5+
cleanupQueue.forEach(fn => fn());
6+
cleanupQueue.clear();
7+
}
8+
9+
export function addToCleanupQueue(
10+
fn: (nextElement?: React$Element<any>) => void
11+
) {
12+
cleanupQueue.add(fn);
13+
}

src/index.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
// @flow
22
import act from './act';
3-
import render from './render';
4-
import shallow from './shallow';
5-
import flushMicrotasksQueue from './flushMicrotasksQueue';
3+
import cleanup from './cleanup';
64
import debug from './debug';
75
import fireEvent from './fireEvent';
6+
import flushMicrotasksQueue from './flushMicrotasksQueue';
7+
import render from './render';
8+
import shallow from './shallow';
89
import waitForElement from './waitForElement';
910

10-
export { render };
11-
export { shallow };
12-
export { flushMicrotasksQueue };
11+
export { act };
12+
export { cleanup };
1313
export { debug };
1414
export { fireEvent };
15+
export { flushMicrotasksQueue };
16+
export { render };
17+
export { shallow };
1518
export { waitForElement };
16-
export { act };

src/render.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import * as React from 'react';
33
import TestRenderer, { type ReactTestRenderer } from 'react-test-renderer'; // eslint-disable-line import/no-extraneous-dependencies
44
import act from './act';
5+
import { addToCleanupQueue } from './cleanup';
56
import { getByAPI } from './helpers/getByAPI';
67
import { queryByAPI } from './helpers/queryByAPI';
78
import a11yAPI from './helpers/a11yAPI';
@@ -34,6 +35,8 @@ export default function render<T>(
3435
const update = updateWithAct(renderer, wrap);
3536
const instance = renderer.root;
3637

38+
addToCleanupQueue(renderer.unmount);
39+
3740
return {
3841
...getByAPI(instance),
3942
...queryByAPI(instance),

typings/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export declare const shallow: <P = {}>(
135135
instance: ReactTestInstance | React.ReactElement<P>
136136
) => { output: React.ReactElement<P> };
137137
export declare const flushMicrotasksQueue: () => Promise<any>;
138+
export declare const cleanup: () => void;
138139
export declare const debug: DebugAPI;
139140
export declare const fireEvent: FireEventAPI;
140141
export declare const waitForElement: WaitForElementFunction;

0 commit comments

Comments
 (0)