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"
}
You could try doing
You can simply mock
location
:Also note, that
global
in your test is the global context for the file to test (global === window
)Note this will only work if your module make the
window.location
call after the test has been finishing import all the modules.So if your module looks like this:
it will not work. In this case you could mock the module using
jest.mock
.Here you can find examples of how to do this:
https://www.codementor.io/pkodmad/dom-testing-react-application-jest-k4ll4f8sd
For example:
Your problem relies on the configuration.
In the moment you set:
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 aNodeJs
environmentTo solve it either you set your
testEnvironment
tojsdom
Or you remove the
testEnvironment
from your config so it will take the default value in yourpackage.json
:This is what they say in the documentation:
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
:or the other way around if you are meant to run the tests under
node
environmentConclusion
With this you can avoid importing
jsdom
manually and setting global variables,jsdom
will mock theDOM
implementation automatically.If you need to change the environment for your tests use the notation
@jest-environment
https://github.com/facebook/jest/issues/2460#issuecomment-324630534
It seems like the one of the contributer declared that he is not planning expose jsdom to global under the jest environment.
However, you could use
Object.defineProperty(window, 'location', {value: '…'}
API to approach it, like the developer in Facebook do. In your case it could be like:Good luck!
Not sure but i think you could do it with
jest.fn()
maybe even as
window = jest.fn(...)