import { afterEach, describe, expect, it } from "vite-plus/test"; import { clearNativeCloseFocusOwner, finishNativeClosePointer, getNativeCloseFocusOwner, recordNativeCloseFocus, recordNativeCloseFocusOut, recordNativeClosePointer,} from "./nativeCloseFocus"; class MockElement extends EventTarget { isConnected = true; owner: "drawer" | "right-panel" | null = null; inRightPanel = false; inPreviewViewport = false; previewTabId: string | null = null; tagName = "div"; closest(selector: string): MockElement | null { if (!this.isConnected) return null; if (selector === "[data-terminal-owner]" && this.owner !== null) return this; if (selector.includes("[data-right-panel-tabbar]") && this.inRightPanel) return this; if (selector === "[data-preview-viewport]" && this.inPreviewViewport) return this; return null; } get dataset(): DOMStringMap { return { terminalOwner: this.owner ?? undefined } as DOMStringMap; } getAttribute(name: string): string | null { if (name === "data-preview-tab" || name === "data-preview-viewport") { return this.previewTabId; } return null; }} const originalDocument = globalThis.document;const originalElement = globalThis.Element; afterEach(() => { clearNativeCloseFocusOwner(); if (originalDocument === undefined) { delete (globalThis as { document?: Document }).document; } else { globalThis.document = originalDocument; } if (originalElement === undefined) { delete (globalThis as { Element?: typeof Element }).Element; } else { globalThis.Element = originalElement; }}); function installDom(activeElement: MockElement, body = new MockElement()): void { globalThis.Element = MockElement as unknown as typeof Element; globalThis.document = { activeElement, body } as unknown as Document;} describe("native close focus", () => { it("distinguishes drawer terminals, right-panel terminals, and other panel focus", () => { const drawer = new MockElement(); drawer.owner = "drawer"; installDom(drawer); expect(getNativeCloseFocusOwner(null)).toBe("drawer-terminal"); const panelTerminal = new MockElement(); panelTerminal.owner = "right-panel"; installDom(panelTerminal); expect(getNativeCloseFocusOwner(null)).toBe("right-panel-terminal"); const panelControl = new MockElement(); panelControl.inRightPanel = true; installDom(panelControl); expect(getNativeCloseFocusOwner(null)).toBe("right-panel"); }); it("retains ownership only while focus is obscured by native UI", () => { const terminal = new MockElement(); terminal.owner = "drawer"; const body = new MockElement(); installDom(body, body); recordNativeCloseFocus(terminal, null); recordNativeCloseFocusOut(null, false, null); expect(getNativeCloseFocusOwner(null)).toBe("drawer-terminal"); recordNativeCloseFocusOut(null, true, null); expect(getNativeCloseFocusOwner(null)).toBeNull(); }); it("preserves pointer ownership until the pointer gesture finishes", async () => { const panelChrome = new MockElement(); panelChrome.inRightPanel = true; const body = new MockElement(); installDom(body, body); recordNativeClosePointer(panelChrome, null); await Promise.resolve(); recordNativeCloseFocusOut(null, true, null); finishNativeClosePointer(); expect(getNativeCloseFocusOwner(null)).toBe("right-panel"); }); it("clears retained ownership on focus or pointer movement outside the panel", () => { const panel = new MockElement(); panel.inRightPanel = true; const outside = new MockElement(); installDom(outside); recordNativeCloseFocus(panel, null); recordNativeCloseFocus(outside, null); expect(getNativeCloseFocusOwner(null)).toBeNull(); }); it("updates ownership directly from a focusout related target", () => { const drawer = new MockElement(); drawer.owner = "drawer"; const panel = new MockElement(); panel.inRightPanel = true; installDom(panel); recordNativeCloseFocus(drawer, null); recordNativeCloseFocusOut(panel, true, null); expect(getNativeCloseFocusOwner(null)).toBe("right-panel"); }); it("treats hosted preview webviews and viewport chrome as right-panel focus", () => { const webview = new MockElement(); webview.tagName = "webview"; webview.previewTabId = "active-preview"; installDom(webview); expect(getNativeCloseFocusOwner("active-preview")).toBe("right-panel"); const viewport = new MockElement(); viewport.inPreviewViewport = true; viewport.previewTabId = "active-preview"; installDom(viewport); expect(getNativeCloseFocusOwner("active-preview")).toBe("right-panel"); }); it("ignores previews that are not the active right-panel preview", () => { const webview = new MockElement(); webview.tagName = "webview"; webview.previewTabId = "standalone-preview"; installDom(webview); recordNativeClosePointer(webview, "right-panel-preview"); expect(getNativeCloseFocusOwner("right-panel-preview")).toBeNull(); }); it("ignores detached retained targets", () => { const terminal = new MockElement(); terminal.owner = "drawer"; terminal.isConnected = false; const body = new MockElement(); installDom(body, body); recordNativeCloseFocus(terminal, null); expect(getNativeCloseFocusOwner(null)).toBeNull(); });});
import { afterEach, describe, expect, it } from "vite-plus/test";
import {
clearNativeCloseFocusOwner,
finishNativeClosePointer,
getNativeCloseFocusOwner,
recordNativeCloseFocus,
recordNativeCloseFocusOut,
recordNativeClosePointer,
} from "./nativeCloseFocus";
class MockElement extends EventTarget {
isConnected = true;
owner: "drawer" | "right-panel" | null = null;
inRightPanel = false;
inPreviewViewport = false;
previewTabId: string | null = null;
tagName = "div";
closest(selector: string): MockElement | null {
if (!this.isConnected) return null;
if (selector === "[data-terminal-owner]" && this.owner !== null) return this;
if (selector.includes("[data-right-panel-tabbar]") && this.inRightPanel) return this;
if (selector === "[data-preview-viewport]" && this.inPreviewViewport) return this;
return null;
}
get dataset(): DOMStringMap {
return { terminalOwner: this.owner ?? undefined } as DOMStringMap;
}
getAttribute(name: string): string | null {
if (name === "data-preview-tab" || name === "data-preview-viewport") {
return this.previewTabId;
}
return null;
}
}
const originalDocument = globalThis.document;
const originalElement = globalThis.Element;
afterEach(() => {
clearNativeCloseFocusOwner();
if (originalDocument === undefined) {
delete (globalThis as { document?: Document }).document;
} else {
globalThis.document = originalDocument;
}
if (originalElement === undefined) {
delete (globalThis as { Element?: typeof Element }).Element;
} else {
globalThis.Element = originalElement;
}
});
function installDom(activeElement: MockElement, body = new MockElement()): void {
globalThis.Element = MockElement as unknown as typeof Element;
globalThis.document = { activeElement, body } as unknown as Document;
}
describe("native close focus", () => {
it("distinguishes drawer terminals, right-panel terminals, and other panel focus", () => {
const drawer = new MockElement();
drawer.owner = "drawer";
installDom(drawer);
expect(getNativeCloseFocusOwner(null)).toBe("drawer-terminal");
const panelTerminal = new MockElement();
panelTerminal.owner = "right-panel";
installDom(panelTerminal);
expect(getNativeCloseFocusOwner(null)).toBe("right-panel-terminal");
const panelControl = new MockElement();
panelControl.inRightPanel = true;
installDom(panelControl);
expect(getNativeCloseFocusOwner(null)).toBe("right-panel");
});
it("retains ownership only while focus is obscured by native UI", () => {
const terminal = new MockElement();
terminal.owner = "drawer";
const body = new MockElement();
installDom(body, body);
recordNativeCloseFocus(terminal, null);
recordNativeCloseFocusOut(null, false, null);
expect(getNativeCloseFocusOwner(null)).toBe("drawer-terminal");
recordNativeCloseFocusOut(null, true, null);
expect(getNativeCloseFocusOwner(null)).toBeNull();
});
it("preserves pointer ownership until the pointer gesture finishes", async () => {
const panelChrome = new MockElement();
panelChrome.inRightPanel = true;
const body = new MockElement();
installDom(body, body);
recordNativeClosePointer(panelChrome, null);
await Promise.resolve();
recordNativeCloseFocusOut(null, true, null);
finishNativeClosePointer();
expect(getNativeCloseFocusOwner(null)).toBe("right-panel");
});
it("clears retained ownership on focus or pointer movement outside the panel", () => {
const panel = new MockElement();
panel.inRightPanel = true;
const outside = new MockElement();
installDom(outside);
recordNativeCloseFocus(panel, null);
recordNativeCloseFocus(outside, null);
expect(getNativeCloseFocusOwner(null)).toBeNull();
});
it("updates ownership directly from a focusout related target", () => {
const drawer = new MockElement();
drawer.owner = "drawer";
const panel = new MockElement();
panel.inRightPanel = true;
installDom(panel);
recordNativeCloseFocus(drawer, null);
recordNativeCloseFocusOut(panel, true, null);
expect(getNativeCloseFocusOwner(null)).toBe("right-panel");
});
it("treats hosted preview webviews and viewport chrome as right-panel focus", () => {
const webview = new MockElement();
webview.tagName = "webview";
webview.previewTabId = "active-preview";
installDom(webview);
expect(getNativeCloseFocusOwner("active-preview")).toBe("right-panel");
const viewport = new MockElement();
viewport.inPreviewViewport = true;
viewport.previewTabId = "active-preview";
installDom(viewport);
expect(getNativeCloseFocusOwner("active-preview")).toBe("right-panel");
});
it("ignores previews that are not the active right-panel preview", () => {
const webview = new MockElement();
webview.tagName = "webview";
webview.previewTabId = "standalone-preview";
installDom(webview);
recordNativeClosePointer(webview, "right-panel-preview");
expect(getNativeCloseFocusOwner("right-panel-preview")).toBeNull();
});
it("ignores detached retained targets", () => {
const terminal = new MockElement();
terminal.owner = "drawer";
terminal.isConnected = false;
const body = new MockElement();
installDom(body, body);
recordNativeCloseFocus(terminal, null);
expect(getNativeCloseFocusOwner(null)).toBeNull();
});
});