import { screen, waitFor, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi } from "vitest"; import { renderWithProviders } from "../test/renderWithProviders"; import WorkspaceSelector from "./WorkspaceSelector"; function makeWorkspace(name: string, overrides: Record = {}) { return { id: `ws-${name}`, name, slug: name, path: `/workspaces/${name}`, runtime_backend: "container", container_name: `codex-${name}`, status: "active", provision_state: "ready", provision_error: "", last_provisioned_at: "", container_state: "running", ...overrides, }; } describe("WorkspaceSelector", () => { it("shows a loading label while workspaces are being resolved", () => { renderWithProviders( , { locale: "en" }, ); expect(screen.getByRole("button", { name: /loading workspaces/i })).toBeInTheDocument(); }); it("lets the user switch to another existing workspace", async () => { const onChange = vi.fn(); const user = userEvent.setup(); renderWithProviders( , { locale: "en" }, ); await user.click(await screen.findByRole("button", { name: /alpha/i })); const betaOption = (await screen.findByText("beta")).closest("button"); if (!betaOption) { throw new Error("beta workspace option not found"); } await user.click(betaOption); expect(onChange).toHaveBeenCalledWith("beta"); }); it("updates the trigger label when the active workspace prop changes", async () => { const { rerender } = renderWithProviders( , { locale: "en" }, ); expect(await screen.findByRole("button", { name: /alpha/i })).toBeInTheDocument(); rerender( , ); expect(await screen.findByRole("button", { name: /beta/i })).toBeInTheDocument(); }); it("opens a labeled dialog, focuses the active project, and restores focus on escape", async () => { const user = userEvent.setup(); renderWithProviders( , { locale: "en" }, ); const trigger = await screen.findByRole("button", { name: /alpha/i }); expect(trigger).toHaveAttribute("aria-expanded", "false"); await user.click(trigger); expect(trigger).toHaveAttribute("aria-expanded", "true"); const dialog = await screen.findByRole("dialog", { name: "Workspaces" }); await waitFor(() => { expect(within(dialog).getByRole("button", { name: /alpha/i })).toHaveFocus(); }); for (let i = 0; i < 4; i += 1) { await user.tab(); expect(dialog.contains(document.activeElement)).toBe(true); } await user.keyboard("{Escape}"); await waitFor(() => { expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); }); await waitFor(() => { expect(trigger).toHaveFocus(); }); expect(trigger).toHaveAttribute("aria-expanded", "false"); }); it("keeps the current workspace selected when the next workspace has no id", async () => { const onChange = vi.fn(); const user = userEvent.setup(); renderWithProviders( , { locale: "en" }, ); await user.click(await screen.findByRole("button", { name: /alpha/i })); const betaOption = (await screen.findByText("beta")).closest("button"); if (!betaOption) { throw new Error("beta workspace option not found"); } await user.click(betaOption); expect(await screen.findByText("Couldn't prepare the workspace runtime.")).toBeInTheDocument(); expect(onChange).not.toHaveBeenCalled(); }); it("shows a header-managed empty state when no workspaces exist", async () => { const user = userEvent.setup(); renderWithProviders( , { locale: "en" }, ); await user.click(await screen.findByRole("button", { name: /no workspaces/i })); expect(await screen.findByText("No workspaces yet")).toBeInTheDocument(); expect(screen.getByText("Workspaces must be created outside the header picker.")).toBeInTheDocument(); }); });