import type { ErrorInfo } from "react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { restartActiveDungeonSession } from "../game/dungeonSession"; import { SceneErrorBoundary } from "./SceneErrorBoundary"; vi.mock("../game/dungeonSession", () => ({ restartActiveDungeonSession: vi.fn(() => true), })); describe("scene error recovery", () => { afterEach(() => { vi.useRealTimers(); vi.unstubAllGlobals(); vi.restoreAllMocks(); }); it("automatically rebuilds the active dungeon without reloading the page", () => { vi.useFakeTimers(); vi.stubGlobal("window", { setTimeout, clearTimeout, location: { reload: vi.fn() }, }); vi.spyOn(console, "error").mockImplementation(() => undefined); const boundary = new SceneErrorBoundary({ children: null }); boundary.state = { error: new Error("transient GLB failure"), retryCount: 0, retryScheduled: false, }; boundary.setState = vi.fn((patch) => { const next = typeof patch === "function" ? patch(boundary.state, boundary.props) : patch; if (next) boundary.state = { ...boundary.state, ...next }; }) as typeof boundary.setState; boundary.componentDidCatch( boundary.state.error!, { componentStack: "\n at DungeonEnvironment" } as ErrorInfo, ); expect(boundary.state.retryScheduled).toBe(true); vi.advanceTimersByTime(500); expect(restartActiveDungeonSession).toHaveBeenCalledOnce(); expect(window.location.reload).not.toHaveBeenCalled(); expect(boundary.state).toMatchObject({ error: null, retryCount: 1, retryScheduled: false, }); boundary.componentWillUnmount(); }); });