Reputation: 51
Problem: My App component is responsible to render the other components based on the path. So inorder to test it, I need to be able to "set" the path. e.g I'd like to see the SignUp component is rendered when the path is /sign-up.
What I've done: I thought I could use initialEntries to give the MemoryRouter an initial path (unsuccessful) then I thought I might be able to use Route/Redirect directly in my test inside MemoryRouter to set the path, but that did not work either.
Specification:
Upvotes: 5
Views: 5804
Reputation: 2396
I spent a while trying to figure this out myself.
Simply put you cannot nest a <Router>
within the <MemoryRouter>
as they are both Routers.
Solution Example:
Routes.js - Remove <BrowserRouter>
from this so you can test your code.
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import SignUp from '../SignUp/SignUp'; //your signup component
import Home from '../Home/Home'; //some other component for an example
const MyRoutes = () => (
<Switch>
<Route path="/" component={Home} />
<Route path="/signup" component={SignUp} />
</Switch>
);
export default MyRoutes;
index.js - Add <BrowserRouter>
here as you will need to wrap a <Switch>
in a <Router>
.
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import Routes from './Components/Routes/Routes';
ReactDOM.render(
(
<BrowserRouter>
<Routes />
</BrowserRouter>
), document.getElementById('root')
);
tests/Routes.test.js - Now add the <MemoryRouter>
in here for testing.
import React from 'react';
import ReactDOM from 'react-dom';
import { MemoryRouter } from 'react-router-dom';
import Routes from '../Components/Routes/Routes';
const TestRoute = props => (
<MemoryRouter initialEntries={props.initialEntries}>
<Routes {...props} />
</MemoryRouter>
);
it('at /signup it displays the signup page', () => {
const div = document.createElement('div');
ReactDOM.render(<TestRoute initialEntries={['/signup']} />, div); // render on the route "/signup"
const elem = div.getElementsByClassName('signup')[0];
expect(elem).not.toBe(undefined); // check an element exists with that class
if(elem !== undefined) {
expect(elem.innerHTML).toEqual('signup'); // check it has worked
}
});
In other words you are separating the Routes/Switch from the Router to enable you to be able to test it, as you cannot nest two Routers.
Upvotes: 8
Reputation: 13385
I'm having a similar issue, I have found that the following works for testing components whose behaviour changes based upon the route:
<MemoryRouter initialEntries={['/about']}>
<Navigation {...props} />
</MemoryRouter>
In this instance, the snapshot shows that an .active
className is added to the 'about' link in Navigation.
What does not seem to work is adding memory router around the component/container holding the initial routes. To keep things simple I have reduced my basic routing page to this:
<Router>
<div className="App">
<Navigation app={this.props.app} />
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/about" component={About} />
<Route exact path="/login" render={() => <Login app={this.props.app} />} />
<Route exact path="/signup" render={() => <Signup app={this.props.app} />} />
<Route exact path="/account" render={() => <Account app={this.props.app} />} />
<Route component={ErrorPage} />
</Switch>
</div>
</Router>
The react-router team states in current testing docs:
We have a lot of tests that the routes work when the location changes, so you probably don’t need to test this stuff.
Upvotes: 2