react-native-testing-library
Advanced tools
Comparing version 0.1.0 to 0.2.0
{ | ||
"name": "react-native-testing-library", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Simple React Native testing utilities helping you write better tests with less effort", | ||
@@ -11,3 +11,6 @@ "main": "src/index.js", | ||
"devDependencies": { | ||
"@babel/core": "^7.1.2", | ||
"@babel/runtime": "^7.1.2", | ||
"@callstack/eslint-config": "^2.0.0", | ||
"babel-core": "7.0.0-bridge.0", | ||
"babel-jest": "^23.6.0", | ||
@@ -17,2 +20,3 @@ "eslint": "^5.6.1", | ||
"jest": "^23.6.0", | ||
"metro-react-native-babel-preset": "^0.47.1", | ||
"pretty-format": "^23.6.0", | ||
@@ -29,3 +33,8 @@ "react": "^16.5.2", | ||
"react-is": "^16.5.2" | ||
}, | ||
"scripts": { | ||
"test": "jest", | ||
"flow-check": "flow check", | ||
"lint": "eslint src --cache" | ||
} | ||
} |
167
README.md
@@ -5,4 +5,10 @@ # react-native-testing-library | ||
_Appreiciation notice: This project is heavily inspired by [react-testing-library](https://github.com/kentcdodds/react-testing-library). Go check it out and use it to test your web React apps._ | ||
[![Build Status][build-badge]][build] | ||
[![Version][version-badge]][package] | ||
[![MIT License][license-badge]][license] | ||
[![PRs Welcome][prs-welcome-badge]][prs-welcome] | ||
[![Chat][chat-badge]][chat] | ||
_Appreciation notice: This project is heavily inspired by [react-testing-library](https://github.com/kentcdodds/react-testing-library). Go check it out and use it to test your web React apps._ | ||
## The problem | ||
@@ -23,7 +29,7 @@ | ||
```jsx | ||
import { render } from 'react-native-testing-library'; | ||
import { render, fireEvent } from 'react-native-testing-library'; | ||
import { QuestionsBoard } from '../QuestionsBoard'; | ||
function setAnswer(getAllByName, index, answer) { | ||
getAllByName('Question')[index].props.onChangeText(answer); | ||
function setAnswer(question, answer) { | ||
fireEvent.changeText(question, answer); | ||
} | ||
@@ -33,7 +39,8 @@ | ||
const { getAllByName, getByText } = render(<QuestionsBoard {...props} />); | ||
const allQuestions = getAllByName('Question'); | ||
setAnswer(getAllByName, 0, 'a1'); | ||
setAnswer(getAllByName, 1, 'a2'); | ||
setAnswer(allQuestions[0], 'a1'); | ||
setAnswer(allQuestions[1], 'a2'); | ||
getByText('submit').props.onPress(); | ||
fireEvent.press(getByText('submit')); | ||
@@ -52,6 +59,6 @@ expect(props.verifyQuestions).toBeCalledWith({ | ||
```sh | ||
yarn add --dev react-testing-library | ||
yarn add --dev react-native-testing-library | ||
``` | ||
This library has a peerDependencies listing for `react-test-renderer` and, of course, `react`. Make sure to install them too! | ||
This library has a peerDependencies listing for `react-test-renderer`, `pretty-format` and, of course, `react`. Make sure to install them too! | ||
@@ -148,2 +155,107 @@ As you may have noticed, it's not tied to React Native at all – you can safely use it in your React components if you feel like not interacting directly with DOM. | ||
## `FireEvent API` | ||
### `fireEvent: (element: ReactTestInstance, eventName: string, data?: *) => void` | ||
Invokes named event handler on the element or parent element in the tree. | ||
```jsx | ||
import { View } from 'react-native'; | ||
import { render, fireEvent } from 'react-native-testing-library'; | ||
import { MyComponent } from './MyComponent'; | ||
const onEventMock = jest.fn(); | ||
const { getByTestId } = render( | ||
<MyComponent testID="custom" onMyCustomEvent={onEventMock} /> | ||
); | ||
fireEvent(getByTestId('custom'), 'myCustomEvent'); | ||
``` | ||
### `fireEvent.press: (element: ReactTestInstance) => void` | ||
Invokes `press` event handler on the element or parent element in the tree. | ||
```jsx | ||
import { View, Text, TouchableOpacity } from 'react-native'; | ||
import { render, fireEvent } from 'react-native-testing-library'; | ||
const onPressMock = jest.fn(); | ||
const { getByTestId } = render( | ||
<View> | ||
<TouchableOpacity onPress={onPressMock} testID="button"> | ||
<Text>Press me</Text> | ||
</TouchableOpacity> | ||
</View> | ||
); | ||
fireEvent.press(getByTestId('button')); | ||
``` | ||
### `fireEvent.doublePress: (element: ReactTestInstance) => void` | ||
Invokes `doublePress` event handler on the element or parent element in the tree. | ||
```jsx | ||
import { TouchableOpacity, Text } from 'react-native'; | ||
import { render, fireEvent } from 'react-native-testing-library'; | ||
const onDoublePressMock = jest.fn(); | ||
const { getByTestId } = render( | ||
<TouchableOpacity onDoublePress={onDoublePressMock}> | ||
<Text testID="button-text">Click me</Text> | ||
</TouchableOpacity> | ||
); | ||
fireEvent.doublePress(getByTestId('button-text')); | ||
``` | ||
### `fireEvent.changeText: (element: ReactTestInstance, data?: *) => void` | ||
Invokes `changeText` event handler on the element or parent element in the tree. | ||
```jsx | ||
import { View, TextInput } from 'react-native'; | ||
import { render, fireEvent } from 'react-native-testing-library'; | ||
const onChangeTextMock = jest.fn(); | ||
const CHANGE_TEXT = 'content'; | ||
const { getByTestId } = render( | ||
<View> | ||
<TextInput testID="text-input" onChangeText={onChangeTextMock} /> | ||
</View> | ||
); | ||
fireEvent.changeText(getByTestId('text-input'), CHANGE_TEXT); | ||
``` | ||
### `fireEvent.scroll: (element: ReactTestInstance, data?: *) => void` | ||
Invokes `scroll` event handler on the element or parent element in the tree. | ||
```jsx | ||
import { ScrollView, TextInput } from 'react-native'; | ||
import { render, fireEvent } from 'react-native-testing-library'; | ||
const onScrollMock = jest.fn(); | ||
const eventData = { | ||
nativeEvent: { | ||
contentOffset: { | ||
y: 200, | ||
}, | ||
}, | ||
}; | ||
const { getByTestId } = render( | ||
<ScrollView testID="scroll-view" onScroll={onScrollMock}> | ||
<Text>XD</Text> | ||
</ScrollView> | ||
); | ||
fireEvent.scroll(getByTestId('scroll-view'), eventData); | ||
``` | ||
## `debug` | ||
@@ -177,1 +289,38 @@ | ||
``` | ||
## `query` APIs | ||
Each of the get APIs listed in the render section above have a complimentary query API. The get APIs will throw errors if a proper node cannot be found. This is normally the desired effect. However, if you want to make an assertion that an element is not present in the hierarchy, then you can use the query API instead: | ||
```jsx | ||
import { render } from 'react-native-testing-library'; | ||
const { queryByText } = render(<Form />); | ||
const submitButton = queryByText('submit'); | ||
expect(submitButton).toBeNull(); // it doesn't exist | ||
``` | ||
## `queryAll` APIs | ||
Each of the query APIs have a corresponding queryAll version that always returns an Array of matching nodes. getAll is the same but throws when the array has a length of 0. | ||
```jsx | ||
import { render } from 'react-native-testing-library'; | ||
const { queryAllByText } = render(<Forms />); | ||
const submitButtons = queryAllByText('submit'); | ||
expect(submitButtons).toHaveLength(3); // expect 3 elements | ||
``` | ||
<!-- badges --> | ||
[build-badge]: https://img.shields.io/circleci/project/github/callstack/react-native-testing-library/master.svg?style=flat-square | ||
[build]: https://circleci.com/gh/callstack/react-native-testing-library | ||
[version-badge]: https://img.shields.io/npm/v/react-native-testing-library.svg?style=flat-square | ||
[package]: https://www.npmjs.com/package/react-native-testing-library | ||
[license-badge]: https://img.shields.io/npm/l/react-native-testing-library.svg?style=flat-square | ||
[license]: https://opensource.org/licenses/MIT | ||
[prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square | ||
[prs-welcome]: http://makeapullrequest.com | ||
[chat-badge]: https://img.shields.io/discord/426714625279524876.svg?style=flat-square&colorB=758ED3 | ||
[chat]: https://discord.gg/QbGezWe |
// @flow | ||
import * as React from 'react'; | ||
import { isValidElementType } from 'react-is'; | ||
import TestRenderer from 'react-test-renderer'; // eslint-disable-line import/no-extraneous-dependencies | ||
import ShallowRenderer from 'react-test-renderer/shallow'; // eslint-disable-line import/no-extraneous-dependencies | ||
import prettyFormat, { plugins } from 'pretty-format'; // eslint-disable-line import/no-extraneous-dependencies | ||
import render from './render'; | ||
import shallow from './shallow'; | ||
import flushMicrotasksQueue from './flushMicrotasksQueue'; | ||
import debug from './debug'; | ||
import fireEvent from './fireEvent'; | ||
const getNodeByName = (node, name) => | ||
node.type.name === name || | ||
node.type.displayName === name || | ||
node.type === name; | ||
const getNodeByText = (node, text) => | ||
(getNodeByName(node, 'Text') || getNodeByName(node, 'TextInput')) && | ||
(typeof text === 'string' | ||
? text === node.props.children | ||
: text.test(node.props.children)); | ||
/** | ||
* Wait for microtasks queue to flush | ||
*/ | ||
export const flushMicrotasksQueue = (): Promise<any> => | ||
new Promise(resolve => setImmediate(resolve)); | ||
/** | ||
* Renders test component deeply using react-test-renderer and exposes helpers | ||
* to assert on the output. | ||
*/ | ||
export const render = ( | ||
component: React.Element<*>, | ||
options?: { createNodeMock: (element: React.Element<*>) => any } | ||
) => { | ||
const renderer = TestRenderer.create(component, options); | ||
const instance = renderer.root; | ||
return { | ||
getByTestId: (testID: string) => instance.findByProps({ testID }), | ||
getByName: (name: string | React.Element<*>) => | ||
instance.find(node => getNodeByName(node, name)), | ||
getAllByName: (name: string | React.Element<*>) => | ||
instance.findAll(node => getNodeByName(node, name)), | ||
getByText: (text: string | RegExp) => | ||
instance.find(node => getNodeByText(node, text)), | ||
getAllByText: (text: string | RegExp) => | ||
instance.findAll(node => getNodeByText(node, text)), | ||
getByProps: (props: { [propName: string]: any }) => | ||
instance.findByProps(props), | ||
getAllByProps: (props: { [propName: string]: any }) => | ||
instance.findAllByProps(props), | ||
update: renderer.update, | ||
unmount: renderer.unmount, | ||
}; | ||
}; | ||
/** | ||
* Renders test component shallowly using react-test-renderer/shallow | ||
*/ | ||
export const shallow = (instance: ReactTestInstance | React.Element<*>) => { | ||
const renderer = new ShallowRenderer(); | ||
if (isValidElementType(instance)) { | ||
// $FlowFixMe - instance is React.Element<*> in this branch | ||
renderer.render(instance); | ||
} else { | ||
renderer.render(React.createElement(instance.type, instance.props)); | ||
} | ||
const output = renderer.getRenderOutput(); | ||
return { | ||
output, | ||
}; | ||
}; | ||
/** | ||
* Log pretty-printed shallow test component instance | ||
*/ | ||
export const debug = ( | ||
instance: ReactTestInstance | React.Element<*>, | ||
message?: any | ||
) => { | ||
const { output } = shallow(instance); | ||
// eslint-disable-next-line no-console | ||
console.log( | ||
prettyFormat(output, { | ||
plugins: [plugins.ReactTestComponent, plugins.ReactElement], | ||
}), | ||
message | ||
); | ||
}; | ||
export { render }; | ||
export { shallow }; | ||
export { flushMicrotasksQueue }; | ||
export { debug }; | ||
export { fireEvent }; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
30784
20
373
321
12
1