Figuring out how to mock the window size changing for a react component test
Asked Answered
S

7

62

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.

Sommelier answered 24/8, 2017 at 17:52 Comment(0)
C
90

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.
});
Carolincarolina answered 16/9, 2017 at 17:8 Comment(10)
according to docs at developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth, innerWidth is read only. should we use window.resizeBy(x, y); instead? – Feebleminded
I'm on Jest 23.6.0 and it works for me, maybe needs an upgrade πŸ€·β€β™‚οΈ – Willpower
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 – Aminta
It seems this no longer works: Property 'innerWidth' does not exist on type 'Global' – Zacharia
Working fine in Jest 21.2.1 – Waers
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? – Tartuffery
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. – Atthia
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 – Spur
Works fine in 2022, using @testing-library/jest-dom: 5.16.2. – Maser
Interesting @Maser It does not work for me, my tests just hang. Using 5.16.1 and Node 14. – Tartuffe
P
45

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})
Plywood answered 2/5, 2019 at 15:10 Comment(0)
X
8

Try this line:

window = Object.assign(window, { innerWidth: 500 });
Xenomorphic answered 12/9, 2019 at 11:28 Comment(0)
B
3

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
  });
Biweekly answered 23/11, 2022 at 13:22 Comment(2)
NB: this will only work in JS. window.innerWidth is a readonly property - you will get build errors. See @Lauren Madigan's annswer above if using TS – Fragmental
Don't see any TS requirement in the question. – Biweekly
C
3

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()
    })
  })
Couple answered 12/8, 2023 at 21:2 Comment(0)
B
1

You can take advantage of jest mocking to mock the width.

This snippet mocks how the window screen retrieves the width, which ultimately results in window.innerWidth getting the mocked value during the test rather than the real value:

jest.spyOn(window.screen, "width", "get").mockReturnValue(1000);
Benis answered 9/9, 2022 at 10:15 Comment(1)
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. – Spanish
D
1

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

Dreadnought answered 10/11, 2022 at 23:56 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.