1

I'm migrating from enzyme to react-testing-library. I have a component called <ScrollToTop/> that just checks if window.location has changed since the last component update and scrolls to the top if it has.

ScrollToTop.jsx

import React from "react";
import { withRouter } from "react-router-dom";

export class UnwrappedScrollToTop extends React.Component {
  componentDidUpdate(prevProps) {
    if (
      this.props.location !== prevProps.location &&
      !this.props.location.hash
    ) {
      window.scrollTo({ top: 0, behavior: "smooth" });
    }
  }

  render() {
    return this.props.children;
  }
}

export default withRouter(UnwrappedScrollToTop);

My old enzyme test used wrapper.setProps to pass a new location and then tested whether window.scrollTo was called:

ScrollToTop.test.js

...
it("calls window.scrollTo (location changes, no hash)", () => {
    const wrapper = mount(
      <UnwrappedScrollToTop children="" location={{ pathname: "dashboard" }} />
    );


    // pass different location prop to trigger componentDidUpdate
    wrapper.setProps({ location: { pathname: "library" } });

    // check to see if scrollToMock was called in componentDidUpdate
    expect(ScrollToMock).toHaveBeenCalledWith({
      top: 0,
      behavior: "smooth"
    });
  });
...

But I can't figure out how to translate this to react-testing-library, since I can't call setProps to pass a different location. How would I test this component?

rg_
  • 401
  • 1
  • 4
  • 19
  • You could spy on it? `spyOn(window, 'scrollTo')` – evolutionxbox May 25 '22 at 21:12
  • @evolutionxbox sure, but how do I create the condition of the changed `window.location`? I don't understand how to render a component, pass a new set of props to it programmatically, and then render it again using `react-testing-library` – rg_ May 25 '22 at 21:16
  • I don't think you can, since the tests run in node, running `window.scrollTo` does nothing – evolutionxbox May 25 '22 at 21:17
  • @evolutionxbox so is there a different way to test a component like this then? or is it just not testable using this library? – rg_ May 25 '22 at 21:18
  • 1
    An option is to mock react-router-dom's `withRouter`. Another option is to change the URL by assigning to location (https://stackoverflow.com/questions/46169824/intercept-navigation-change-with-jest-js-or-how-to-override-and-restore-locatio) and then call `rerender` from RTL. Or if you are only testing the unwrapped component, you can just `rerender`. – Jakub Kotrs May 25 '22 at 21:22
  • 1
    See https://testing-library.com/docs/react-testing-library/api#rerender – Jakub Kotrs May 25 '22 at 21:24
  • @JakubKotrs thank you!, if you post that as an answer i'll mark it as the accepted one – rg_ May 25 '22 at 22:32

1 Answers1

1

The simplest way to do this is to use the rerender function returned by render.

const {rerender} = render(<UnwrappedScrollToTop children="" location={{ pathname: "dashboard" }} />)

rerender(<UnwrappedScrollToTop children="" location={{ pathname: "library" }} />)

expect(ScrollToMock).toHaveBeenCalledWith({
    top: 0,
    behavior: "smooth"
});

Link to docs: https://testing-library.com/docs/react-testing-library/api/#rerender

Jakub Kotrs
  • 5,823
  • 1
  • 14
  • 30