-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Description
I'm having a problem, I'm using Enzyme
, Jest
, react-router
, React
, and the enzyme-async-helpers
in my project. Full project link.
But how do I wait for a child component state?
I need this, because to render my component I use the react-router
.
Code of my tests and component:
/* App.jsx */
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import * as BooksAPI from './BooksAPI';
import './App.css';
import HomePage from './components/HomePage';
import SearchPage from './components/SearchPage';
class BooksApp extends Component {
constructor(props) {
super(props);
this.state = {
myBooks: [],
booksSearch: [],
query: '',
loading: true,
};
}
componentDidMount() {
// Capturando todos os MEUS LIVROS da api
BooksAPI.getAll().then((books) => {
this.setState({
myBooks: books,
loading: false,
});
}).catch(err => console.err('[componentDidMount] App -', err));
}
addBook = (book, newShelf) => {
const newBook = book;
newBook.shelf = newShelf;
this.setState(currentState => ({
myBooks: currentState.myBooks.concat([newBook]),
}));
}
setShelfBook = (book, books, newShelf) => {
const newBooks = books.map((myBook) => {
// Percorre por toda lista dos MyBooks já cadastrados para mudar shelf do livro atualizado
if (myBook.id === book.id) {
const newMyBook = myBook;
newMyBook.shelf = newShelf;
return newMyBook;
}
return myBook;
});
return newBooks;
}
movBookShelf = (book, newShelf) => {
const { myBooks, booksSearch } = this.state;
const newMyBooks = this.setShelfBook(book, myBooks, newShelf);
const newBooksSearch = this.setShelfBook(book, booksSearch, newShelf);
this.setState({
myBooks: newMyBooks,
booksSearch: newBooksSearch,
});
}
delBook = (book) => {
const { booksSearch } = this.state;
const newBooksSearch = this.setShelfBook(book, booksSearch, 'none');
this.setState(currentState => ({
myBooks: currentState.myBooks.filter(myBook => myBook.id !== book.id),
booksSearch: newBooksSearch,
}));
}
checkMyBooksHaveChanged = (resultUpdate) => {
const { myBooks } = this.state;
const currentLengthMyBooks = myBooks.length;
const { currentlyReading, wantToRead, read } = resultUpdate;
const newLengthMyBooks = currentlyReading.length + wantToRead.length + read.length;
return currentLengthMyBooks !== newLengthMyBooks;
}
getShelfBook = (book) => {
const { myBooks } = this.state;
const filterBook = myBooks.filter(myBook => myBook.id === book.id);
if (filterBook.length > 0) {
return filterBook[0].shelf;
}
return 'none';
}
addShelfBooksSearch = (booksSearch) => {
const books = booksSearch.map((bookSearch) => {
const book = bookSearch;
book.shelf = this.getShelfBook(book);
return book;
});
return books;
}
updateBook = (book, newShelf) => {
// Atualizando shelf de um livro
BooksAPI.update(book, newShelf).then((result) => {
// Caso consiga atualizar
if (newShelf === 'none') {
this.delBook(book);
} else if (this.checkMyBooksHaveChanged(result) === true) {
this.addBook(book, newShelf);
} else {
this.movBookShelf(book, newShelf);
}
});
}
updateQuery = (newQuery) => {
this.setState({
query: newQuery,
});
if (newQuery !== '') {
BooksAPI.search(newQuery).then((response) => {
const { error } = response;
const booksSearch = error ? [] : this.addShelfBooksSearch(response);
this.setState({
booksSearch,
});
}).catch(err => console.error('deu ruim', err));
}
}
render() {
const { myBooks, booksSearch, query } = this.state;
return (
<div className="app">
<Route
exact
path="/"
render={() => (
<HomePage myBooks={myBooks} updateBook={this.updateBook} />
)}
/>
<Route
path="/search"
render={() => (
<SearchPage
books={booksSearch}
query={query}
updateQuery={this.updateQuery}
updateBook={this.updateBook}
/>
)}
/>
</div>
);
}
}
export default BooksApp
/* App.test.jsx */
import React from 'react';
import { shallow, mount } from 'enzyme';
import { MemoryRouter } from 'react-router';
import { waitForState } from 'enzyme-async-helpers';
import App from './App';
import MyList from './components/HomePage/MyListsBooks/MyList';
import ListBooks from './components/ListBooks';
import Book from './components/ListBooks/Book';
describe('[component] App', () => {
const PATHS = {
homePage: '/',
searchPage: '/search',
};
const CONFIG_API_REQUEST = { timeout: 5000 };
const LIST_BOOKS = [
{
title: 'The Linux Command Line',
authors: [
'William E. Shotts, Jr.',
],
imageLinks: {
smallThumbnail: 'http://books.google.com/books/content?id=nggnmAEACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api',
thumbnail: 'http://books.google.com/books/content?id=nggnmAEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api',
},
id: 'nggnmAEACAAJ',
shelf: 'read',
},
{
title: 'The Sebastian Thrun Handbook - Everything You Need to Know about Sebastian Thrun',
authors: [
'Andre Cantrell',
],
imageLinks: {
smallThumbnail: 'http://books.google.com/books/content?id=NqkKvgAACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api',
thumbnail: 'http://books.google.com/books/content?id=NqkKvgAACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api',
},
id: 'NqkKvgAACAAJ',
shelf: 'wantToRead',
},
{
title: 'Travel by Design',
authors: [
'Marlon G. Boarnet',
'Randall Crane',
],
imageLinks: {
smallThumbnail: 'http://books.google.com/books/content?id=f7EtoDfL6yYC&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api',
thumbnail: 'http://books.google.com/books/content?id=f7EtoDfL6yYC&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api',
},
id: 'f7EtoDfL6yYC',
shelf: 'currentlyReading',
},
];
it('check init state', () => {
const initStateExptected = {
myBooks: [],
booksSearch: [],
query: '',
loading: true,
};
const wrapper = shallow(<App />);
expect(wrapper.state()).toEqual(initStateExptected);
});
it('check loading state', async () => {
const wrapper = shallow(<App />);
await waitForState(wrapper, state => state.loading === false, CONFIG_API_REQUEST);
const { loading } = wrapper.state();
expect(loading).toBeFalsy();
});
describe('HomePage', () => {
it('check my lists books', () => {
const wrapper = mount((
<MemoryRouter initialEntries={[PATHS.homePage]}>
<App />
</MemoryRouter>
));
expect(wrapper.exists('MyListsBooks')).toBeTruthy();
});
it('check three my list', () => {
const lengthMyListExpected = 3;
const wrapper = mount((
<MemoryRouter initialEntries={[PATHS.homePage]}>
<App />
</MemoryRouter>
));
const myListsBooks = wrapper.find(MyList);
expect(myListsBooks).toHaveLength(lengthMyListExpected);
});
it('check three lists books', () => {
const lengthListsBooksExpected = 3;
const wrapper = mount((
<MemoryRouter initialEntries={[PATHS.homePage]}>
<App />
</MemoryRouter>
));
const myListsBooks = wrapper.find(ListBooks);
expect(myListsBooks).toHaveLength(lengthListsBooksExpected);
});
it('check empty books rendering init state', () => {
const wrapper = mount((
<MemoryRouter initialEntries={[PATHS.homePage]}>
<App />
</MemoryRouter>
));
expect(wrapper.exists('Book')).toBeFalsy();
});
it('check books rendering', () => {
const lengthBooksExpected = 3;
const wrapper = mount((
<MemoryRouter initialEntries={[PATHS.homePage]}>
<App />
</MemoryRouter>
));
const appComponent = wrapper.find(App).instance();
appComponent.setState({ myBooks: LIST_BOOKS });
wrapper.update();
const booksSearch = wrapper.find(Book);
expect(booksSearch).toHaveLength(lengthBooksExpected);
});
});
describe('SearchPage', () => {
it('check lists books', () => {
const wrapper = mount((
<MemoryRouter initialEntries={[PATHS.searchPage]}>
<App />
</MemoryRouter>
));
expect(wrapper.exists('ListBooks')).toBeTruthy();
});
it('check empty books rendering init state', () => {
const wrapper = mount((
<MemoryRouter initialEntries={[PATHS.searchPage]}>
<App />
</MemoryRouter>
));
expect(wrapper.exists('Book')).toBeFalsy();
});
it('check books rendering', () => {
const lengthBooksExpected = 3;
const wrapper = mount((
<MemoryRouter initialEntries={[PATHS.searchPage]}>
<App />
</MemoryRouter>
));
const appComponent = wrapper.find(App).instance();
appComponent.setState({ booksSearch: LIST_BOOKS });
wrapper.update();
const booksSearch = wrapper.find(Book);
expect(booksSearch).toHaveLength(lengthBooksExpected);
});
xit('check update query search book', () => {
const newQuery = 'react';
const wrapper = mount((
<MemoryRouter initialEntries={[PATHS.searchPage]}>
<App />
</MemoryRouter>
));
const search = wrapper.find('SearchBooksBar input');
const target = { value: newQuery };
search.simulate('change', { target });
wrapper.update();
console.log(wrapper.debug());
});
});
});
Activity
zth commentedon Dec 23, 2018
Hi!
I've moved away from using this library myself in favor of
react-testing-library
, and I encourage you to do the same.react-testing-library
is a little bit different, but it avoids testing implementation details like Enzyme (and this library) encourages you to do.Good luck!
gustafsilva commentedon Dec 23, 2018
Thanks @zth for the tip 👍
I'll take a look right now! 😄