diff --git a/frontend/src/components/ErrorBoundary.staleBuild.dom.test.tsx b/frontend/src/components/ErrorBoundary.staleBuild.dom.test.tsx new file mode 100644 index 00000000..3bbc1323 --- /dev/null +++ b/frontend/src/components/ErrorBoundary.staleBuild.dom.test.tsx @@ -0,0 +1,44 @@ +// @vitest-environment jsdom +// +// A tab reloading onto a newer build (lib/staleBuild) renders the chunk it could not load as a render +// error: the boundary must neither report it as a crash (a fatal notice outlives the reload) nor flash +// the crash screen before the reload lands. Its own file: staleBuild's "reloading" flag is module state +// and never turns back off. +import { afterEach, expect, it, vi } from "vitest"; +import { cleanup, render } from "@testing-library/react"; + +const notify = vi.fn(); +vi.mock("../lib/notifications", () => ({ notify: (...args: unknown[]) => notify(...args) })); + +import { clear, events } from "../lib/log"; +import { installStaleBuildReload } from "../lib/staleBuild"; +import ErrorBoundary from "./ErrorBoundary"; + +afterEach(() => { + cleanup(); + clear(); + vi.restoreAllMocks(); +}); + +it("a missing chunk while reloading for a new build is no crash: nothing reported, nothing shown", () => { + vi.spyOn(console, "error").mockImplementation(() => {}); // React reports the throw itself too + sessionStorage.clear(); + const reload = vi.fn(); + const uninstall = installStaleBuildReload(reload); + window.dispatchEvent(new Event("vite:preloadError", { cancelable: true })); + uninstall(); + expect(reload).toHaveBeenCalledOnce(); + + const Missing = () => { + throw new Error("Failed to fetch dynamically imported module"); + }; + const { container } = render( + + + , + ); + + expect(events().filter((e) => e.event === "ui.crash")).toEqual([]); + expect(notify).not.toHaveBeenCalled(); + expect(container.innerHTML).toBe(""); +}); diff --git a/frontend/src/components/Home.dom.test.tsx b/frontend/src/components/Home.dom.test.tsx index 4c2aab42..c41abdb4 100644 --- a/frontend/src/components/Home.dom.test.tsx +++ b/frontend/src/components/Home.dom.test.tsx @@ -575,13 +575,18 @@ describe("Home", () => { fireEvent.click(screen.getByText("home.budget.short")); fireEvent.click(screen.getByText("home.hero.reroll")); expect(await screen.findByText("Rerolled")).toBeTruthy(); + const asked = discoverPick.mock.lastCall; first.unmount(); discover.mockClear(); + discoverPick.mockClear(); mount(); expect(await screen.findByText("Rerolled")).toBeTruthy(); expect(discover).toHaveBeenLastCalledWith("short", 0); + // The very same question — seed, the cards already seen and the press count all came back, not + // just the budget: the server answers each of them differently. + await waitFor(() => expect(discoverPick.mock.lastCall).toEqual(asked)); }); // Measured in the browser (2026-09-07), before the fix: the palette's "Meglepetés" arrived at Home