register/zlayer/anchor/motion/density are pure cores over plain data, so most of the suite runs with no DOM at all; jsdom covers only what is genuinely DOM. 100 tests, zero elements. These five are why v4 had 11 hand-built modals and 7 hand-built popovers: there was nothing shared to reuse. register makes an element exist only because a file defined it; portal gives every overlay one stacking parent; zlayer owns the only z-index in the app; dismiss keeps one stack so Escape pops the top and not the modal underneath; anchor measures, flips and clamps once. Three bugs the tests found: unmount() called removeChild on null when no portal existed yet, focusables() checked aria-hidden on the element instead of its ancestors so a hidden subtree stayed tab-reachable, and the density ratios v4 hand-tuned are now derived from the gap they were meant to allow for.
34 lines
1.2 KiB
JavaScript
34 lines
1.2 KiB
JavaScript
// A DOM for the internals that genuinely need one.
|
|
//
|
|
// jsdom is honest about structure and events and dishonest about layout -- it
|
|
// has no real box model, so anything that depends on measured geometry belongs
|
|
// in the browser (see the catalogue and the Playwright pass), not here.
|
|
|
|
import { JSDOM } from "jsdom";
|
|
|
|
const GLOBALS = [
|
|
"window", "document", "Node", "HTMLElement", "Event", "KeyboardEvent",
|
|
"CustomEvent", "getComputedStyle", "DocumentFragment", "Text",
|
|
];
|
|
|
|
export function setupDom(html = "<!doctype html><html><body></body></html>") {
|
|
const dom = new JSDOM(html, { pretendToBeVisual: true });
|
|
for (const name of GLOBALS) globalThis[name] = dom.window[name];
|
|
globalThis.window = dom.window;
|
|
globalThis.document = dom.window.document;
|
|
return dom;
|
|
}
|
|
|
|
export function teardownDom() {
|
|
for (const name of GLOBALS) delete globalThis[name];
|
|
}
|
|
|
|
/** Dispatch an event as it would arrive from a real interaction. */
|
|
export const fire = (node, type, init = {}) =>
|
|
node.dispatchEvent(new globalThis.window.Event(type, { bubbles: true, cancelable: true, ...init }));
|
|
|
|
export const key = (node, k) =>
|
|
node.dispatchEvent(new globalThis.window.KeyboardEvent("keydown", {
|
|
key: k, bubbles: true, cancelable: true,
|
|
}));
|