Reputation: 1801
I have a button component that creates a react-router
Link
element. It also allows an onClick
function to be passed in for additional functionality (e.g. sending a Google Analytics event).
I have included this component in a parent, like so:
export default class Page extends Component {
const doSomething = () => {
//do a thing to test here
}
return (
<div>
<Button
onClickFn{() => doSomething()}
linkToUrl='/other/page' //this creates a <Link> inside the button
/>
</div>
)
}
Problem comes when I want to test that doSomething
is being triggered correctly. I have used Enzyme mount to create the test Page
component including the button. When I simulate a click I get the following error
'<Link>s rendered outside of a router context cannot navigate.'
because the Link in the button has no context. Is there a way of mocking this or preventing the error from showing? Or is there a better way of testing this functionality?
Upvotes: 3
Views: 2372
Reputation: 59449
Building on top of Paul's answer, here's a more detailed example for testing the onClick
of a Button
(or its Link
child to be more precise). The example uses the testing libraries mocha (BDD test runner), chai (BDD assertions), enzyme (React testing utility), and sinon (test doubles).
import React from 'react';
import { Router, Route } from 'react-router';
import { createMemoryHistory } from 'history';
import MyCustomPage from '/.index';
describe('MyCustomPage', function(){
it('stores data when clicking the link', function() {
// Arrange
const Page = () => (
<MyCustomPage foo="foo" bar="bar" />
);
const container = enzyme.mount(
<Router history={createMemoryHistory()}>
<Route path="/" component={Page} />
</Router>
);
// Act
container.find('#my-link').simulate('click');
// Assert
expect(sessionStorage.setItem).to.have.been.calledWith('key', 'value');
});
});
Upvotes: 2
Reputation: 36787
In your test, you will need to render the component within a <Router>
. You can take a look at the tests for the <Link>
component for examples on how to do this.
The basic idea is to create a memory history instance, pass that to the <Router>
, and then render the <Link>
inside of a <Route>
passed to that. It sounds a bit involved, but it is fairly simple.
import { createMemoryHistory } from 'history'
it('clicks', () => {
const history = createMemoryHistory()
const App = () => (
<Router history={history}>
<Route path='/' component={Page} />
</Router>
)
})
Upvotes: 3