From c3425e1d7b9550c7ff36deda1c1b0fc0f97332a1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 1 Oct 2026 06:22:55 -0700 Subject: [PATCH] fix(test): shared-worker files inherit document focus after jsdom 30.1.1 jsdom 30.1.1 (#162575) implements the HTML unfocusing steps: blur() now focuses the document viewport, so document.hasFocus() stays true. The non-isolated runner's between-file reset focuses and blurs body to clear stale shadow focus, which therefore left every later file with hasFocus() === true instead of a fresh document's false. The runner's composite test (10-focus observers) failed on main, and UI code that gates on document.hasFocus() (usage refresh policy) could behave differently in a shared worker than in an isolated file. jsdom-compat already owns the jsdom internal bindings; clear the viewport-only focus there after the reset, restoring the fresh-document state. Element focus is untouched. --- test/jsdom-compat.mts | 13 +++++++++++++ test/non-isolated-runner.ts | 2 ++ 2 files changed, 15 insertions(+) diff --git a/test/jsdom-compat.mts b/test/jsdom-compat.mts index 18f30fb27e45..b4cd9aba8f5c 100644 --- a/test/jsdom-compat.mts +++ b/test/jsdom-compat.mts @@ -42,6 +42,19 @@ function bindings() { return { utils, eventTarget, blob, formData, registry }; } +// jsdom 30.1.1 follows the unfocusing steps: blur() focuses the document viewport, so +// hasFocus() stays true. A fresh document has no focused area; restore that state. +export function clearJsdomViewportFocus(document: Document): void { + const impl = bindings().utils.implForWrapper(document) as { + _lastFocusedElement?: unknown; + _clearDOMSelector?: () => void; + } | null; + if (impl && impl._lastFocusedElement === impl) { + impl._lastFocusedElement = null; + impl._clearDOMSelector?.(); + } +} + export function jsdomCustomElementDefinitions(registry: object) { const native = bindings().registry; return native.is(registry) diff --git a/test/non-isolated-runner.ts b/test/non-isolated-runner.ts index c2ff8ce170a3..9b79cab4b047 100644 --- a/test/non-isolated-runner.ts +++ b/test/non-isolated-runner.ts @@ -22,6 +22,7 @@ import { resetGatewayWorkAdmission, } from "../src/process/gateway-work-admission.js"; import { hasOpenClawAgentDatabaseAsyncResources } from "../src/state/openclaw-agent-db-resources.js"; +import { clearJsdomViewportFocus } from "./jsdom-compat.mts"; import { type CustomElementTracking, dropRepoOwnedCustomElements, @@ -223,6 +224,7 @@ function resetSharedDocumentBody(): void { body.focus(); body.blur(); body.removeAttribute("tabindex"); + clearJsdomViewportFocus(body.ownerDocument); } function restoreRealTimers(): void {