56

So basically when the component mounts, I have an event listener listen for resize events. It toggles the isMobileView state and then passes it into the children as a prop. So it's imperative that this works and is tested. I'm fairly new to testing and I'm trying to figure out a way I can write a test that resizes the window and makes all the logic happen and test that it executed how it should.

Here is the code -

componentDidMount() {
    this.setMobileViewState()
    window.addEventListener('resize', this.setMobileViewState.bind(this));
}

setMobileViewState() {
    if(document.documentElement.clientWidth <= this.props.mobileMenuShowWidth) {
        this.setState({ isMobileView: true })
    } else {
        this.setState({ isMobileView: false })
    }
}

I know the code works, but I want to write a test for it. Basically just something that makes sure the state changes correctly.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
joe
  • 1,563
  • 4
  • 16
  • 35

7 Answers7

87

Using Jest and Enzyme you can do the following. Jest has JSDOM baked in. In your tests Jest provides the window object and it is represented by global (I think that the default window.innerWidth set by Jest is 1024px):

test('Test something when the viewport changes.', () => {

    // Mount the component to test.
    const component = mount(<ComponentToTest/>);

    ...

    // Change the viewport to 500px.
    global.innerWidth = 500;

    // Trigger the window resize event.
    global.dispatchEvent(new Event('resize'));

    ...

    // Run your assertion.
});
JoeTidee
  • 24,754
  • 25
  • 104
  • 149
  • 1
    according to docs at https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth, innerWidth is read only. should we use window.resizeBy(x, y); instead? – Simon Mar 09 '18 at 22:17
  • 1
    I'm on Jest 23.6.0 and it works for me, maybe needs an upgrade ‍♂️ – cameck Feb 26 '19 at 23:10
  • 1
    Confirmed working still in Jest 24.1.0 as well. the `resizeBy` would make sense to use but Jest does not implement that and the `innerWidth` is not read only – Chris Mar 16 '19 at 17:03
  • 6
    It seems this no longer works: `Property 'innerWidth' does not exist on type 'Global'` – Jake May 17 '19 at 04:15
  • Working fine in Jest 21.2.1 – santosh gawade Jun 12 '19 at 12:22
  • 1
    i tried setting innerWidth on window object but my @media css query didn't seem to kick in for the test. does this only work for 'global' object in node.js, or should it work for 'window' object in browser too? – 1ak31sha Jul 04 '19 at 19:06
  • I recommend putting this in a `beforeEach(() => { global.innerWidth = 500; global.dispatchEvent(new Event('resize'))'; });` so that you have control of the `window.innerWidth` for subsequent specs. – Paul Pettengill Oct 06 '20 at 18:34
  • would we need to reset the window width after the test is run? would be better to mock the property getter for the particular test instead of changing it directly on global – gaurav5430 Oct 10 '21 at 17:47
  • Works fine in 2022, using `@testing-library/jest-dom: 5.16.2`. – crevulus Apr 12 '22 at 07:21
  • Interesting @crevulus It does not work for me, my tests just hang. Using 5.16.1 and Node 14. – philipjc Jul 06 '22 at 14:34
42

If you are getting the typescript error message

Cannot assign to 'innerWidth' because it is a read-only property.

Try:

Object.defineProperty(window, 'innerWidth', {writable: true, configurable: true, value: 200})
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Lauren Madigan
  • 429
  • 4
  • 3
8

Try this line:

window = Object.assign(window, { innerWidth: 500 });
Lital Levy
  • 107
  • 1
  • 4
1
jest.spyOn(window.screen, "width", "get").mockReturnValue(1000);
Rakchamp25
  • 31
  • 3
  • 6
    Please don't post code-only answers. The main audience, future readers, will be grateful to see explained *why* this answers the question instead of having to infer it from the code. Also, since this is an old question, please explain how it complements the other answers. – Gert Arnold Sep 10 '22 at 14:33
0

I did it this way with testing library:

function fireResize(width) {
  window.innerWidth = width;
  window.dispatchEvent(new Event('resize'));
}

it('some test', async () => {
  ....
  await waitFor(() => {
   fireResize(500);
  });
  ....
});

Source: https://medium.com/@dawchihliou/testing-react-hooks-6d3ae95cd838

Sanchitos
  • 8,423
  • 6
  • 52
  • 52
0

For anyone who's using react-testing-library, there is a native way to achieve it using fireEvent as well.

  import { fireEvent } from '@testing-library/react';
  ...

  it('should resize', () => {
    window.innerWidth = 500;
    fireEvent(window, new Event('resize'));

    // your test here
  });
Can Ural
  • 19
  • 1
  • 4
0

It worked for me with testing library:

  it('should resize', () => {
    const altImage = 'test'

    window.innerWidth = 500

    fireEvent(window, new Event('resize'))

    waitFor(() => {
      expect(screen.getByAltText(altImage)).not.toBeInTheDocument()
    })
  })
Gabriel Duete
  • 81
  • 1
  • 4