Reputation: 13379
I'm trying to write tests for my web components projects in jest. I already use babel with es2015 preset. I'm facing an issue while loading the js file. I have followed a piece of code where document
object has a currentScript
object. But in test context it is null
. So I was thinking of mocking same. But jest.fn()
is not really help in same. How can I handle this issue?
Piece of code where jest is failing.
var currentScriptElement = document._currentScript || document.currentScript;
var importDoc = currentScriptElement.ownerDocument;
Test case I have written. component.test.js
import * as Component from './sample-component.js';
describe('component test', function() {
it('check instance', function() {
console.log(Component);
expect(Component).toBeDefined();
});
});
Following is the error thrown by jest
Test suite failed to run
TypeError: Cannot read property 'ownerDocument' of null
at src/components/sample-component/sample-component.js:4:39
Update: As per suggestion from Andreas Köberle, I have added some global vars and tried to mock like following
__DEV__.document.currentScript = document._currentScript = {
ownerDocument: ''
};
__DEV__.window = {
document: __DEV__.document
}
__DEV__.document.registerElement = jest.fn();
import * as Component from './arc-sample-component.js';
describe('component test', function() {
it('check instance', function() {
console.log(Component);
expect(Component).toBeDefined();
});
});
But no luck
Update: I have tried above code without __dev__
. Also by setting document as global.
Upvotes: 88
Views: 166007
Reputation: 1
Object.defineProperty(global.document, 'visibilityState', {
configurable: true,
writable: true,
value: {
get: jest.fn().mockResolvedValue('visible'),
},
});
Upvotes: 0
Reputation: 8699
Similar to what others have said, but instead of trying to mock the DOM yourself, just use JSDOM:
// __mocks__/client.js
import { JSDOM } from "jsdom"
const dom = new JSDOM()
global.document = dom.window.document
global.window = dom.window
Then in your jest config:
"setupFiles": [
"./__mocks__/client.js"
],
Upvotes: 57
Reputation: 441
I have been struggling with mocking document for a project I am on. I am calling document.querySelector()
inside a React component and need to make sure it is working right. Ultimately this is what worked for me:
it('should test something', () => {
const spyFunc = jest.fn();
Object.defineProperty(global.document, 'querySelector', { value: spyFunc });
<run some test>
expect(spyFunc).toHaveBeenCalled()
});
Upvotes: 20
Reputation: 1912
I found another solution. Let's say inside of your component you want to get a reference to an element in the DOM by className (document.getElementsByClassName). You could do the following:
let wrapper
beforeEach(() => {
wrapper = mount(<YourComponent/>)
jest.spyOn(document, 'getElementsByClassName').mockImplementation(() =>
[wrapper.find('.some-class').getDOMNode()]
)
})
This way you are manually setting the return value of getElementsByClassName equal to the reference of .some-class. It might be necessary to rerender the component by calling wrapper.setProps({}).
Hope this helps some of you!
Upvotes: 2
Reputation: 9325
This is the structure in my project named super-project inside the folder super-project:
__mocks__
You need to setup Jest to use a mock in your tests:
dom.js:
import { JSDOM } from "jsdom"
const dom = new JSDOM()
global.document = dom.window.document
global.window = dom.window
user.js:
export function create() {
return document.createElement('table');
}
user.test.js:
import { create } from "../src/user";
test('create table', () => {
expect(create().outerHTML).toBe('<table></table>');
});
jest.config.js:
module.exports = {
setupFiles: ["./config/__mocks__/dom.js"],
};
References:
You need to create a manual mock:
https://jestjs.io/docs/en/manual-mocks.html
Manipulating DOM object:
https://jestjs.io/docs/en/tutorial-jquery
Jest Configuration:
https://jestjs.io/docs/en/configuration
Upvotes: 6
Reputation: 466
Hope this helps
const wrapper = document.createElement('div');
const render = shallow(<MockComponent{...props} />);
document.getElementById = jest.fn((id) => {
wrapper.innerHTML = render.find(`#${id}`).html();
return wrapper;
});
Upvotes: -2
Reputation: 3476
If you need to define test values for properties, there is a slightly more granular approach. Each property needs to be defined individually, and it's also necessary to make the properties writeable
:
Object.defineProperty(window.document, 'URL', {
writable: true,
value: 'someurl'
});
See: https://github.com/facebook/jest/issues/890
This worked for me using Jest 21.2.1
and Node v8.11.1
Upvotes: 7
Reputation: 1363
If like me you're looking to mock document to undefined (e.g. for server side / client side tests) I was able to use object.defineProperty inside my test suites without having to use setupFiles
Example:
beforeAll(() => {
Object.defineProperty(global, 'document', {});
})
Upvotes: 7
Reputation: 31
I could resolve this same issue using global
scope module on nodejs, setting document with mock of document, in my case, getElementsByClassName
:
// My simple mock file
export default {
getElementsByClassName: () => {
return [{
className: 'welcome'
}]
}
};
// Your test file
import document from './name.component.mock.js';
global.document = {
getElementsByClassName: document.getElementsByClassName
};
Upvotes: 3
Reputation: 13379
I have resolved this using setUpFiles
property in jest. This will execute after jsdom and before each test which is perfect for me.
Set setupFiles, in Jest config, e.g.:
"setupFiles": ["<rootDir>/browserMock.js"]
// browserMock.js
Object.defineProperty(document, 'currentScript', {
value: document.createElement('script'),
});
Ideal situation would be loading webcomponents.js to polyfill the jsdom.
Upvotes: 25