import rootReducer from '../my-rootreducer-dir';
function renderWithRedux(ui, { initialState, store = createStore(rootReducer, initialState) } = {}, renderFn = render) {
const obj = {
...renderFn(<Provider store={store}>{ui}</Provider>),
store,
};
obj.rerenderWithRedux = (el, nextState) => {
if (nextState) {
store.replaceReducer(() => nextState);
store.dispatch({ type: '__TEST_ACTION_REPLACE_STATE__' });
store.replaceReducer(rootReducer);
}
return renderWithRedux(el, { store }, obj.rerender);
};
return obj;
}
// FileCmp.js
export default ({ isUploading, sadFace }) => <>
{isUploading ? 'upload in progress' : '...'}
{sadFace && ':('}
</>
// FileContainter.js
import React from 'react';
import { connect } from 'react-redux';
import FileCmp from 'dir-to/FileCmp';
import { makeSelectIsUploading } from '../selectors';
const mapStateToProps = (state, { id }) => ({
isUploading: makeSelectIsUploading(id)(state),
})
export default connect(mapStateToProps)(FileCmp)
// FileCmp.test.js
test('some crazy test', () => {
const initialState = {
isUploading: false
}
const { rerenderWithRedux, store } = renderWithRedux(<FileCmp />, { initialState })
// we can update store by dispatching some action eg
store.dispatch({ type: '__FILE_UPLOAD_ACTION__', id: 1 }) // produces new state --> { isUploading: true }
expect(getByText('upload in progress')).toBeInTheDOM()
// we can also update store by providing next state
const nextState = { isUploading: false }
rerenderWithRedux(<FileCmp />, nextState)
expect(() => getByText('upload in progress')).toThrow()
// and last but not least, if we invoke rerenderWithRedux only with one arg -> component
// it will use store as expected
rerenderWithRedux(<FileCmp sadFace />)
expect(getByText(':(')).toBeInTheDOM()
expect(() => getByText('upload in progress')).toThrow()
})
where is
getByText
coming from?