magicrobots
magicrobots•2mo ago

Test react components, having difficulty with `document global not in Deno` using happy-dom

I've done:
import { Window } from 'happy-dom';

const window = new Window();
const document = window.document;
globalThis.document = document;
import { Window } from 'happy-dom';

const window = new Window();
const document = window.document;
globalThis.document = document;
and still get the error when running deno test --allow-a-bunch-of-stuff document global is not available in Deno. Any idea what I'm missing? Cheers y'all.
7 Replies
toni
toni•2mo ago
Did you use deno 2? AFAIK, global has been removed from deno 2. You can use latest deno 1.46.3
magicrobots
magicrobotsOP•2mo ago
Oh interesting, I am using deno 2 - I'll look into how it should be modified. That's good to know thank you
Brolands
Brolands•5d ago
Hello @magicrobots , Wondering whether you were able to make it work in the end with Deno 2? After searching around, this looks to be the best example of the setup: https://github.com/udibo/react-app-example/tree/main Curious if you did something different 🙂
GitHub
GitHub - udibo/react-app-example: A basic example of a Udibo React ...
A basic example of a Udibo React App. Contribute to udibo/react-app-example development by creating an account on GitHub.
magicrobots
magicrobotsOP•5d ago
Hey, I was able to get it working using happy-dom and Object.assign. Apologies for using a screenshot of all things, but I'm only using discord on my phone these days, and this is for work so it's a private repo. Anyway, let me know if this is useful:
No description
magicrobots
magicrobotsOP•5d ago
So then just import that at the beginning of each test that needs it and it's good to go I was trying to avoid jest, and have been successful at that - but do need testing-library for rendering react, which isn't too bad. It works for components and hooks.
Brolands
Brolands•3d ago
Nice, thanks a lot! I will try with jsdom approach that was mentioned in GitHub issue and then also with happy-dom with your example and then see what I like best 🙂 Would be cool to put this somewhere as a tutorial, because I as well wanted to avoid jest completely, as it didn't look so user friendly to me, when I first set it up So, I tried your @magicrobots approach with happy-dom and also approach with jsdom and for me with Deno v2.2.0 happy-dom works much faster. jsdom - 60ms happy-dom - 26ms What is interesting, with Deno.test I don't know why, but tests were taking like 15 seconds with happy-dom, so I thought it was much slower as jsdom was consistent with 60ms. But after using @std/testing/bdd library to have afterEach with cleanUp function in it to have multiple tests running, happy-dom started to perform super fast. So, again, thanks a lot for your setup example! I'm not yet sure if I need all of the properties you've setup for globalThis (not that much experience in FE development), but will see how it goes 🙂 Putting here as a reference test setups for both jsdom and happy-dom stored in TestSetup.ts file. For happy-dom:
import { PropertySymbol, Window} from 'happy-dom';

const window = new Window();
const document = window.document;
const browserWindow = document[PropertySymbol.window];

const setInnerHTML = (html: string) => document.documentElement.innerHTML = html;
const cancelAsync = () => window.happyDOM.abort();

Object.assign(globalThis,{
window,
document,
HTMLElement: browserWindow.HTMLElement,
Element: browserWindow.Element,
Node: browserWindow.Node,
navigator: browserWindow.navigator,
DocumentFragment: browserWindow.DocumentFragment,
DocumentType: browserWindow.DocumentType,
SVGElement: browserWindow.SVGElement,
Text: browserWindow.Text,
requestAnimationFrame: browserWindow.requestAnimationFrame,
cancelAnimationFrame: browserWindow.cancelAnimationFrame,
setTimeout: browserWindow.setTimeout,
clearTimeout: browserWindow.clearTimeout,
setInterval: browserWindow.setInterval,
clearInterval: browserWindow.clearInterval,
queueMicrotask: browserWindow.queueMicrotask,
abortController: browserWindow.AbortController,
cancelAsync,
setInnerHTML
})
import { PropertySymbol, Window} from 'happy-dom';

const window = new Window();
const document = window.document;
const browserWindow = document[PropertySymbol.window];

const setInnerHTML = (html: string) => document.documentElement.innerHTML = html;
const cancelAsync = () => window.happyDOM.abort();

Object.assign(globalThis,{
window,
document,
HTMLElement: browserWindow.HTMLElement,
Element: browserWindow.Element,
Node: browserWindow.Node,
navigator: browserWindow.navigator,
DocumentFragment: browserWindow.DocumentFragment,
DocumentType: browserWindow.DocumentType,
SVGElement: browserWindow.SVGElement,
Text: browserWindow.Text,
requestAnimationFrame: browserWindow.requestAnimationFrame,
cancelAnimationFrame: browserWindow.cancelAnimationFrame,
setTimeout: browserWindow.setTimeout,
clearTimeout: browserWindow.clearTimeout,
setInterval: browserWindow.setInterval,
clearInterval: browserWindow.clearInterval,
queueMicrotask: browserWindow.queueMicrotask,
abortController: browserWindow.AbortController,
cancelAsync,
setInnerHTML
})
For jsdom:
import {JSDOM} from "jsdom";

const defaultHtml = '<!doctype html><html lang="en"><head title="For testing"><meta charset="utf-8"></head><body></body></html>';
const jsdom = new JSDOM(defaultHtml);
const { window } = jsdom;
const { document } = window;
globalThis.document = document;
(globalThis as any).window = window;
window.console = globalThis.console;
import {JSDOM} from "jsdom";

const defaultHtml = '<!doctype html><html lang="en"><head title="For testing"><meta charset="utf-8"></head><body></body></html>';
const jsdom = new JSDOM(defaultHtml);
const { window } = jsdom;
const { document } = window;
globalThis.document = document;
(globalThis as any).window = window;
window.console = globalThis.console;
And then simple test file:
import { cleanup, render } from "@testing-library/react";
import { expect } from "jsr:@std/expect";
import * as React from "react";
import { afterEach, describe, it } from "@std/testing/bdd";

import "../config/TestSetup.ts";

describe("Helloing and worlding", () => {
afterEach(() => {
cleanup();
});
// ARRANGE
it("Helloes", () => {
const screen = render(<p>Hello</p>);
expect(screen.getByRole("paragraph").innerHTML).toBe("Hello");
});

it("Worlds", () => {
const screen = render(<p>World</p>);
expect(screen.getByRole("paragraph").innerHTML).toBe("World");
});
});
import { cleanup, render } from "@testing-library/react";
import { expect } from "jsr:@std/expect";
import * as React from "react";
import { afterEach, describe, it } from "@std/testing/bdd";

import "../config/TestSetup.ts";

describe("Helloing and worlding", () => {
afterEach(() => {
cleanup();
});
// ARRANGE
it("Helloes", () => {
const screen = render(<p>Hello</p>);
expect(screen.getByRole("paragraph").innerHTML).toBe("Hello");
});

it("Worlds", () => {
const screen = render(<p>World</p>);
expect(screen.getByRole("paragraph").innerHTML).toBe("World");
});
});
magicrobots
magicrobotsOP•3d ago
Awesome! Stoked you got it working and appreciate your work with the comparisons 🤘

Did you find this page helpful?