How to spy on window.scrollTo in Jest?

23,890

Solution 1

In jest the global name space must be accessed by global not window.

global.scrollTo = jest.fn()

Solution 2

I had a similar problem and just assigning the spy didn't work for me.

Eventually I was able to get this working with this code

const spyScrollTo = jest.fn();
Object.defineProperty(global.window, 'scrollTo', { value: spyScrollTo });

Then in Jest I was able to check it like this

const spyScrollTo = jest.fn();

describe('myComponent', () => {
  beforeEach(() => {
    Object.defineProperty(global.window, 'scrollTo', { value: spyScrollTo });
    Object.defineProperty(global.window, 'scrollY', { value: 1 });
    spyScrollTo.mockClear();
  });

  it('calls window.scrollTo', () => {
    myComponent();
    expect(spyScrollTo).toHaveBeenCalledWith({
      top: 1,
      behavior: 'smooth',
    });
  });
});

Solution 3

Dependencies:

"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"jest": "^24.8.0"
"jest-environment-enzyme": "^7.1.1",
"jest-enzyme": "^7.1.1",
"jsdom": "^15.1.1"

window.scrollTo = jest.fn() works fine.

For example:

index.spec.tsx:

import React, { Component } from 'react';

class SomeComponent extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(e) {
    e.preventDefault();
    window.scrollTo(0, 0);
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>Click Me!</button>
      </div>
    );
  }
}

export default SomeComponent;

index.spec.tsx, mock window.scrollTo in each test case:

import React from 'react';
import { shallow } from 'enzyme';
import SomeComponent from './';

describe('SomeComponent', () => {
  test('should handle click', () => {
    const wrapper = shallow(<SomeComponent></SomeComponent>);
    const mEvent = { preventDefault: jest.fn() };
    window.scrollTo = jest.fn();
    wrapper.find('button').simulate('click', mEvent);
    expect(mEvent.preventDefault).toBeCalled();
    expect(window.scrollTo).toBeCalledWith(0, 0);
  });
});

jest.config.js:

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'enzyme',
  setupFilesAfterEnv: ['jest-enzyme'],
  testEnvironmentOptions: {
    enzymeAdapter: 'react16'
  },
  coverageReporters: ['json', 'text', 'lcov', 'clover']
};

Unit test result:

 PASS  src/stackoverflow/58585527/index.spec.tsx
  SomeComponent
    ✓ should handle click (13ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.932s, estimated 3s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58585527

Solution 4

Like Andreas said, you will need to access scrollTo() via the global namespace. But to spy on it and make assertions, you will need to assign your spy function to a variable. Something like this:

const scrollToSpy = jest.fn();
global.scrollTo = scrollToSpy;
...
expect(scrollToSpy).toHaveBeenCalled();
Share:
23,890
Amir Hoseinian
Author by

Amir Hoseinian

Front end developer, photographer, terribly forgetful.

Updated on October 28, 2020

Comments

  • Amir Hoseinian
    Amir Hoseinian over 3 years

    I got a very simple react component with following functionallity:

    componentDidMount() {
      window.scrollTo(0, 0)
    }
    

    It seems that you cannot do something like

    window.scrollTo = jest.fn()
    

    to spy on scrollTo function.

    Therefor, I want to know what is the best way to spy on this function and assert that it has been used in my tests.

    Thanks

  • Amir Hoseinian
    Amir Hoseinian about 7 years
    Thanks it actually did work out but I had to define global.scrollTo = jest.fn() both in my setupTests.js and my actual test otherwise it was undefined. Can you tell me why is that so?
  • user3019647
    user3019647 over 4 years
    @AmirHoseinian = where did u update in setupTests.js ?
  • Michael McKenna
    Michael McKenna about 4 years
    expect(window.scrollTo).toBeCalledWith(0, 0); was the trick for me. Before I got some error "expected 2 arguments but got 1" by using Martin's answer
  • Soldeplata Saketos
    Soldeplata Saketos about 3 years
    you can avoid a declaration by expect(global.scrollTo).toHaveBeenCalled();