Unit test: how to test redux "Provider"? - reactjs

I have a function like following, this is a rudux to provide EstateList.
import {render} from "react-dom";
import React from "react";
import {Provider} from "react-redux";
import store from "./Store";
import EstateList from "../estates/estateList/estateList";
function estateList() {
render(
<Provider store={store}>
<EstateList />
</Provider>,
window.document.getElementById('estateList')
);
}
And EstateList is like this:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {connect} from "react-redux";
class EstateList extends Component {
constructor(){
...
console.log('this can not be seen');
}
render() {...}
}
const mapStateToProps = (state) => {
return {
title: state.subNavReducer.title
};
};
const mapDispatchToProps = (dispatch) => {
return {
setSubNav: (title) => {
dispatch({
type: "SET_TITLE",
payload: title
});
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(EstateList);
I tried to test EstateList, like following:
import {shallow, configure} from 'enzyme';
import Adapter from 'enzyme-adapter-react-15';
import {expect} from 'chai';
import {Provider} from "react-redux";
import store from "../../../components/store/Store";
import EstateList from "../../../components/estates/estateList/estateList";
//import {EstateList} from "../../../components/estates/estateList/estateList";
describe('estate', function () {
it('getArticlesFromDatabase ', function () {
let app = shallow(
<Provider store={store}>
<EstateList />
</Provider>
);
});
});
But it shows this warning message:
console.error node_modules/fbjs/lib/warning.js:33
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite
components) but got: undefined. You likely forgot to export your
component from the file it's defined in.
How to fix it?
I had changed
import {EstateList} from "../../../components/estates/estateList/estateList";
to
import EstateList from "../../../components/estates/estateList/estateList";
Then the warning message gone.
But seems like class estateList still not be runed.
Cause when I run the test, I haven't seen the console.log which I add inside estateList's constructor.
By the way, If I changed
it('getArticlesFromDatabase ', function () {
let app = shallow(
<Provider store={store}>
<EstateList />
</Provider>
);
});
to
it('getArticlesFromDatabase ', function () {
let app = shallow(
<Provider store={store}>
<EstateList />
</Provider>
).dive();
});
then I will get following error message:
Invariant Violation: Could not find "store" in either the context or
props of "Connect(EstateList)". Either wrap the root component in a
, or explicitly pass "store" as a prop to
"Connect(EstateList)".

you shouldn't have to write a test for provider, the libary authors have already done that for you.
You are importing your EstateList component wrong, you export default'd it, and then are doing a object destructure import for named imports.
just import EstateList from 'path'

Related

How to test components using Mobx stores with Jest

I'm trying to test my React components using Mobx stores with Jest and React-testing-library.
The problem is that I have no clues on how to inject my stores for the test.
Here is my simplified codes.
StaffInfo.js(component)
import React, { useState } from "react";
import { observer, inject } from "mobx-react";
const StaffInfo = props => {
const store = props.instituteStore;
const [staffs, setStaffs] = useState(store.staffs);
return (
<div>
....
</div>
);
}
export default inject(rootStore => ({
instituteStore : rootStore.instituteStore
}))(observer(StaffInfo));
index.js(Root store)
import LoginStore from "./LoginStore";
import InstituteStore from "./InstituteStore";
class RootStore {
constructor(){
this.loginStore = new LoginStore (this);
this.instituteStore = new InstituteStore(this);
}
}
export default RootStore;
InstituteStore.js(target store)
import { observable, action } from "mobx";
class InstituteStore {
constructor(root){
this.root = root;
}
#observable
staffs = [];
}
export default InstituteStore;
StaffInfo.test.js(test file)
import React from "react";
import ReactDom from "react-dom";
import { MemoryRouter } from "react-router-dom";
import { Provider } from "mobx-react";
import StaffInfo from "./StaffInfo";
import InstituteStore from "../stores/InstituteStore";
describe("Staff Component testing", () => {
test("should be rendered without crashing", () => {
const div = document.createElement("div");
ReactDOM.render(
<MemoryRouter initialEntries={["/staff"]}>
<StaffInfo instituteStore={RootStore.instituteStore} />
</MemoryRouter>,
div
);
ReactDOM.unmountComponentAtNode(div);
});
});
As soon as running this test file, the error messages are like :
TypeError : Cannot read property 'staffs' of undefined
Please tell me which parts of the codes are wrong.
Thanks so much in advance!
Mobx-react's Inject is used to insert stores to the deep child component. These stars are provided by the context-based API Provider.
so wherever you are providing the stores to the child components use something like.
import rootStore from 'path_to_rootStore'
<Provider rootStore={rootStore}>
...
...
<App/>
...
...
<.Provider>
Thanks to #uneet7:
Legend! Finally someone gave a sensible answer :D
This is what My component looks like and
#inject('routing', 'navigationStore')
#observer
export default class PageTitle extends React.Component<*> {...}
And this is how I made it work:
let view = mount(
<Provider {...getStores()}>
<UserPage notificationStore={notificationStore} routing={routing} />
</Provider>
);
So the UserPage has components (many) and one of those components has PageTitle component. Obviously PageTitle has the #inject on it. It doesn't matter, as Provider HOC will provide stores via inject function to the component props.

Error: Invariant Violation: Element type is invalid just while testing

Files:
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './containers/App/App.js';
import { Provider } from 'react-redux';
import { store } from './store';
ReactDOM.render(
(
<Provider store={store}>
<App />
</Provider>
),
document.getElementById('root') || document.createElement("div")
);
//App.js
import React, { useEffect, useState } from "react";
import { getUser } from "../../firebase/user";
import Dashboard from "../Dashboard";
import SignIn from "../SignIn";
import { useSelector, useDispatch } from "react-redux";
import "../../styles/Global.scss";
import "../../styles/App.scss";
import "../../styles/Transitions.scss";
import "../../styles/Form.scss";
import "../../styles/Icons.scss";
import "../../styles/Cursors.scss";
const App = () => {
const dispatch = useDispatch();
const userId = useSelector(({ user: { userId } }) => userId);
const [ content, setContent ] = useState("");
useEffect(() => { dispatch(getUser()) }, []);
useEffect(() => {
if (userId) {
setContent("dashboard");
} else if (!userId && userId !== undefined) {
setContent("sign-in");
}
}, [userId])
console.log("Update App");
const Content = () => {
console.log("Update App Content")
if (userId === undefined) {
return null;
}
if (userId) {
return <Dashboard />;
} else {
return <SignIn />;
}
};
return (
<div className={`content-wrapper ${content}`}>
{Content()}
</div>
);
};
export default App;
//App.test.js
import React from "react";
import { shallow } from "enzyme";
import App from "./App";
describe('App', () => {
it('should render correctly', () => {
const Component = shallow(<App />);
expect(Component).toMatchSnapshot();
});
});
Full error:
First part:
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Second part (?!):
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check your code at index.js:11.
I know that there's loads of questions about this error while testing react with jest and enzyme, and with the redux provider wrapper etc.. Read a lot of them, but unfortunately none helped. Any ideas?

Using a Provider but still seeing error Invariant Violation: Could not find "store" in the context of "Connect

I have looked at several answers but all recommend to wrap the main component in a Provider.
I have already done that but the error remains.
This is my App.js component
const App = ({ store }) =>
<Provider store={store}>
<div className="App">
<Users/>
</div>
</Provider>
And I am doing a very simple test. First time using enzyme,
import React from 'react'
import Adapter from 'enzyme-adapter-react-16'
import Users from './'
import { shallow, configure } from 'enzyme'
configure({adapter: new Adapter()});
describe('First React component test with Enzyme', () => {
it('renders without crashing', () => {
shallow(<Users />);
});
});
The error is:
Invariant Violation: Could not find "store" in the context of "Connect(Users)". Either wrap the root component in a Provider, or pass a custom React context provider to Provider and the corresponding React context consumer to Connect(Users) in connect options.
A possible solution is as follows:
import React from "react";
import { shallow } from "enzyme";
import { Provider } from "react-redux";
import configureMockStore from "redux-mock-store";
import Userfrom "../User";
const mockStore = configureMockStore();
const store = mockStore({});
describe('First React component test with Enzyme', () => {
it('renders without crashing', () => {
shallow(
<Provider store={store}>
<User/>
</Provider>
);
});
});

Field must be inside a component decorated with reduxForm(), Error in test file

When i run my tests, i get the error:
Field must be inside a component decorated with reduxForm()
I am mocking a store, so i would think that would take care of injecting redux on the test but, i'm not really sure.
Inside appointments.js I have a component that has a redux form
import React from 'react';
... other imports
import configureMockStore from 'redux-mock-store';
import { mount } from 'enzyme';
import expect from 'expect';
import { Provider } from 'react-redux';
import { IntlProvider } from 'react-intl';
import LoginSection from '../User/LoginSection';
import AppointmentsContainer from './AppointmentsContainer';
import Appointments from './Appointments';
import AppointmentStatus from .../Layout/AppointmentStatus/AppointmentStatusContainer';
jest.mock('./Appointments');
jest.mock('../User/LoginSection');
jest.mock('../Layout/AppointmentStatus/AppointmentStatusContainer');
const store = configureMockStore()({
form: 'Appointments',
});
const setup = (newProps) => {
const props = {
handleSubmit: jest.fn(),
},
form: 'appointmentsContainer',
locale: 'en',
...newProps,
};
const root = mount(
<Provider store={store}>
<IntlProvider {...props}>
<AppointmentsContainer {...props} />
</IntlProvider>
</Provider>
,
);
const wrapper = root.find(Appointments);
return {
root,
wrapper,
props,
};
};
describe('AppointmentsContainer', () => {
beforeEach(() => {
store.clearActions();
});
Any idea how can i fix this?

TypeError: dispatch is not a function when testing with react-create-app jest and enzyme

I'm trying to setup testing on a new project created with react-create-app. Which now seems to be using React 16 and Jest 3 (which supposedly had some breaking changes, or maybe that was enzime). I'm getting an error similar to this post TypeError: dispatch is not a function when I try to test a method using JEST
TypeError: dispatch is not a function
at App.componentDidMount (src/components/App.js:21:68)
import React from 'react';
import { Provider } from 'react-redux';
import { mount } from 'enzyme';
import { App } from '../components/App';
import configureStore from '../state/store/configureStore';
window.store = configureStore({
slider: {
mainImageIndex: 0,
pageNum: 1,
perPage: 4,
},
});
const appTest = (
<Provider store={window.store}>
<App />
</Provider>
);
describe('App', () => {
it('should render without crashing', () => {
mount(appTest);
});
});
Originally I just tried to do this:
import React from 'react';
import { mount } from 'enzyme';
import { App } from '../components/App';
describe('App', () => {
it('should render without crashing', () => {
mount(<App />);
});
});
Which threw this error
Invariant Violation: Could not find "store" in either the context or props of "Connect(Form(SearchForm))". Either wrap the root component in a , or explicitly pass "store" as a prop
Code for App.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { searchPhotos } from '../state/actions/searchPhotos';
import { setMainImageIndex, setFirstPage } from '../state/actions/slider';
import Slider from './Slider';
import SearchForm from './SearchForm';
import Error from './Error';
import '../styles/App.css';
export class App extends Component {
componentDidMount() {
const { dispatch } = this.props;
dispatch(searchPhotos(window.store));
}
searchPhotosSubmit = () => {
const { dispatch } = this.props;
dispatch(setFirstPage());
dispatch(setMainImageIndex(0));
dispatch(searchPhotos(window.store));
}
render() {
const { fetchError } = this.props;
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Flickr Slider in React.js</h1>
<SearchForm onSubmit={this.searchPhotosSubmit} />
</header>
{!fetchError ? <Slider /> : <Error />}
</div>
);
}
}
export default connect(state => ({
fetchError: state.fetchError,
form: state.form,
slider: state.slider,
}))(App);
Please not that you export both presentational component (as named export) and container component (as default export) in App.js. Then in your tests you import and use the presentational component using:
import { App } from '../components/App';
but you should import connected container component instead using:
import App from '../components/App'; // IMPORTANT! - no braces around `App`
Since you're using component that is not connected to Redux store dispatch prop is not injected as prop. Just use correct import and it should work.
For more details about importing default and named exports please check this doc. About presentational and container components you can read here.

Resources