8

I need to have a value set in the window object before a dependency is imported. Say I have this code

// foo.test.js
import { dependency } from './foo'

describe('...', () => {
  it('...', () => {
    // use dependency
  })
})

But for dependency to be imported I need to have a value defined in window.myValues

// foo.js
export const dependency = {
  key: window.myValue.nestedValue
}

That code will give me an error when importing the file because window.myValue.nestedValue is trying to access the property nestedValue of undefined.

How can I get that done?

Edit Following christianeide's answer below I get the following error

  ● Test suite failed to run

    TypeError: Cannot convert undefined or null to object

      2 |   delete global.window.myValue
      3 |   global.window = Object.create(window)
    > 4 |   global.window.myValue = {
        |                 ^
      5 |     nestedValue: 'someValue'
      6 |   }
      7 | }

      at module.exports (jest.setup.js:4:17)
      at node_modules/@jest/core/build/runGlobalHook.js:82:17
      at ScriptTransformer.requireAndTranspileModule (node_modules/@jest/transform/build/ScriptTransformer.js:684:24)
      at node_modules/@jest/core/build/runGlobalHook.js:72:27
      at pEachSeries (node_modules/p-each-series/index.js:8:9)
      at async _default (node_modules/@jest/core/build/runGlobalHook.js:58:5)
      at async runJest (node_modules/@jest/core/build/runJest.js:345:5)
Daniel Reina
  • 5,764
  • 1
  • 37
  • 50
  • Does this answer your question? [How can I mock the JavaScript 'window' object using Jest?](https://stackoverflow.com/questions/41885841/how-can-i-mock-the-javascript-window-object-using-jest) – Michael Freidgeim Feb 20 '23 at 04:26

4 Answers4

8

es6 imports are "hoisted", meaning wherever you write them in the code, they'll get processed before the importing module is executed, so the imported module is always executed before the importing module. In your case, this means foo.js executes before foo.test.js, so even if you properly mock your property of window in your tests, foo.js will not see your mock.

You can get around this by using require in your tests to import foo.js after the property of window has been mocked.

// foo.test.js
window.myValue = { nestedValue: MOCK_NESTED_VALUE };

const { dependency } = require('./foo');

describe('...', () => {
  it('...', () => {
    // use dependency
  })
})

As other answers have pointed out, if myValue is one of existing "sytem" properties of window such as window.location, you might have to delete it first. When deleting, don't forget to back it up so that you can restore it when cleaning up after your test.

Miloš Rašić
  • 2,229
  • 5
  • 24
  • 43
1

I was able to append properties on the window object by creating a setup.js:

global.propertyA = () => {};
global.nestedPropertyB = {
  propertyC: () => {}
};

And setting the file in setupFilesAfterEnv in my jest.config.js file:

module.exports = {
  setupFilesAfterEnv: ['<rootDir>/tests/js/setup.js']
}
mbchoa
  • 11
  • 4
1

I have solved such an issue with jest.mock: as it's hoisted above import statements, it's possible to execute any code before and after importing an actual module.

In my case I needed to test some functionality and set node env to a value, different from 'test', and, as I needed this substitution only for the time of import, I set its value back to 'test' before returning the actual module. Of course, it may vary depending on use cases - it's just an example of executing code before and after importing is done. Also i tried to use a getter for a particular module field, and it also worked.

import ApiBase from "../ApiBase";

jest.mock('../ApiBase', () => {
    process.env.NODE_ENV = '';
    const actual = jest.requireActual('../ApiBase');
    process.env.NODE_ENV = 'test';
    return actual;
});

Roman Karagodin
  • 740
  • 2
  • 11
  • 16
0

Try assigning values to global.window.

Something like this:

delete global.window.myValue;
global.window = Object.create(window);
global.window.myValue = {
    nestedValue: 'someValue',
};

This can be done inside jest.setup.js, but you could also probably define the values inside foo.test.js

christianeide
  • 417
  • 2
  • 13
  • That's not working for me. I updated my question with the stack trace of the error I'm getting. I can't believe it's that hard to do what I'm trying to do. I have to be missing something – Daniel Reina Jun 10 '20 at 13:29
  • What testenvironment are you using? Jsdom/browser or node? – christianeide Jun 11 '20 at 14:09
  • I haven't explicitly defined the testenvironment, so I assume I'm using the default, jsdom – Daniel Reina Jun 12 '20 at 17:14
  • If you try to do a `console.log(global.window);`, does it log out the entire global.window-object? If id does not Im guessing that your jest.config.js have set the `testEnvironment` to something other then jsdom. – christianeide Jun 15 '20 at 10:22
  • yes, it logs a huge object with properties I can recognise as being part of the window object, as well as many others that I don't recognise. – Daniel Reina Jun 16 '20 at 08:52