33

I am trying to run a test on a component at a certain viewport width. I am doing the following, but this doesn't seem to change it:

test('Component should do something at a certain viewport width.', () => {
    global.innerWidth = 2000;
    const component = mount(<SomeComponent />);
    // ...
});

I also found an article that explains how to do it using JSDom, but as Jest now ships with JSDom, I wondered if there was a native solution.

https://www.codementor.io/pkodmad/dom-testing-react-application-jest-k4ll4f8sd

agilgur5
  • 667
  • 12
  • 30
JoeTidee
  • 24,754
  • 25
  • 104
  • 149

4 Answers4

50

Background Information:

Here is an example:


comp.js

import * as React from 'react';

export default class Comp extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = { width: 0, height: 0 }
  }
  updateDimensions = () => {
    this.setState({ width: window.innerWidth, height: window.innerHeight });
  }
  componentDidMount() {
    this.updateDimensions();
    window.addEventListener("resize", this.updateDimensions);
  }
  componentWillUnmount() {
    window.removeEventListener("resize", this.updateDimensions);
  }
  render() {
    return <div>{this.state.width} x {this.state.height}</div>;
  }
}

comp.test.js

import * as React from 'react';
import { shallow } from 'enzyme';

import Comp from './comp';

const resizeWindow = (x, y) => {
  window.innerWidth = x;
  window.innerHeight = y;
  window.dispatchEvent(new Event('resize'));
}

describe('Comp', () => {
  it('should display the window size', () => {
    const component = shallow(<Comp />);

    expect(component.html()).toEqual('<div>1024 x 768</div>');

    resizeWindow(500, 300);
    expect(component.html()).toEqual('<div>500 x 300</div>');

    resizeWindow(2880, 1800);
    expect(component.html()).toEqual('<div>2880 x 1800</div>');
  });
});

Notes:

Brian Adams
  • 43,011
  • 9
  • 113
  • 111
  • 7
    Is there a way to handle `Cannot assign to 'innerHeight' because it is a read-only property.`? – kb_ Jan 03 '19 at 18:04
  • 2
    @kb_ When a test is running in `Jest` the `window` is the object provided by `jsdom` and its `innerHeight` property is a normal property. I'm guessing you're getting this as a TypeScript error, in which case you may need to use a type assertion to tell TypeScript the property is not read-only. – Brian Adams Jan 03 '19 at 20:25
9

If you're using TypeScript it will complain that window.innerWidth / innerHeight are read-only. You can get around this by either redeclaring the property:

Object.defineProperty(window, 'innerWidth', {writable: true, configurable: true, value: 105})

Or using the Object.assign method:

window = Object.assign(window, { innerWidth: 105 });

Both are not extremely nice solutions, but they work.

Kevin M. Mansour
  • 2,915
  • 6
  • 18
  • 35
r3dDoX
  • 211
  • 3
  • 2
1

The below test works for me. Source code is no longer marked as uncovered.

it('resize event listener changes the state', () => {
  const wrapper = shallow(<Component />);
  const instance = wrapper.instance();

  instance.setState({
    mobileMode: true
  });

  global.innerWidth = 800;
  window.dispatchEvent(new Event('resize'));
  expect(instance.state.mobileMode).toBeFalsy();

  global.innerWidth = 600;
  window.dispatchEvent(new Event('resize'));
  expect(instance.state.mobileMode).toBeTruthy();
});

Resize listener inside my component:

// ...
  resizeListener = () => {
      if (window.innerWidth < 768) {
        this.setState({
          mobileMode: true
        });
      } else {
        this.setState({
          mobileMode: false
        });
      }
    };
    window.addEventListener('resize', resizeListener);
// ...
agilgur5
  • 667
  • 12
  • 30
Dm Mh
  • 820
  • 7
  • 9
  • It looks like there's some missing code for adding the `resizeListener`. One common way to use `window.addEventListener` is within `componentDidMount` in class-based React, or `useEffect` with Hooks. – agilgur5 Jun 29 '23 at 21:05
0

Having written helpers similar to the other answers in my tests a few times, I decided to create window-resizeto as a very tiny library of reusable logic.

window-resizeto is a tiny polyfill for window.resizeTo for test environments (as JSDOM does not implement it).

Per the Usage section of the README, you can add it to your jest.config.js:

module.exports = {
  setupFilesAfterEnv: [
    // polyfill window.resizeTo
    'window-resizeto/polyfill'
  ]
}

and then use it in a spec file:

window.resizeTo(500, 500) // window is now resized to 500x500

It's also written in TypeScript, so typings are provided out-of-the-box, and has 100% test coverage to ensure it keeps working correctly.

agilgur5
  • 667
  • 12
  • 30