import { expect, test } from "vitest"; import { screen, waitFor, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { http, HttpResponse } from "msw"; import { server } from "../test/server"; import { renderApp } from "../test/render"; import { UserMenu } from "./user-menu"; test("shows the signed-in email on the trigger", async () => { renderApp(); expect(await screen.findByText("editor@example.com")).toBeInTheDocument(); }); test("opens the menu showing email + role and signs out", async () => { let loggedOut = false; server.use( http.post("/api/admin/logout", () => { loggedOut = true; return new HttpResponse(null, { status: 204 }); }), ); renderApp(); const trigger = await screen.findByRole("button", { name: /editor@example.com/ }); await userEvent.click(trigger); // Menu content renders in a portal on document.body. const menu = within(document.body); expect(await menu.findByText("editor")).toBeInTheDocument(); const signOut = await menu.findByText("Sign out"); await userEvent.click(signOut); await waitFor(() => expect(loggedOut).toBe(true)); }); test("shows a pending state on Sign out while logging out", async () => { let release!: () => void; const gate = new Promise((resolve) => { release = resolve; }); server.use( http.post("/api/admin/logout", async () => { await gate; return new HttpResponse(null, { status: 204 }); }), ); renderApp(); const trigger = await screen.findByRole("button", { name: /editor@example.com/ }); await userEvent.click(trigger); const menu = within(document.body); await userEvent.click(await menu.findByText("Sign out")); // The logout is held open by `gate`, so the pending state is observed // deterministically (no reliance on a timing window). expect(await menu.findByText(/signing out/i)).toBeInTheDocument(); release(); await waitFor(() => expect(menu.queryByText(/signing out/i)).toBeNull()); });