DEV Community

Jay Vincent
Jay Vincent

Posted on

Testing the ScrollToTop component in React with Enzyme and Jest

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:

  1. The component calls window.scrollTo with the correct parameters when location changes.
  2. 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)