Files
openclaw/test/jsdom-custom-elements.test.ts
T
Peter Steinberger e7a2d1758a fix: Control UI test lane fails at random on unrelated pull requests (#123512)
* fix(test): keep shared jsdom window in step with the per-file module reset

The non-isolated runner already resets the module graph after every test file,
so each file evaluates its own component classes. The jsdom window it shares
across the whole worker was never reset with it: every Control UI component
registers with `if (!customElements.get(tag))`, so the first file to import a
component owned that tag for the rest of the run and `document.createElement`
kept building elements closed over that file's module instances. Later files'
singletons, module mocks, and spies were never the ones production reached, so
assertions failed as "expected ... to be called once, but got 0 times" in
whichever files the size-based sequencer happened to place after a warming one.

Drop repo-owned tags with the graph they came from. Dependency packages are
externalized and register once per worker through native ESM, so their
definitions are attributed by define call site and kept.

The same window also carried mounted DOM forward, so helpers reading
`document.body.querySelector(...)` answered an earlier file's leaked dialog and
focus assertions read its stale activeElement. Clear the body with it;
`document.head` stays, since dependency styles cannot be replayed either.

* fix(test): keep the jsdom definition shape local to its module
2026-08-13 23:25:08 -07:00

57 lines
2.3 KiB
TypeScript

/* @vitest-environment jsdom */
import { describe, expect, it } from "vitest";
import {
dropRepoOwnedCustomElements,
isRepoOwnedDefineStack,
jsdomCustomElementDefinitions,
trackCustomElementRegistry,
} from "./jsdom-custom-elements.ts";
describe("jsdom custom element tracking", () => {
it("reaches the live jsdom definition list", () => {
// Contract with jsdom internals: losing this silently restores the stale-class
// flake, because the shared runner would stop dropping repo-owned tags.
const definitions = jsdomCustomElementDefinitions(customElements);
expect(Array.isArray(definitions)).toBe(true);
customElements.define("openclaw-jsdom-contract-probe", class extends HTMLElement {});
expect(definitions?.some((entry) => entry.name === "openclaw-jsdom-contract-probe")).toBe(true);
});
it("drops repo-owned tags and keeps dependency-owned ones", () => {
const tracking = trackCustomElementRegistry(customElements);
if (!tracking) {
throw new Error("expected a jsdom registry");
}
customElements.define("openclaw-repo-owned-probe", class extends HTMLElement {});
// Dependency packages are externalized and register once per worker, so their
// definitions must survive a reset that the module graph cannot replay.
tracking.definitions.push({ name: "wa-dependency-probe" });
dropRepoOwnedCustomElements(tracking);
expect(customElements.get("openclaw-repo-owned-probe")).toBeUndefined();
expect(tracking.definitions.some((entry) => entry.name === "wa-dependency-probe")).toBe(true);
// A repo module re-evaluated by the next file must be able to register again.
expect(() =>
customElements.define("openclaw-repo-owned-probe", class extends HTMLElement {}),
).not.toThrow();
});
it("attributes a define call to the module that made it", () => {
const stack = (caller: string) =>
`Error\n at define (/repo/test/jsdom-custom-elements.ts:58:9)\n${caller}`;
expect(isRepoOwnedDefineStack(stack(" at /repo/ui/src/components/tooltip.ts:576:18"))).toBe(
true,
);
expect(
isRepoOwnedDefineStack(
stack(
" at file:///repo/node_modules/@lit/reactive-element/decorators/custom-element.js:27:24",
),
),
).toBe(false);
expect(isRepoOwnedDefineStack(undefined)).toBe(false);
});
});