Files
ai-workflow/dashboard/src/components/WorkspaceSelector.test.tsx
T

168 lines
5.0 KiB
TypeScript

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<string, unknown> = {}) {
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(
<WorkspaceSelector value="" workspaces={[]} loading onChange={vi.fn()} />,
{ 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(
<WorkspaceSelector
value="alpha"
workspaces={[makeWorkspace("alpha"), makeWorkspace("beta")]}
onChange={onChange}
/>,
{ 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(
<WorkspaceSelector
value="alpha"
workspaces={[makeWorkspace("alpha"), makeWorkspace("beta")]}
onChange={vi.fn()}
/>,
{ locale: "en" },
);
expect(await screen.findByRole("button", { name: /alpha/i })).toBeInTheDocument();
rerender(
<WorkspaceSelector
value="beta"
workspaces={[makeWorkspace("alpha"), makeWorkspace("beta")]}
onChange={vi.fn()}
/>,
);
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(
<WorkspaceSelector
value="alpha"
workspaces={[makeWorkspace("alpha"), makeWorkspace("beta")]}
onChange={vi.fn()}
/>,
{ 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(
<WorkspaceSelector
value="alpha"
workspaces={[
makeWorkspace("alpha"),
makeWorkspace("beta", {
id: "",
provision_state: "failed",
container_state: "missing",
}),
]}
onChange={onChange}
/>,
{ 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(
<WorkspaceSelector value="" workspaces={[]} onChange={vi.fn()} />,
{ 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();
});
});