// @vitest-environment jsdom // // ONE test, ONE file, on purpose. This is the close→reclaim→run ordering contract of the palette // (see closeThenNavigate), and it ASSERTS on a real useBackToClose entry driven through a real // programmatic history.back() — which is why it cannot share module state with anything. (It is not // the only place in the suite where an unstubbed back() fires; history.burial.dom.test.ts is the // other file that asserts on one, and any test rendering a real Modal can let one go.) // // It cannot live in CommandPalette.dom.test.tsx. jsdom gives a test FILE a single session history, // and history.ts's overlay bookkeeping is module-global, so the sibling tests there leave an // in-flight programmatic back() behind — under which this one either hangs or, worse, goes green on // somebody else's popstate. That is exactly how it used to pass: it asserted nothing on its own, and // running it with `-t` failed (E10 review round 1). Its own file gives it clean module state. import { useState } from "react"; import { describe, it, expect, vi, beforeAll } from "vitest"; import { render, screen, fireEvent, waitFor, act } from "@testing-library/react"; import CommandPalette, { type CommandGroup } from "./CommandPalette"; vi.mock("react-i18next", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, useTranslation: () => ({ t: (k: string) => k }) }; }); beforeAll(() => { // jsdom has no layout, so scrollIntoView is missing on the elements the active-row effect touches. Element.prototype.scrollIntoView ??= () => {}; }); const ran: string[] = []; function Harness() { const [isOpen, setIsOpen] = useState(true); const groups: CommandGroup[] = [ { id: "channels", label: "Channels", items: [{ id: "ch:1", label: "TheVR Podcast", run: () => ran.push("ch:1") }], }, ]; return ( { ran.push("closed"); setIsOpen(false); }} groups={groups} /> ); } describe("CommandPalette · closeThenNavigate ordering", () => { it("closes, and runs the command only once the close has landed", async () => { render(); // Load-bearing: useBackToClose pushes its entry from a coalesced microtask, so a click in the // same synchronous tick as the render finds no entry to reclaim — no back(), no popstate. await act(async () => { await Promise.resolve(); }); fireEvent.click(screen.getByText("TheVR Podcast")); expect(ran).toEqual(["closed"]); // Inside closeThenNavigate's 250ms no-popstate fallback, so a pass means the RECLAIM ran the // command — not the safety net that exists for overlays which never got an entry at all. await waitFor(() => expect(ran).toEqual(["closed", "ch:1"]), { timeout: 200 }); }); });