If you're writing unit tests for your components with Jest, you're probably using it under the hood, JSDOM.
JSDOM is a pure-JavaScript implementation of many web standards. In other words, because our tests do not run in the browser but in node, we need, somehow, to have the same API available in the browser to fake render the components.
The problem is that it does not implement every single web api, which would be unpractical and not necessary to be fair, and sometimes, our code relies on global methods, and we want to test that.
Problem
In my current project, I've implemented a React component that has a URL auto-generated based on input values (like project name, environment, etc.) and a button the user could click and open in another tab this URL.
I could not use native anchor elements and pass href. Instead, I had to use a component from our component library to have the company look and fill and force this navigation via window.open:
<ButtonCircular
color="outline"
onClick={() => {
window.open(repoUrl, "_blank");
}}
>
Open
</ButtonCircular>;
So, I wanted to test that whenever the user fills the fields and presses the open button, we call window.open with the generated URL.
it("opens another browser tab with the repository URL", () => {
// ... setup code
const openSpy = jest.spyOn(window, "open");
const openUrlElement = screen.getByTestId("open-url");
fireEvent.click(openUrlElement);
expect(openSpy).toHaveBeenCalledWith(inputUrlElement.value, "_blank");
});
But then, I got this error:
console.error
Error: Not implemented: window.open
at module.exports (/Users/raulmelo/development/test/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
Solution
As I said before, JSDOM does not implement ALL browser methods.
You can check a list of not implemented methods here
To solve that, we could assign a jest mock function (jest.fn) to window.open and assert against that.
Jest has a configuration called setupFilesAfterEnv, which receives a list of files that jest will run before executing the assertions. You can understand that as an "environment preparation" for your tests.
In this context, we can add the window.open.
I'm going to call this file jest.setup.js and place it at the root project level but feel free to give the name you want and place it wherever you desire:
/**
* JSDOM does not implement global "open" function
*/
window.open = jest.fn();
Now, all I need is to reference this file in jest.config.js:
module.exports = {
// ... other configs
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']
}
By doing that, now in my test environment, window.open will be defined, and it'll be a jest function where I can assert against it.
Conclusion
window.open isn't the only method not implemented, and you probably will face more problems like that.
Luckily the solution could be the same for all of them.