import { act, render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { http, HttpResponse } from "msw"; import { server } from "../test/msw"; import { useSessionStore } from "../store/sessionStore"; import { WidgetHost } from "./WidgetHost"; import { setAuthState } from "../auth/authState"; const userA = { issuer: "local", subject: "user-a", roles: ["user"] as const, permissions: ["session.use"], isAdmin: false, csrfToken: "a".repeat(43), session: null, }; beforeEach(() => { useSessionStore.getState().resetSession(); setAuthState(userA); }); test("a gate choice shows progress, prevents duplicate clicks, and stays silent on success", async () => { let release!: () => void; const held = new Promise((resolve) => { release = resolve; }); let requests = 0; server.use(http.post("/api/sessions/s1/response", async () => { requests += 1; await held; return new HttpResponse(null, { status: 204 }); })); useSessionStore.setState({ pendingWidget: { id: "gate-1", widget: "select", options: [{ id: "recommended", label: "Recommended answer" }], }, }); const user = userEvent.setup(); render(); const choice = screen.getByRole("button", { name: "Recommended answer" }); await user.click(choice); expect(screen.getByRole("status")).toHaveTextContent("Sending response"); expect(choice).toBeDisabled(); await user.click(choice); expect(requests).toBe(1); act(() => release()); await waitFor(() => expect(useSessionStore.getState().pendingWidget).toBeNull()); expect(useSessionStore.getState().toasts).toEqual([]); act(() => useSessionStore.setState({ pendingWidget: { id: "gate-2", widget: "select", options: [{ id: "next", label: "Next answer" }], }, })); expect(screen.getByRole("button", { name: "Next answer" })).toBeEnabled(); expect(screen.queryByRole("status")).not.toBeInTheDocument(); }); test("a delayed gate response from user A cannot clear user B's pending gate", async () => { let release!: () => void; let started!: () => void; let settled!: () => void; const held = new Promise((resolve) => { release = resolve; }); const requestStarted = new Promise((resolve) => { started = resolve; }); const requestSettled = new Promise((resolve) => { settled = resolve; }); server.use(http.post("/api/sessions/s1/response", async () => { started(); try { await held; return new HttpResponse(null, { status: 204 }); } finally { settled(); } })); setAuthState(userA); useSessionStore.setState({ pendingWidget: { id: "gate-a", widget: "select", options: [{ id: "a", label: "A answer" }] }, }); render(); await userEvent.click(screen.getByRole("button", { name: "A answer" })); await requestStarted; act(() => setAuthState({ ...userA, subject: "user-b", csrfToken: "b".repeat(43) })); act(() => useSessionStore.setState({ pendingWidget: { id: "gate-b", widget: "select", options: [{ id: "b", label: "B answer" }] }, })); release(); await act(async () => { await requestSettled; }); expect(useSessionStore.getState().pendingWidget?.id).toBe("gate-b"); expect(useSessionStore.getState().lastUserEntry).toBeNull(); });