react-router-util
react-router-util copied to clipboard
Testing with MemoryRouter
I'm trying to write Ava unit tests for a component using AuthenticatedRoute, but I don't think there is a way to get this to work with the MemoryRouter right now. Do you have any suggestions for workarounds?
We depend on the history export for now until #3 is fixed. So we could probably fix it for you for now by exporting a custom version of MemoryRouter, like done with BrowserRouter: https://github.com/sindresorhus/react-router-util/blob/2eff2bfd3ee6fb081c621663a455db50e2ea3ac5/index.js#L15-L20 And you need to use that.
Can you try adding this to index.js and making use of this MemoryRouter?
import {MemoryRouter as OriginalMemoryRouter} from 'react-router-dom';
export class MemoryRouter extends OriginalMemoryRouter {
constructor(...args) {
super(...args);
this.history = history;
}
}
So I added that and made a new build, and it threw this error:
git:(ava-tests) ✗ ava
The above error occurred in the <Redirect> component:
in Redirect (created by AuthenticatedRoute)
in AuthenticatedRoute (created by App)
in App
in Router (created by MemoryRouter)
in MemoryRouter
in Provider (created by WrapperComponent)
in WrapperComponent
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
1 failed
Render login page when not authenticated
/opt/pairity/projects/pairity-react/node_modules/jsdom/lib/jsdom/living/window/History-impl.js:73
Error thrown in test:
DOMException {}
HistoryImpl._sharedPushAndReplaceState (node_modules/jsdom/lib/jsdom/living/window/History-impl.js:73:15)
HistoryImpl.replaceState (node_modules/jsdom/lib/jsdom/living/window/History-impl.js:57:10)
History.replaceState (node_modules/jsdom/lib/jsdom/living/generated/History.js:129:21)
node_modules/history/createBrowserHistory.js:211:23
Object.confirmTransitionTo (node_modules/history/createTransitionManager.js:44:7)
Object.replace (node_modules/history/createBrowserHistory.js:202:23)
Redirect.perform (node_modules/react-router/Redirect.js:80:15)
Redirect.componentDidMount (node_modules/react-router/Redirect.js:55:32)
commitLifeCycles (node_modules/react-dom/cjs/react-dom.development.js:8770:24)
commitAllLifeCycles (node_modules/react-dom/cjs/react-dom.development.js:9946:9)
Object.invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:491:10)
invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:438:27)
commitRoot (node_modules/react-dom/cjs/react-dom.development.js:10050:9)
performWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:11017:42)
performWork (node_modules/react-dom/cjs/react-dom.development.js:10967:7)
requestWork (node_modules/react-dom/cjs/react-dom.development.js:10878:7)
scheduleWorkImpl (node_modules/react-dom/cjs/react-dom.development.js:10732:11)
scheduleWork (node_modules/react-dom/cjs/react-dom.development.js:10689:12)
scheduleTopLevelUpdate (node_modules/react-dom/cjs/react-dom.development.js:11193:5)
Object.updateContainer (node_modules/react-dom/cjs/react-dom.development.js:11231:7)
node_modules/react-dom/cjs/react-dom.development.js:15226:19
Object.unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:11102:12)
renderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:15225:17)
Object.render (node_modules/react-dom/cjs/react-dom.development.js:15290:12)
Object.render (node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:218:50)
new ReactWrapper (node_modules/enzyme/build/ReactWrapper.js:98:16)
mount (node_modules/enzyme/build/mount.js:19:10)
Test.t [as fn] (lib/js/components/__tests__/app.test.js:22:21)
The test looks like this:
import test from 'ava';
import React from 'react';
import { shallow, mount } from 'enzyme';
import { MemoryRouter } from 'react-router-util';
import { Provider } from 'react-redux';
import App from '../app.jsx';
import Main from '../main/main.jsx';
import Login from '../login/login.jsx';
import { createStore } from '../../store';
const store = createStore();
test('Render login page when not authenticated', (t) => {
const props = {
isLoggedIn: false,
location: {
pathname: '/login',
search: ''
}
};
const wrapper = mount(
<Provider store={store}>
<MemoryRouter initialEntries={['/login']}>
<App {...props}/>
</MemoryRouter>
</Provider>
);
});
The component looks like this:
import React from 'react';
import PropTypes from 'prop-types';
import { Route, Switch } from 'react-router-dom';
import { AuthenticatedRoute } from 'react-router-util';
// eslint-disable-next-line import/no-unassigned-import
import '../../css/app.css';
import Login from './login/login-container.jsx';
import Main from './main/main-container.jsx';
class App extends React.Component {
constructor(props) {
super(props);
this.referrer = '/';
}
componentWillUpdate({ location: nextLocation }) {
// eslint-disable-next-line no-shadow
const { location } = this.props;
const url = location.pathname + location.search;
const nextUrl = nextLocation.pathname + nextLocation.search;
if (url !== nextUrl) {
this.referrer = url;
}
}
render() {
const { isLoggedIn } = this.props;
return (
<AuthenticatedRoute
isAuthenticated={isLoggedIn}
redirectFromLoginTo={this.referrer}
>
<Switch>
<Route exact path="/login" component={Login} />
<Route component={Main} />
</Switch>
</AuthenticatedRoute>
);
}
}
App.propTypes = {
isLoggedIn : PropTypes.bool.isRequired,
location : PropTypes.object.isRequired
};
export default App;
It seems like the problem is that I hadn't set jsdom's url https://github.com/jsdom/jsdom/issues/1372
When I set a url when instantiating browser-env, everything seems to work, even using the BrowserRouter. Looks like no changes necessary
Interestingly, if I run two or more copies of the same test simultaneously, one of them hangs. I assume this has to do with sharing a global history object, but I haven't nailed it down yet. Hopefully #3 will solve that as well
You can see how we do testing of it here: https://github.com/sindresorhus/react-router-util/tree/master/test
Hopefully #3 will solve that as well
#3 is now released: https://github.com/sindresorhus/react-router-util/releases/tag/v0.10.0