Skip to content

Redux Integration #292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions docs/ReduxIntegration.md
Original file line number Diff line number Diff line change
@@ -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 = (
<Provider store={store}>
<AddTodo />
</Provider>
);

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 = (
<Provider store={store}>
<TodoList />
</Provider>
);

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 = (
<Provider store={store}>
<TodoList />
</Provider>
);

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.
27 changes: 27 additions & 0 deletions examples/redux/App.js
Original file line number Diff line number Diff line change
@@ -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 (
<Provider store={store}>
<View style={styles.container}>
<StatusBar barStyle="dark-content" />
<AddTodo />
<TodoList />
</View>
</Provider>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 32,
},
});
25 changes: 25 additions & 0 deletions examples/redux/actions/todoActions.js
Original file line number Diff line number Diff line change
@@ -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,
});
6 changes: 6 additions & 0 deletions examples/redux/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['module:metro-react-native-babel-preset'],
};
};
73 changes: 73 additions & 0 deletions examples/redux/components/AddTodo.js
Original file line number Diff line number Diff line change
@@ -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 (
<View style={styles.container}>
<Text style={styles.header}>Enter a text below to add a new todo</Text>
<TextInput
autoFocus
value={text}
style={styles.input}
returnKeyType="search"
onSubmitEditing={submitForm}
onChangeText={(t) => setText(t)}
placeholder="Enter the name of the repository here"
/>

<Button onPress={submitForm} title="Submit form" />
</View>
);
}

const styles = StyleSheet.create({
container: {
minHeight: 156,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
borderBottomColor: '#EEEEEE',
borderBottomWidth: 2,
marginBottom: 16,
padding: 16,
},
header: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 8,
},
input: {
borderColor: '#DDDDDD',
borderWidth: 1,
paddingVertical: 8,
width: '100%',
textAlign: 'center',
borderRadius: 4,
},
});

const mapStateToProps = ({ todos }) => ({
todoLength: todos.length,
});

const mapDispatchToProps = (dispatch) =>
bindActionCreators({ addTodo }, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(AddTodo);
42 changes: 42 additions & 0 deletions examples/redux/components/AddTodo.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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('Application test', () => {
afterEach(cleanup);

test('adds a new test when entry has been included', () => {
const store = configureStore();

const component = (
<Provider store={store}>
<AddTodo />
</Provider>
);

const { getByPlaceholder, getByText } = render(component);

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),
}),
])
);
});
});
25 changes: 25 additions & 0 deletions examples/redux/components/TodoElem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { StyleSheet, Button, Text, View } from 'react-native';

export default function TodoElem({ todo, onDelete }) {
return (
<View style={styles.container}>
<View style={styles.textContainer}>
<Text style={styles.text}>{todo.text}</Text>
<Text style={styles.date}>{new Date(todo.date).toDateString()}</Text>
</View>
<View style={styles.buttonContainer}>
<Button onPress={() => onDelete(todo.id)} title="Delete" />
</View>
</View>
);
}

const styles = StyleSheet.create({
container: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
});
29 changes: 29 additions & 0 deletions examples/redux/components/TodoList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { FlatList, Text } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { removeTodo } from '../actions/todoActions';
import TodoElem from './TodoElem';

export function TodoList(props) {
const onDeleteTodo = (id) => props.removeTodo(id);

return (
<FlatList
data={props.todos}
keyExtractor={(todo) => todo.id.toString()}
renderItem={({ item }) => (
<TodoElem todo={item} onDelete={onDeleteTodo} />
)}
/>
);
}

const mapStateToProps = (state) => ({
todos: state.todos,
});

const mapDispatchToProps = (dispatch) =>
bindActionCreators({ removeTodo }, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
Loading