Reputation: 21
I'm playing around with react and jest and I've came to the following situation where I simply cannot figure it out how should I do it.
Todo.js
import React from 'react';
import PropTypes from 'prop-types';
import TodoItem from './TodoItem';
import {fetchTodoItems} from '../helpers/todo';
class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [],
error: false,
loading: true
};
this.updateMockState = this.updateMockState.bind(this);
}
updateMockState() {
this.setState({
todos: [{ id: 8101, text: "Cook fresh food", status: "completed" }],
loading: false
});
}
componentDidMount() {
// TODO: add error handling
fetchTodoItems().then(response => {
this.setState({
todos: response.data,
loading: false
})
}, error => {});
}
render() {
let content;
// TODO: add error handling
if (this.state.loading) {
return (
<div>
<div>
<button id="update-data" onClick={this.updateMockState}>Update State</button>
</div>
<p>Todos are being fetched...</p>
</div>
);
}
return (
content ||
<div>
<div>
<button id="update-data" onClick={this.updateMockState}>Update State</button>
</div>
<p><b>{this.state.todos.length}</b> todos fetched so far</p>
{this.state.todos.map(
(todo) => <TodoItem key={todo.id} id={todo.id} status={todo.status} text={todo.text} />)}
</div>
);
}
}
Todo.proptypes = {
timeout: PropTypes.number
};
export default Todo;
Todo.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import { mount, shallow, render } from 'enzyme';
import Todo from '../components/Todo';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import toJson from 'enzyme-to-json';
// TODO: remove sinon from NPM packages
Enzyme.configure({ adapter: new Adapter() });
const todos = { data: [{
"id": 4404,
"status": "active",
"text": "Shopping List"
}, {
"id": 7162,
"status": "completed",
"text": "Time Registration"
}]};
describe('Todo', () => {
it('calls mock API', async () => {
fetch = jest.fn().mockReturnValue(Promise.resolve(todos));
const spy = jest.spyOn(Todo.prototype, 'componentDidMount');
var component = mount(<Todo timeout={2000} />);
component.instance().componentDidMount();
expect(spy).toHaveBeenCalled();
expect(toJson(component)).toMatchSnapshot();
});
});
As you can see Todo component is a simple component that inside componentDidMount calls an API, get the response and displays. While waiting for the api call, some info is shown... There's also a button for dummy state update but that's not important for now.
fetchTodoItems (file is todo.js)
export const fetchTodoItems = () => {
return fetch("data/todo.json").then(res => res.json());
};
My problem is the following:
On 1st I'm supposed to see no todo items, but on 2nd I should see my todo items.
Here is TodoItem
import React from 'react';
import PropTypes from 'prop-types';
const TodoItem = (props) => {
let htmlClass = [];
if (props.status === 'completed') {
htmlClass.push("todo-completed");
}
return (
<ul>
<p className={htmlClass.join(" ")}>
<small>[#{props.id}]</small> {props.text} <i>({props.status})</i>
</p>
</ul>
);
}
TodoItem.proptypes = {
id: PropTypes.number.required,
text: PropTypes.string.required,
status: PropTypes.string.required
};
export default TodoItem;
So far I've tried the following:
pure snapshot testing with expect(component.toJSON()).toMatchSnapshot() - doesn't show results after API call
jest.spyON... the method is called but after that toMatchSnapshot still shows 1st snapshot with no data
return promise().then(... still no results
Any help?
EDIT:
If I remove API call from componentDidMount and use only setState, then snapshot testing shows todos items (without 1st case where it should say that todos are being fetched...) Why?? Shouldn't be the same?
If then only the promises are the root cause, how can I wait for all promises to finish?
Upvotes: 2
Views: 3438
Reputation: 51
One way would be to wait for the last event(promise) if any in the queue to be resolved and then do a component instance update ?
const allOver = () => new Promise((resolve) => setImmediate(resolve));
describe('Todo', () => {
it('calls mock API', async () => {
fetch = jest.fn().mockReturnValue(Promise.resolve(todos));
var component = mount(<Todo timeout={2000} />);
await allOver();
component.update();
expect(toJson(component)).toMatchSnapshot();
});
});
Upvotes: 1