import { GlobalRegistrator } from "@happy-dom/global-registrator"; // Bun can run multiple test files in one process, and registerSw.test.ts // registers happy-dom globally too — guard so both files can run together. if (!GlobalRegistrator.isRegistered) { GlobalRegistrator.register(); } // Silences React's "environment not configured to support act(...)" warning; // this file drives React purely through explicit `act()` calls below. (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; import { test, expect, afterEach } from "bun:test"; import { act } from "react"; import { createRoot, type Root } from "react-dom/client"; import { Link, MemoryRouter, useLocation } from "react-router"; import { RouteErrorBoundary, RouteErrorBoundaryReset } from "./ErrorBoundary.tsx"; let container: HTMLDivElement | null = null; let root: Root | null = null; function mount(children: React.ReactNode) { container = document.createElement("div"); document.body.appendChild(container); root = createRoot(container); act(() => { root!.render(children); }); return container; } afterEach(() => { if (root) { act(() => { root!.unmount(); }); root = null; } if (container) { container.remove(); container = null; } }); function Thrower(): never { throw new Error("No exchange rate configured for EUR"); } function Fine() { return
all good
; } test("a throwing child renders the fallback including the thrown message", () => { const el = mount( , ); expect(el.textContent).toContain("This view could not be displayed"); expect(el.textContent).toContain("No exchange rate configured for EUR"); // The way back to Settings must still be present. const link = el.querySelector("a[href*='/settings']"); expect(link).not.toBeNull(); // "Try again" control is present to retry without a full reload. expect(el.textContent).toContain("Try again"); }); test("a non-throwing child renders normally", () => { const el = mount( , ); expect(el.textContent).toContain("all good"); expect(el.textContent).not.toContain("could not be displayed"); }); function View() { const location = useLocation(); return ( {location.pathname === "/a" ? : } ); } test("RouteErrorBoundaryReset resets the error state when the route changes", () => { const el = mount( go to settings , ); expect(el.textContent).toContain("No exchange rate configured for EUR"); // A real navigation (e.g. following the boundary's Settings link) changes // the route; the boundary is keyed on location.pathname, so it should // remount with a clean error state instead of staying stuck. const link = el.querySelector("a"); expect(link).not.toBeNull(); act(() => { link!.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true })); }); expect(el.textContent).toContain("all good"); expect(el.textContent).not.toContain("could not be displayed"); });