Jest Store is Mocked -- Not detecting it? - javascript

I have some code I've pulled from another project I had done. I am moving the store here and passing the App as a child component.
Unfortunately I receive this error. I'm not sure why this doesn't work.
could not find react-redux context value; please ensure the component is wrapped in a <Provider>
29 |
30 | function App() {
> 31 | const { userData } = useSelector(state => state.user)
utils.js
import { Provider } from 'react-redux'
import store from "../redux/store"
const ReduxProvider = ({ children }) => (
<Provider store={store}>{children}</Provider>
)
export {
ReduxProvider
}
App.test.js
import { render } from '#testing-library/react';
import { ReduxProvider } from './utils'
import App from '../App';
test('renders', () => {
render(
<ReduxProvider>
<App />
</ReduxProvider>
);
});

Related

redux/react-redux Provider TypeError: Object(...) is not a function

Why am I getting a this error TypeError: Object(...) is not a function when trying to use the store prop with <Provider store={store}/>
//Error in browser from Create-React-App
TypeError: Object(...) is not a function
8 | var store = _ref.store,
9 | context = _ref.context,
10 | children = _ref.children;
> 11 | var contextValue = useMemo(function () {
| ^ 12 | var subscription = new Subscription(store);
13 | subscription.onStateChange = subscription.notifyNestedSubs;
14 | return {
//index.js (in root of project)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducers from './reducers/index.js'
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// My reducers folder in the same level. Has two files: index.js and photos.js
INDEX.JS
import { combineReducers } from 'redux'
import { Photos } from './photos'
const reducers = combineReducers({
Photos
})
export default reducers
PHOTOS.JS
import { SET_PHOTOS } from '../actions/photos'
const DATA = {
photos: []
}
export const Photos = (state = DATA, action) => {
switch(action.type) {
case SET_PHOTOS:
return {
...state,
photos: action.payload,
};
default:
return state
}
}
This is with a create-react-app settings. I dont really use it and this simple task I have done many times is giving me such a hard time.

Enzime: Wait that context is updated to start execute tests

I have a React app that I need to test. It's using the useContext() hook to create Provider that are using in most of my components. I have a dedicated component to handle a Context (lets say UserContext for the example) that look like that:
UserContext.jsx:
import React from 'react'
export const UserContext = React.createContext(undefined)
export const UserProvider = (props) => {
const [currentUser, setCurrentUser] = React.useState(undefined)
const context = {
currentUser,
setCurrentUser,
}
return (
<UserContext.Provider value={context}>
{props.children}
</UserContext.Provider>
)
}
So you can use the Provider like that:
import { UserProvider } from './context/UserContext'
<UserProvider>
{ ... }
</UserProvider>
Now I need to test a component that use this UserContext so let's say UserModal:
UserModal.test.jsx
import React from 'react'
import { mount } from 'enzyme'
import { BrowserRouter as Router } from 'react-router-dom'
import { UserProvider, UserContext } from '../context/UserContext'
import UserModal from '../components/UserModal'
// D A T A
import exampleUser from '../data/user.json' // Load user's data from a json file
describe('<UserModal />', () => {
let wrapper
const Wrapper = () => {
const { setCurrentUser } = React.useContext(UserContext)
React.useEffect(() => {
// Init UserContext value
setCurrentUser(exampleUser)
}, [])
return (
<UserProvider>
<UserModal />
</UserProvider>
)
}
beforeEach(() => {
wrapper = mount(<Wrapper />)
})
})
Problem is that when <UserModal /> is mounted inside of the <UserProvider>, I get an error that the currentUser in the UserContext is undefined. This error make sense because I call setCurrentUser() when the component is mounted once using React.useEffect(() => { }, []).
So have you an idea how I can mount() my <UserModal /> component inside of a context's provider in the way that the context is not undefined?
Your test should look like this:
import React from 'react'
import { mount } from 'enzyme'
import { BrowserRouter as Router } from 'react-router-dom'
import { UserProvider, UserContext } from '../context/UserContext'
import UserModal from '../components/UserModal'
// D A T A
import exampleUser from '../data/user.json' // Load user's data from a json file
describe('<UserModal />', () => {
let wrapper
const Wrapper = () => {
const { setCurrentUser } = React.useContext(UserContext)
React.useEffect(() => {
// Init UserContext value
setCurrentUser(exampleUser)
}, [])
return (
<UserModal />
)
}
beforeEach(() => {
wrapper = mount(<UserProvider><Wrapper /></UserProvider>)
})
})
Or see codesandbox here - simple test passes.
Note that UserProvider wraps Wrapper and not is used inside. It's like this because if you are using it inside, there is no UserContext to get with useContext hook, therefore there is no setCurrentUser function.

Can't access to state from redux action in jest

I cannot have my state when dispatch an action inside my jest unit test
I try to make like this :
import configureMockStore from 'redux-mock-store'
import * as React from 'react'
import MarginPage from './index'
import { Provider } from 'react-redux'
import {
mount,
} from 'enzyme'
import thunk from 'redux-thunk'
const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)
const state = {
margin: {
list: null,
},
}
it('Should list props are correctly filling', async () => {
await store.dispatch({
type: 'SET_MARGIN_LIST',
payload: [0, 1, 2],
})
const wrapper = mount(
<Provider store={store}>
<MarginPage />
</Provider>,
)
wrapper.update()
const actions = store.getActions() // Here I have my state
// But now I would like to have the updated state list inside getState()
console.log(store.getState().margin.list) // return undefined
})
I found an answer I hope its helping you if you encountered this problem.
We can take the above code and just update :
import * as React from 'react'
import MarginPage from './index'
import { Provider } from 'react-redux'
import {
mount,
} from 'enzyme'
// #ts-ignore
import store from '#App/helpers/configureStore' // Finally I prefer use store from my app
import { IAppState } from '#App/reducers' // This line come from interface of typeScript
describe('<MarginPage /> Component', () => {
it('Should list props are correctly filling', async () => {
// instanciate component and make the test use async/await
const wrapper = mount(
<Provider store={store}>
<MarginPage />
</Provider>,
)
await store.dispatch({
type: 'SET_MARGIN_LIST',
payload: MOCK_MARGIN_LIST,
})
// Don't forget to force the re-render
wrapper.update()
// Now we have access to our store
const state = (store.getState() as IAppState).margin.list as any
expect(state.results[0].campaigns_revenue).toEqual(47096.52)
})
})

Cannot import store a second time anywhere, doing so makes TabNavigator undefined

My app uses a TabNavigator in which I want to show a badge next to one of the tab icons based on a value in my Redux store. Because I store the routerConfiguration of the TabNavigator in a separate file the idea was to import the store, add a listener and use the value I want in my configuration. However, no matter in which module I try to import my store for a second time I get an error that causes my TabBar to become undefined (?!)
I've tried to remove any unnecessary code, but I simply can't see what's going wrong here.
My basic file structure is as follows (reduced for readability)
.
├── app.js
├── store.js
├── components
│   └── TabIcon.js
└── navigators
   ├── TabBarNavigation.js
   └── TabBarNavigationConfiguration.js
app.js
import React from 'react';
import { AppRegistry } from 'react-native'
import { Provider } from 'react-redux'
import TabBarNavigation from './navigators/TabBarNavigation'
import store from './store'
class App extends React.Component {
render() {
return (
<Provider store={store}>
<TabBarNavigation />
</Provider>
);
}
}
AppRegistry.registerComponent('testapp', () => App);
TabBarNavigation
import React from 'react';
// Navigation
import { addNavigationHelpers } from 'react-navigation'
import { TabBar } from './TabBarNavigationConfiguration'
// Redux
import { connect } from 'react-redux'
class TabBarNavigation extends React.Component {
render () {
const { dispatch, navigationState } = this.props
return (
<TabBar
navigation={
addNavigationHelpers({
dispatch: dispatch,
state: navigationState,
})
}
/>
)
}
}
const mapStateToProps = (state) => {
return {
navigationState: state.tabBarNavigation,
}
}
export default connect(mapStateToProps)(TabBarNavigation)
TabBarNavigationConfiguration
import React from 'react'
import { TabNavigator } from 'react-navigation'
// Tab-navigators
import GiveFeedbackNavigation from './GiveFeedbackNavigation'
import ViewFeedbackNavigation from './ViewFeedbackNavigation'
import TabIcon from '../components/TabIcon'
//import store from '../store'
store.subscribe(listener)
var newFeedbackCount
listener = () => {
newFeedbackCount = store.getState().feedback.newFeedbackCount
}
const routeConfiguration = {
// ...tab 1
// ...tab 2
ViewFeedbackNavigation: {
screen: ViewFeedbackNavigation,
navigationOptions: {
tabBarLabel: 'View Feedback',
tabBarIcon: ({ tintColor, focused }) => (
<TabIcon
icon={focused ? 'comments' : 'comments-o'}
size={24}
color={tintColor}
count={newFeedbackCount} // value from Redux store
/>
),
},
},
}
const tabBarConfiguration = {
//...
}
export const TabBar = TabNavigator( routeConfiguration, tabBarConfiguration )
store
// Redux
import { createStore } from 'redux'
import reducer from './reducers'
export default createStore(
reducer,
undefined, // initial state
undefined, // middleware
)
Pretty straight forward I believe, and working. However, the moment I uncomment //import store from '../store' from TabBarNavigationConfiguration , the export const TabBar from the same file will be undefined causing havoc later down the line.
What confuses me even more is that if I try to import the store from any other module e.g. TabIcon, I get the same error (TabBar undefined).
Any ideas what might cause this and how to solve this besides combining TabBarNavigation & TabBarNavigationConfiguration into a single module and then reading the store using mapStateToProps? I'd like to keep the files separated.
For what it's worth, I'm using:
react#16.0.0-alpha.6
react-navigation#1.0.0-beta.9
react-redux#5.0.4
Thanks in advance.

TypeError: Cannot read property 'pathname' of undefined in react/redux testing

I'm testing some react components, a basic tests suite just to know if a component is rendering and their childs.
I'm using redux-mock-store to make the store and {mount} enzyme to mount the container in a provider, but even mocking the correct store this error is always fired:
TypeError: Cannot read property 'pathname' of undefined
Here is my very deadly basic test:
import React from 'react';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import App from '../containers/App.container';
describe('App', () => {
let wrapper;
const mockStore = configureStore([]);
const store = mockStore({
router: {
location: { pathname: '/home', query: {}, search: '' },
params: {}
}
});
console.log(store.getState());
beforeEach(() => {
wrapper = mount(
<Provider store={store}>
<App />
</Provider>
);
});
it('Should render app and container elements', () => {
expect(wrapper.find('.app').exists()).toBeTruthy();
expect(wrapper.find('.container').exists()).toBeTruthy();
});
it('Should render the navbar', () => {
expect(wrapper.find('nav').exists()).toBeTruthy();
});
});
And the (even more) simple component / container:
import React, { Component } from 'react';
import NavBar from '../components/Navbar';
class App extends Component {
render() {
const { location, logout} = this.props;
console.log(location);
return (
<section className='app'>
<NavBar location={location.pathname} onLogoutClick={logout}/>
<div className='container'>
{this.props.children}
</div>
</section>
);
}
}
export default App;
Container:
import { connect } from 'react-redux';
import { signOut } from '../actions/auth.actions'
import App from '../components/App';
const mapStateToProps = (state, ownProps) => {
return {
location: ownProps.location
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
logout: () => {
dispatch(signOut())
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
I can't figure out the problem of the test, the mockStore is in the correct format:
Any idea?
Update:
Thinking about it, I have no reducer / prop in the rootReducer for the location, but, i just want to pass down through the children components the location object properties that react-redux-router make available in the ownProps argument.
Weird fact: logging the location property in the app returns me the correct object.
In the tests, is always undefined... (as the error shows).
Here is my rootReducer:
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import { routerReducer } from 'react-router-redux';
import authReducer from './auth.reducer';
import analysisReportsReducer from './AnalysisReports.reducer';
import titleAnalysisReducer from './TitleAnalysis.reducer';
import postsReportsReducer from './PostsReports.reducer';
const rootReducer = combineReducers({
form: formReducer,
routing: routerReducer,
auth: authReducer,
analysis: titleAnalysisReducer,
analysis_reports: analysisReportsReducer,
posts: postsReportsReducer
});
export default rootReducer;
It looks like your location object is scoped beneath the router.
Your test may be grabbing the window.location property, which your test suite may not replicate, assuming the test is cli and not in a browser.
Perhaps try:
<NavBar location={this.props.router.location.pathname} onLogoutClick={logout}/>

Categories

Resources