diff --git a/cleanup-after-each.js b/cleanup-after-each.js
new file mode 100644
index 0000000..8f2e439
--- /dev/null
+++ b/cleanup-after-each.js
@@ -0,0 +1 @@
+afterEach(require('./dist').cleanup);
diff --git a/other/cheat-sheet.pdf b/other/cheat-sheet.pdf
index 0210ae3..0df2ce2 100644
Binary files a/other/cheat-sheet.pdf and b/other/cheat-sheet.pdf differ
diff --git a/package.json b/package.json
index 34bb196..d75660c 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"files": [
"dist",
"typings",
+ "cleanup-after-each.js",
"jest-preset.js"
],
"engines": {
diff --git a/src/__tests__/act.js b/src/__tests__/act.js
index 84791c7..ac9ce61 100644
--- a/src/__tests__/act.js
+++ b/src/__tests__/act.js
@@ -2,7 +2,9 @@ import React from 'react';
import 'jest-native/extend-expect';
import { Button } from 'react-native';
-import { render, fireEvent } from '../';
+import { render, fireEvent, cleanup } from '../';
+
+afterEach(cleanup);
test('render calls useEffect immediately', () => {
const effectCb = jest.fn();
diff --git a/src/__tests__/bugs.js b/src/__tests__/bugs.js
index 30e61e1..063d862 100644
--- a/src/__tests__/bugs.js
+++ b/src/__tests__/bugs.js
@@ -1,7 +1,9 @@
import React from 'react';
import { Text, View } from 'react-native';
-import { render, queryAllByProp } from '../';
+import { render, queryAllByProp, cleanup } from '../';
+
+afterEach(cleanup);
// This is to ensure custom queries can be passed to render. In most cases, you
// wouldn't/shouldn't need to do this, but we do allow it so we'll test to
diff --git a/src/__tests__/debug.js b/src/__tests__/debug.js
index 3220ca4..231154d 100644
--- a/src/__tests__/debug.js
+++ b/src/__tests__/debug.js
@@ -1,13 +1,14 @@
import React from 'react';
import { Text } from 'react-native';
-import { render } from '../';
+import { cleanup, render } from '../';
beforeEach(() => {
jest.spyOn(console, 'log').mockImplementation(() => {});
});
afterEach(() => {
+ cleanup();
console.log.mockRestore();
});
diff --git a/src/__tests__/end-to-end.js b/src/__tests__/end-to-end.js
index 455b06e..5a1b6f8 100644
--- a/src/__tests__/end-to-end.js
+++ b/src/__tests__/end-to-end.js
@@ -2,7 +2,9 @@ import React from 'react';
import 'jest-native/extend-expect';
import { Text } from 'react-native';
-import { render, wait } from '../';
+import { cleanup, render, wait } from '../';
+
+afterEach(cleanup);
const fetchAMessage = () =>
new Promise(resolve => {
diff --git a/src/__tests__/events.js b/src/__tests__/events.js
index 0f0c21c..5f03421 100644
--- a/src/__tests__/events.js
+++ b/src/__tests__/events.js
@@ -2,7 +2,9 @@ import React from 'react';
import 'jest-native/extend-expect';
import { Button, Image, Text, TextInput, TouchableHighlight } from 'react-native';
-import { render, fireEvent, eventMap, NativeTestEvent, getEventHandlerName, wait } from '../';
+import { render, fireEvent, eventMap, getEventHandlerName, wait, cleanup } from '../';
+
+afterEach(cleanup);
Object.keys(eventMap).forEach(key => {
describe(`${key} events`, () => {
diff --git a/src/__tests__/fetch.js b/src/__tests__/fetch.js
index febc65f..c118e02 100644
--- a/src/__tests__/fetch.js
+++ b/src/__tests__/fetch.js
@@ -2,7 +2,9 @@ import React from 'react';
import 'jest-native/extend-expect';
import { TouchableOpacity, Text, View } from 'react-native';
-import { render, fireEvent, wait } from '../';
+import { render, fireEvent, wait, cleanup } from '../';
+
+afterEach(cleanup);
global.fetch = require('jest-fetch-mock');
diff --git a/src/__tests__/forms.js b/src/__tests__/forms.js
index 4e5b64b..af6641c 100644
--- a/src/__tests__/forms.js
+++ b/src/__tests__/forms.js
@@ -1,6 +1,8 @@
import React from 'react';
import { Button, TextInput, View } from 'react-native';
-import { render, fireEvent } from '../';
+import { render, fireEvent, cleanup } from '../';
+
+afterEach(cleanup);
function Login({ onSubmit, user }) {
return (
diff --git a/src/__tests__/misc.js b/src/__tests__/misc.js
index 385caf7..51191bb 100644
--- a/src/__tests__/misc.js
+++ b/src/__tests__/misc.js
@@ -2,7 +2,9 @@ import React from 'react';
import { Button, Picker, Text, View } from 'react-native';
import { toMatchDiffSnapshot } from 'snapshot-diff';
-import { fireEvent, render } from '../';
+import { cleanup, fireEvent, render } from '../';
+
+afterEach(cleanup);
test(' works', () => {
function Wrapper() {
@@ -32,15 +34,15 @@ test('fragments can show diffs', () => {
expect.extend({ toMatchDiffSnapshot });
- const { getByText, asFragment } = render();
- const firstRender = asFragment();
+ const { getByText, asJSON } = render();
+ const firstRender = asJSON();
fireEvent.press(getByText(/Click to increase/));
// This will snapshot only the difference between the first render, and the
// state of the DOM after the click event.
// See https://github.com/jest-community/snapshot-diff
- expect(firstRender).toMatchDiffSnapshot(asFragment());
+ expect(firstRender).toMatchDiffSnapshot(asJSON());
});
test('finds only valid children', () => {
diff --git a/src/__tests__/render.js b/src/__tests__/render.js
index 6e83a35..fab3b7c 100644
--- a/src/__tests__/render.js
+++ b/src/__tests__/render.js
@@ -1,7 +1,9 @@
import React from 'react';
import { Text, SafeAreaView, View } from 'react-native';
-import { render, toJSON } from '../';
+import { cleanup, render } from '../';
+
+afterEach(cleanup);
test('renders View', () => {
const { container } = render();
@@ -24,10 +26,10 @@ it('supports fragments', () => {
}
}
- const { asFragment, unmount } = render();
- expect(asFragment()).toMatchSnapshot();
+ const { asJSON, unmount } = render();
+ expect(asJSON()).toMatchSnapshot();
unmount();
- expect(asFragment()).toBeNull();
+ expect(asJSON()).toBeNull();
});
test('renders options.wrapper around node', () => {
diff --git a/src/__tests__/rerender.js b/src/__tests__/rerender.js
index 6631909..2539528 100644
--- a/src/__tests__/rerender.js
+++ b/src/__tests__/rerender.js
@@ -2,7 +2,9 @@ import React from 'react';
import 'jest-native/extend-expect';
import { Text } from 'react-native';
-import { render } from '../';
+import { cleanup, render } from '../';
+
+afterEach(cleanup);
test('rerender will re-render the element', () => {
const Greeting = props => {props.message};
diff --git a/src/__tests__/stopwatch.js b/src/__tests__/stopwatch.js
index 4b6bbc0..5dd86dc 100644
--- a/src/__tests__/stopwatch.js
+++ b/src/__tests__/stopwatch.js
@@ -1,7 +1,9 @@
import React from 'react';
import { Button, Text, View } from 'react-native';
-import { render, fireEvent, prettyPrint } from '../';
+import { render, fireEvent, prettyPrint, cleanup } from '../';
+
+afterEach(cleanup);
class StopWatch extends React.Component {
state = { lapse: 0, running: false };
diff --git a/src/index.js b/src/index.js
index 2cf70eb..32f1a04 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,10 +8,12 @@ import {
getQueriesForElement,
NativeTestEvent,
prettyPrint,
- proxyElement as proxy,
+ proxyElement,
} from './lib';
import act from './act-compat';
+const renderers = new Set();
+
function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) {
const wrapUiIfNeeded = innerElement =>
WrapperComponent ? (
@@ -28,7 +30,9 @@ function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) {
testRenderer = TR.create(wrapUiIfNeeded(ui), options);
});
- const wrappers = proxy(testRenderer.root).findAll(n => n.getProp('pointerEvents') === 'box-none');
+ renderers.add(testRenderer);
+
+ const wrappers = proxyElement(testRenderer.root).findAll(n => n.type === 'View');
const baseElement = wrappers[0]; // Includes YellowBox and your render
const container = wrappers[1]; // Includes only your render
@@ -42,13 +46,22 @@ function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) {
testRenderer.update(wrapUiIfNeeded(rerenderUi));
});
},
- asFragment: () => {
+ asJSON: () => {
return toJSON(container);
},
...getQueriesForElement(baseElement, queries),
};
}
+function cleanup() {
+ renderers.forEach(cleanupRenderer);
+}
+
+function cleanupRenderer(renderer) {
+ renderer.unmount();
+ renderers.delete(renderer);
+}
+
function fireEvent(...args) {
let returnValue;
act(() => {
@@ -68,4 +81,4 @@ Object.keys(rntlFireEvent).forEach(typeArg => {
});
export * from './lib';
-export { act, fireEvent, render, NativeTestEvent };
+export { act, cleanup, fireEvent, render, NativeTestEvent };
diff --git a/src/lib/__tests__/get-by-errors.js b/src/lib/__tests__/get-by-errors.js
index 458dada..10adff0 100644
--- a/src/lib/__tests__/get-by-errors.js
+++ b/src/lib/__tests__/get-by-errors.js
@@ -2,7 +2,9 @@ import React from 'react';
import { Button, Text, TextInput, View } from 'react-native';
import cases from 'jest-in-case';
-import { render } from '../../';
+import { cleanup, render } from '../../';
+
+afterEach(cleanup);
cases(
'getBy* queries throw an error when there are multiple elements returned',
diff --git a/src/lib/__tests__/misc.js b/src/lib/__tests__/misc.js
index 6d12662..47a41bb 100644
--- a/src/lib/__tests__/misc.js
+++ b/src/lib/__tests__/misc.js
@@ -1,7 +1,9 @@
import React from 'react';
-import { Text, View } from 'react-native';
+import { View } from 'react-native';
-import { render, queryByProp, queryByTestId } from '../../';
+import { render, queryByProp, queryByTestId, cleanup } from '../../';
+
+afterEach(cleanup);
test('queryByProp', () => {
const { container } = render(
diff --git a/src/lib/__tests__/pretty-print.js b/src/lib/__tests__/pretty-print.js
index 841ee94..886ca53 100644
--- a/src/lib/__tests__/pretty-print.js
+++ b/src/lib/__tests__/pretty-print.js
@@ -1,7 +1,9 @@
import React from 'react';
import { Text, View } from 'react-native';
-import { render, prettyPrint } from '../../';
+import { render, prettyPrint, cleanup } from '../../';
+
+afterEach(cleanup);
test('it prints correctly with no children', () => {
const { container } = render();
diff --git a/src/lib/__tests__/queries.find.js b/src/lib/__tests__/queries.find.js
index 1d31615..115d5a4 100644
--- a/src/lib/__tests__/queries.find.js
+++ b/src/lib/__tests__/queries.find.js
@@ -1,7 +1,9 @@
import React from 'react';
import { Button, Image, Text, TextInput, View } from 'react-native';
-import { render } from '../../';
+import { cleanup, render } from '../../';
+
+afterEach(cleanup);
test('find asynchronously finds elements', async () => {
const {
diff --git a/src/lib/__tests__/text-matchers.js b/src/lib/__tests__/text-matchers.js
index fe41f0d..6413989 100644
--- a/src/lib/__tests__/text-matchers.js
+++ b/src/lib/__tests__/text-matchers.js
@@ -2,7 +2,9 @@ import React from 'react';
import cases from 'jest-in-case';
import { Button, Image, Text, TextInput, TouchableOpacity } from 'react-native';
-import { getDefaultNormalizer, render } from '../../';
+import { cleanup, getDefaultNormalizer, render } from '../../';
+
+afterEach(cleanup);
cases(
'matches find case-sensitive full strings by default',
diff --git a/src/lib/__tests__/to-json.js b/src/lib/__tests__/to-json.js
index ce63d09..cae532e 100644
--- a/src/lib/__tests__/to-json.js
+++ b/src/lib/__tests__/to-json.js
@@ -1,7 +1,9 @@
import React from 'react';
import { Text, View } from 'react-native';
-import { prettyPrint, render, toJSON } from '../../';
+import { cleanup, prettyPrint, render, toJSON } from '../../';
+
+afterEach(cleanup);
test('it converts to json', () => {
function ParentComponent({ children }) {
diff --git a/src/lib/__tests__/wait-for-element-to-be-removed.fake-timers.js b/src/lib/__tests__/wait-for-element-to-be-removed.fake-timers.js
index bcc0646..0b8d688 100644
--- a/src/lib/__tests__/wait-for-element-to-be-removed.fake-timers.js
+++ b/src/lib/__tests__/wait-for-element-to-be-removed.fake-timers.js
@@ -1,7 +1,9 @@
import React from 'react';
import { View } from 'react-native';
-import { render, waitForElementToBeRemoved } from '../../';
+import { cleanup, render, waitForElementToBeRemoved } from '../../';
+
+afterEach(cleanup);
jest.useFakeTimers();
diff --git a/src/lib/__tests__/wait-for-element-to-be-removed.js b/src/lib/__tests__/wait-for-element-to-be-removed.js
index 640ec9b..0536f25 100644
--- a/src/lib/__tests__/wait-for-element-to-be-removed.js
+++ b/src/lib/__tests__/wait-for-element-to-be-removed.js
@@ -1,7 +1,9 @@
import React from 'react';
import { View } from 'react-native';
-import { render, waitForElementToBeRemoved } from '../../';
+import { cleanup, render, waitForElementToBeRemoved } from '../../';
+
+afterEach(cleanup);
test('resolves only when the element is removed', async () => {
class MutatedElement extends React.Component {
diff --git a/src/lib/__tests__/wait-for-element.js b/src/lib/__tests__/wait-for-element.js
index 91d362e..baab649 100644
--- a/src/lib/__tests__/wait-for-element.js
+++ b/src/lib/__tests__/wait-for-element.js
@@ -1,7 +1,9 @@
import React from 'react';
import { Text, View } from 'react-native';
-import { render, waitForElement } from '../../';
+import { cleanup, render, waitForElement } from '../../';
+
+afterEach(cleanup);
test('waits for element to appear', async () => {
const { rerender, getByTestId } = render();
diff --git a/src/lib/__tests__/within.js b/src/lib/__tests__/within.js
index adcae0f..55fc563 100644
--- a/src/lib/__tests__/within.js
+++ b/src/lib/__tests__/within.js
@@ -1,7 +1,9 @@
import React from 'react';
import { Button, View } from 'react-native';
-import { render, within } from '../../';
+import { cleanup, render, within } from '../../';
+
+afterEach(cleanup);
test('it works when scoping to a smaller set of elements', () => {
const { getByTestId } = render(
diff --git a/src/preset/configure.js b/src/preset/configure.js
index e257c1e..385b308 100644
--- a/src/preset/configure.js
+++ b/src/preset/configure.js
@@ -1,6 +1,10 @@
import { asyncAct } from '../act-compat';
+import { NativeTestEvent } from '../lib/events';
import { configure as configureNTL } from '../lib';
+// Make this global for convenience, just like browser events
+global.NativeTestEvent = NativeTestEvent;
+
configureNTL({
asyncWrapper: async cb => {
let result;
diff --git a/typings/index.d.ts b/typings/index.d.ts
index f6cf069..9b58eef 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -1,10 +1,11 @@
import { ReactElement, ComponentType } from 'react';
import { ReactTestRenderer } from 'react-test-renderer';
-import { getQueriesForElement, BoundFunction } from './get-queries-for-element';
import * as queries from './queries';
import * as queryHelpers from './query-helpers';
import { NativeTestInstance } from './query-helpers';
+import { NativeTestInstanceJSON } from './to-json';
+import { getQueriesForElement, BoundFunction } from './get-queries-for-element';
declare const within: typeof getQueriesForElement;
@@ -25,9 +26,10 @@ interface Queries {
export type RenderResult = {
baseElement: NativeTestInstance;
container: NativeTestInstance;
- debug: () => void;
+ debug: (element?: NativeTestInstance) => void;
rerender: (ui: ReactElement) => void;
unmount: () => void;
+ asFragment: () => NativeTestInstanceJSON;
} & { [P in keyof Q]: BoundFunction };
export interface RenderOptions {
@@ -46,6 +48,8 @@ export function render(
options: RenderOptions,
): RenderResult;
+export const cleanup: () => void;
+
export const act: (callback: () => void) => void;
export { queries, queryHelpers, within };