36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
import { act, cleanup, render, screen } from "@testing-library/react";
|
|
import { WidgetHost } from "./WidgetHost";
|
|
import { useSessionStore } from "../store/sessionStore";
|
|
import type { WidgetDescriptor } from "../api/types";
|
|
|
|
// Replace the widget registry with a renderer that always throws, simulating a
|
|
// viewer/widget that crashes on malformed gate data (the real white-screen path).
|
|
vi.mock("../widgets", () => ({
|
|
resolve: () => function Exploding() {
|
|
throw new Error("bad gate payload");
|
|
},
|
|
}));
|
|
|
|
let errorSpy: ReturnType<typeof vi.spyOn>;
|
|
beforeEach(() => {
|
|
errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
useSessionStore.getState().resetSession();
|
|
});
|
|
afterEach(() => {
|
|
cleanup();
|
|
errorSpy.mockRestore();
|
|
act(() => useSessionStore.getState().resetSession());
|
|
});
|
|
|
|
const gate: WidgetDescriptor = { id: "g1", widget: "select", title: "Choose" };
|
|
|
|
test("a crashing widget shows a fallback instead of unmounting the app", async () => {
|
|
useSessionStore.setState({ pendingWidget: gate });
|
|
await act(async () => { render(<WidgetHost sessionId="s1" />); });
|
|
// No throw escaped: the fallback rendered.
|
|
expect(await screen.findByRole("alert")).toBeInTheDocument();
|
|
expect(await screen.findByText(/couldn't be displayed/i)).toBeInTheDocument();
|
|
// The store (and thus the surrounding app) is still alive and interactive.
|
|
expect(useSessionStore.getState().pendingWidget).toEqual(gate);
|
|
});
|