diff --git a/docs/ReduxIntegration.md b/docs/ReduxIntegration.md new file mode 100644 index 000000000..20d6975df --- /dev/null +++ b/docs/ReduxIntegration.md @@ -0,0 +1,137 @@ +--- +id: redux-integration +title: Redux Integration +--- + +This section deals with testing RN applications developed with Redux. We will be developing a simple TODO application capable of adding and removing an item. Once included, the timestamp is included. + +## Setting up + +An example of setting up can be found [here](https://github.com/callstack/react-native-testing-library/tree/master/examples/redux). + +## Test cases + +Our test is on the components that either dispatch actions on the redux store or read some data from the redux store. This means we will test `./components/TodoElem.js` and `./components/TodoList.js`. Thus we will create `./components/AddTodo.test.js` and `./components/TodoList.test.js` + +For `./components/AddTodo.test.js` + +```jsx +import React from 'react'; +import { Provider } from 'react-redux'; +import { cleanup, fireEvent, render } from 'react-native-testing-library'; +import configureStore from '../store'; +import AddTodo from './AddTodo'; + +describe('AddTodo component test', () => { + test('adds a new TODO when the button is pressed', () => { + const store = configureStore(); + + const component = ( + + + + ); + + const { getByPlaceholder, getByText } = render(component); + + // There is a TextInput. + // https://github.com/callstack/react-native-testing-library/blob/ae3d4af370487e1e8fedd8219f77225690aefc59/examples/redux/components/AddTodo.js#L24 + const input = getByPlaceholder(/repository/i); + expect(input).toBeTruthy(); + + const textToEnter = 'This is a random element'; + fireEvent.changeText(input, textToEnter); + fireEvent.press(getByText('Submit form')); + + const todosState = store.getState().todos; + + expect(todosState.length).toEqual(1); + + expect(todosState).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: 1, + text: textToEnter, + date: expect.any(Date), + }), + ]) + ); + }); +}); +``` + +For the `./components/TodoList.js` + +```jsx +import React from 'react'; +import { Provider } from 'react-redux'; +import { fireEvent, render } from 'react-native-testing-library'; +import configureStore from '../store'; +import TodoList from './TodoList'; + +describe('TodoList component test', () => { + test('it should execute with a store with 4 elements', () => { + const initialState = { + todos: [ + { id: 1, text: 'Sing something', date: new Date() }, + { id: 2, text: 'Dance something', date: new Date() }, + { id: 3, text: 'Sleep something', date: new Date() }, + { id: 4, text: 'Sleep something', date: new Date() }, + ], + }; + const store = configureStore(initialState); + + const component = ( + + + + ); + + const { getAllByText } = render(component); + const todoElems = getAllByText(/something/i); + + expect(todoElems.length).toEqual(4); + }); + + test('should execute with 2 elements and end up with 1 after delete', () => { + const initialState = { + todos: [ + { id: 1, text: 'Sing something', date: new Date() }, + { id: 2, text: 'Dance something', date: new Date() }, + ], + }; + const store = configureStore(initialState); + + const component = ( + + + + ); + + const { getAllByText } = render(component); + const todoElems = getAllByText(/something/i); + + expect(todoElems.length).toBe(2); + + const buttons = getAllByText('Delete'); + expect(buttons.length).toBe(2); + + fireEvent.press(buttons[0]); + expect(getAllByText('Delete').length).toBe(1); + }); +}); +``` + +## Running tests + +To run the tests, place a test script inside your package.json + +```json +{ + "scripts": { + "test": "jest" + } +} +``` + +And run the test script with npm test or yarn test. diff --git a/examples/redux/App.js b/examples/redux/App.js new file mode 100644 index 000000000..9712cdf2d --- /dev/null +++ b/examples/redux/App.js @@ -0,0 +1,27 @@ +import React from 'react'; +import { StyleSheet, View, StatusBar } from 'react-native'; +import { Provider } from 'react-redux'; +import configureStore from './store'; +import AddTodo from './components/AddTodo'; +import TodoList from './components/TodoList'; + +const store = configureStore(); + +export default function App() { + return ( + + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingTop: 32, + }, +}); diff --git a/examples/redux/actions/todoActions.js b/examples/redux/actions/todoActions.js new file mode 100644 index 000000000..b83a37153 --- /dev/null +++ b/examples/redux/actions/todoActions.js @@ -0,0 +1,25 @@ +export const actions = { + ADD: '@ADD_TODO', + REMOVE: '@REMOVE_TODO', + MODIFY: '@MODIFY_TODO', + CLEAR: '@CLEAR_TODO', +}; + +export const addTodo = (todo) => ({ + type: actions.ADD, + payload: todo, +}); + +export const removeTodo = (id) => ({ + type: actions.REMOVE, + payload: { id }, +}); + +export const modifyTodo = (todo) => ({ + type: actions.MODIFY, + payload: todo, +}); + +export const clearTodos = () => ({ + type: actions.CLEAR, +}); diff --git a/examples/redux/babel.config.js b/examples/redux/babel.config.js new file mode 100644 index 000000000..4d710acf8 --- /dev/null +++ b/examples/redux/babel.config.js @@ -0,0 +1,6 @@ +module.exports = function (api) { + api.cache(true); + return { + presets: ['module:metro-react-native-babel-preset'], + }; +}; diff --git a/examples/redux/components/AddTodo.js b/examples/redux/components/AddTodo.js new file mode 100644 index 000000000..7ab31fc89 --- /dev/null +++ b/examples/redux/components/AddTodo.js @@ -0,0 +1,73 @@ +import React from 'react'; +import { Button, StyleSheet, Text, View, TextInput } from 'react-native'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { addTodo } from '../actions/todoActions'; + +export function AddTodo(props) { + const [text, setText] = React.useState(''); + + const submitForm = () => { + const todo = { + id: props.todoLength + 1, + text, + date: new Date(), + }; + + props.addTodo(todo); + setText(''); + }; + + return ( + + Enter a text below to add a new todo + setText(t)} + placeholder="Enter the name of the repository here" + /> + +