If you’ve implemented React Router in an application with long pages, you will have no doubt noticed that the scroll position doesn’t get reset when the location changes.
React Training recommend a neat little component to wrap around your App component, which will reset the scroll position to 0 when it detects a change in location:
import React from 'react';
import { withRouter } from 'react-router';
class ScrollToTop extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
window.scrollTo(0, 0);
}
}
render() {
return this.props.children;
}
}
export default withRouter(ScrollToTop);
To ensure the component is doing what we expect, let’s write some unit tests for this component.
We want to test that:
- The component calls
window.scrollTo
with the correct parameters when location changes. - It renders nested components correctly.
Let’s set up our test file:
import React from 'react';
import { mount } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
import ScrollToTop from './ScrollToTop';
global.scrollTo = jest.fn();
describe('ScrollToTop', () => {
let wrapper;
let history;
beforeEach(() => {
wrapper = mount(
<MemoryRouter initialEntries={['/']}>
<ScrollToTop>
<p>Hi</p>
</ScrollToTop>
</MemoryRouter>
);
history = wrapper.instance().history;
});
afterEach(() => {
jest.clearAllMocks();
});
});
Firstly, we create a spy for the window.scrollTo
method. Jest uses global
as the window
object, so we do this by assigning the spy to global.scrollTo
.
We mount our ScrollToTop
component within MemoryRouter
and get a reference to the mounted components history.
We then make sure to reset our spy method after each test.
With the set-up done, we are ready to write some tests!
it('calls window.scrollTo when route changes', () => {
expect(global.scrollTo).not.toHaveBeenCalled();
history.push('/new-url');
expect(global.scrollTo).toHaveBeenCalledWith(0, 0);
});
We call history.push
just as we would in our application. This will activate a route change within MemoryRouter
which will then pass through updated props to the ScrollToTop
component, triggering the componentDidUpdate
lifecycle method.
We can then assert that our spy method has been called with the correct parameters.
Lastly, we write a test to ensure ScrollToTop
is rendering it’s nested components as expected.
it('it renders children', () => {
const component = wrapper.find(ScrollToTop);
expect(component.children().length).toEqual(1);
expect(component.contains(<p>Hi</p>)).toEqual(true);
});
And we are done! I hope this proves useful to someone wanting to test a component that reacts to router events.
Top comments (0)