Javascript – TypeError: Cannot read property ‘scrollIntoView’ of null – react. jest enzyme

enzymejavascriptjestjsreactjstesting

Using react 16.3.1, jest 16.3.1, enzyme 3.3.0.

Within my React Class component I have created a react ref which I use to ensure that when the component is mounted the browser is at the top of the page.

class PageView extends React.Component {

  constructor(props) {
    super(props);
    this.topOfPageRef = React.createRef();
  }

  componentDidMount() {
    ReactDOM.findDOMNode(this.topOfPageRef.current).scrollIntoView();
  }

  render(){
    const { x } = this.props;

    return (
      <React.Fragment>
        <div className="main-wrapper" ref={this.topOfPageRef}>
         Top
        </div>
        )}
      </React.Fragment>
    );
  }
}

This all works perfectly within the browser but fails in my enzyme test.

My test is simple, it just tries to render the component.

  it('should render component correctly', () => {
    const props = {
      ...defaultProps,
    };
    const wrapper = mount(<PageView {...props} />);
    expect(wrapper).toMatchSnapshot();
  });

TypeError: Cannot read property 'scrollIntoView' of null

I have tried both shallow and mount methods and whilst the element found is not null, it appears to be a react instance of HTMLDivElement which is missing the scrollIntoView method.

Best Answer

Error message clarification

Using mount like in the sample code above gives this error:

TypeError: _reactDom2.default.findDOMNode(...).scrollIntoView is not a function

Using shallow gives the error listed above:

TypeError: Cannot read property 'scrollIntoView' of null


shallow

Issue

shallow does not do DOM rendering so there will never be a DOM node on which to call scrollIntoView().

Solution

Any code that does DOM manipulation needs to be tested using the full DOM rendering provided by mount.


mount

"The default environment in Jest is a browser-like environment through jsdom".

"jsdom is a pure-JavaScript implementation of many web standards...[that] emulate[s] enough of a subset of a web browser to be useful for testing".

Issue

jsdom implements much of the browser environment but it does not implement everything. Of particular note for this question is that it does not implement scrollIntoView since jsdom does not do layout and would therefore not be able to provide an accurate implementation.

Because jsdom does not implement scrollIntoView it will be undefined on elements provided by jsdom.

Solution

The recommended approach from this Google dev is to add the following line to your test code:

Element.prototype.scrollIntoView = () => {};

That line will add a noop implementation of scrollIntoView to the jsdom-provided Element.

For your test you could take it a step further and set scrollIntoView to a spy to make sure it is called:

it('should render component correctly', () => {
  const props = {
    ...defaultProps,
  };
  Element.prototype.scrollIntoView = jest.fn();  // set scrollIntoView to a spy
  const wrapper = mount(<PageView {...props} />);
  expect(wrapper).toMatchSnapshot();
  expect(Element.prototype.scrollIntoView).toHaveBeenCalled();  // PASSES
});

Also, Antonio is correct that you shouldn't need to use ReactDOM.findDOMNode(), you should be able to use this.topOfPageRef.current directly:

componentDidMount() {
  this.topOfPageRef.current.scrollIntoView();
}
Related Topic