Reputation: 15379
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 implemented 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.
window
or document
to a Jest test? "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"
}
Upvotes: 24
Views: 36940
Reputation: 156
In my case, I was injecting a custom environment file's value into a Vue.js component using Ruby on Rails. So, I was getting a base URL for assets as undefined in a Jest snapshot. The solution was to include a setup file in jest.config.js → setupFiles as an array like below.
jest.config.js
{
setupFiles: [
"./app/javascript/tests/testsConfig.js",
],
}
testsConfig.js
process.env.BASE_DOMAIN = process.env.BASE_DOMAIN || 'http://localhost:3000/'
window.TestsConfig = {
"CLOUD_FRONT_BASE_URL": "https://cdn.site.com"
}
Upvotes: 0
Reputation: 898
I am not sure, but I think you could do it with jest.fn()
:
global.window = jest.fn(() => {
location: { ... }
})
Maybe even as window = jest.fn(...)
.
Upvotes: 1
Reputation: 4090
As mentioned by @RiZKiT in the comment below, since Jest v27.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.js-like) meaning that your test will be run under a Node.js environment
To solve it either you set your testEnvironment
to jsdom
Or you remove the testEnvironment
from your configuration, 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
}
}
}
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.
As I could see, your tests are meant to be run under a browser-like environment.
If you ever need an explicit Node.js 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 a Node.js environment:
/**
* @jest-environment jsdom
*/
test('use jsdom in this test file', () => {
const element = document.createElement('div');
expect(element).not.toBeNull();
});
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
.
Upvotes: 25
Reputation: 1192
Here you can find examples of how to do this:
DOM Testing React Applications with Jest
For example:
import {jsdom} from 'jsdom';
const documentHTML = '<!doctype html><html><body><div id="root"></div></body></html>';
global.document = jsdom(documentHTML);
global.window = document.parentWindow;
Upvotes: 4
Reputation: 639
See Expose jsdom to global environment #2460
It seems like one of the contributors declared that he is not planning to expose jsdom to global under the Jest environment.
However, you could use Object.defineProperty(window, 'location', {value: '…'}
API to approach it, like the developer from Facebook do. In your case it could be like:
Object.defineProperty(window, 'location', {
value: {
search: ...
},
})
Upvotes: 4
Reputation: 111062
You can simply mock location
:
global.location = {search: 'someSearchString'}
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.
export default () => window.location
So if your module looks like this:
const l = window.location
export default l
it will not work. In this case you could mock the module using jest.mock
.
Upvotes: 2
Reputation: 826
You could try doing
global.window = new jsdom.JSDOM().window;
global.document = window.document;
Upvotes: 4