Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`window` is not being exposed to Jest

Tags:

I have a test that imports a component that in turn imports a helper file that uses the window object to pull out a query string parameter. I get the following error about window:

 FAIL  src/js/components/__tests__/Controls.test.jsx
  ● Test suite failed to run

    ReferenceError: window is not defined

Controls.jsx:

import { Unwrapped as Controls } from '../Controls'

describe('<MyInterestsControls />', () => {
  it('should render the component with the fixture data', () => {
    const component = shallow(
      <UnwrappedMyInterestControls
        dashboardData={dashboardData}
        loadingFlags={{ controls: false }}
      />
    )
    expect(component).toMatchSnapshot()
  })
})

Controls.jsx imports ./helpers/services.js which contains the following:

import * as queryString from 'query-string'
const flag = queryString.parse(window.location.search).flag || 'off'
                               ^^^^^^ this seems to be the problem

I have attempted to import jsdom

import { JSDOM } from 'jsdom'

And implement the solution presented here at the top of my test file:

const { JSDOM } = require('jsdom');

const jsdom = new JSDOM('<!doctype html><html><body></body></html>');
const { window } = jsdom;

function copyProps(src, target) {
  const props = Object.getOwnPropertyNames(src)
    .filter(prop => typeof target[prop] === 'undefined')
    .map(prop => Object.getOwnPropertyDescriptor(src, prop));
  Object.defineProperties(target, props);
}

global.window = window;
global.document = window.document;
global.navigator = {
  userAgent: 'node.js',
};
copyProps(window, global);

however I still get the error and it seems JSDOM's window object isn't exposed to the test.

How can I properly expose global objects like window or document to a jest test?

relevant package.json
  "scripts": {
    "test:watch": "NODE_ENV=test jest --watch"
  },
  ...
  "devDependencies": {
    ...
    "jest": "^20.0.4",
    "jest-mock": "^21.2.0",
    "jsdom": "^11.0.0",   
    ...  
  },
  ...
  "jest": {
    "verbose": true,
    "collectCoverageFrom": [
      "src/js/helpers/preparePayload.js",
      "src/js/components-ni",
      "!**/node_modules/**",
      "!**/dist/**"
    ],
    "coverageThreshold": {
      "global": {
        "statements": 50,
        "branches": 50,
        "functions": 50,
        "lines": 75
      }
    },
    "testEnvironment": "jest-environment-node"
  }
like image 438
1252748 Avatar asked Sep 07 '17 21:09

1252748


People also ask

How do you open mock windows in Jest?

To mock the JavaScript window object using Jest, we can use the jest. spyOn method. let windowSpy; beforeEach(() => { windowSpy = jest. spyOn(window, "window", "get"); }); afterEach(() => { windowSpy.

Is Jest owned by Facebook?

Jest is a JavaScript testing framework built on top of Jasmine and maintained by Meta (formerly Facebook). It was designed and built by Christoph Nakazawa with a focus on simplicity and support for large web applications.

What is Jest and why it is used?

Jest is a JavaScript testing framework designed to ensure correctness of any JavaScript codebase. It allows you to write tests with an approachable, familiar and feature-rich API that gives you results quickly. Jest is well-documented, requires little configuration and can be extended to match your requirements.

What is Jest environment Jsdom?

By default, jest uses the node testEnvironment. This essentially makes any tests meant for a browser environment invalid. jsdom is an implementation of a browser environment, which supports these types of UI tests.


2 Answers

UPDATE:

As mentioned by @RiZKiT in the comment bellow, since Jest v.27.0 the default test environment has changed from "jsdom" to "node".

Your problem relies on the configuration.

In the moment you set:

"testEnvironment": "jest-environment-node"

you are changing the default configuration from jest which is browser-like to jest-environment-node (node-like) meaning that your test will be run under a NodeJs environment

To solve it either you set your testEnvironment to jsdom
Or you remove the testEnvironment from your config so it will take the default value in yourpackage.json:

 ...
  "jest": {
    "verbose": true,
    "collectCoverageFrom": [
      "src/js/helpers/preparePayload.js",
      "src/js/components-ni",
      "!**/node_modules/**",
      "!**/dist/**"
    ],
    "coverageThreshold": {
      "global": {
        "statements": 50,
        "branches": 50,
        "functions": 50,
        "lines": 75
      }
    }
  }

This is what they say in the documentation:

testEnvironment [string] # Default: "jsdom"

The test environment that will be used for testing. The default environment in Jest is a browser-like environment through jsdom. If you are building a node service, you can use the node option to use a node-like environment instead.


Do you need the `node` environment?

As I could see, your tests are meant to be run under a browser-like environment.

If you ever need an explicit node environment, better you isolate that case using @jest-environment:

/**
 * @jest-environment node
 */

test('use node in this test file', () => {
  expect(true).not.toBeNull();
});

or the other way around if you are meant to run the tests under node environment

/**
 * @jest-environment jsdom
 */

test('use jsdom in this test file', () => {
  const element = document.createElement('div');
  expect(element).not.toBeNull();
});

Conclusion

With this you can avoid importing jsdom manually and setting global variables, jsdom will mock the DOM implementation automatically.

If you need to change the environment for your tests use the notation @jest-environment

like image 191
Jose Paredes Avatar answered Oct 03 '22 11:10

Jose Paredes


You could try doing

global.window = new jsdom.JSDOM().window;
global.document = window.document;
like image 33
Demon Avatar answered Oct 03 '22 11:10

Demon