import type { UserWorkspace } from "@/lib/api/workspaces"; import type { ReactElement, ReactNode } from "react"; import { WorkspaceMemberRole } from "@mosaic/shared"; import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { createWorkspace, fetchUserWorkspaces } from "@/lib/api/workspaces"; import WorkspacesPage from "./page"; vi.mock("next/link", () => ({ default: function LinkMock({ children, href, }: { children: ReactNode; href: string; }): ReactElement { return {children}; }, })); vi.mock("@/components/workspace/WorkspaceCard", () => ({ WorkspaceCard: function WorkspaceCardMock({ workspace, userRole, memberCount, }: { workspace: { name: string }; userRole: WorkspaceMemberRole; memberCount: number; }): ReactElement { return (
{workspace.name} | {userRole} | {String(memberCount)}
); }, })); vi.mock("@/lib/api/workspaces", () => ({ fetchUserWorkspaces: vi.fn(), createWorkspace: vi.fn(), })); const fetchUserWorkspacesMock = vi.mocked(fetchUserWorkspaces); const createWorkspaceMock = vi.mocked(createWorkspace); const baseWorkspace: UserWorkspace = { id: "workspace-1", name: "Personal Workspace", ownerId: "owner-1", role: WorkspaceMemberRole.OWNER, createdAt: "2026-01-01T00:00:00.000Z", }; describe("WorkspacesPage", () => { beforeEach(() => { vi.clearAllMocks(); }); it("loads and renders user workspaces from the API", async () => { fetchUserWorkspacesMock.mockResolvedValue([baseWorkspace]); render(); expect(screen.getByText("Loading workspaces...")).toBeInTheDocument(); expect(await screen.findByText("Your Workspaces (1)")).toBeInTheDocument(); expect(screen.getByTestId("workspace-card")).toHaveTextContent("Personal Workspace"); expect(fetchUserWorkspacesMock).toHaveBeenCalledTimes(1); }); it("shows fetch errors in the UI", async () => { fetchUserWorkspacesMock.mockRejectedValue(new Error("Unable to load workspaces")); render(); expect(await screen.findByText("Unable to load workspaces")).toBeInTheDocument(); }); it("creates a workspace and refreshes the list", async () => { fetchUserWorkspacesMock.mockResolvedValueOnce([baseWorkspace]).mockResolvedValueOnce([ baseWorkspace, { ...baseWorkspace, id: "workspace-2", name: "New Workspace", role: WorkspaceMemberRole.MEMBER, }, ]); createWorkspaceMock.mockResolvedValue({ id: "workspace-2", name: "New Workspace", ownerId: "owner-1", settings: {}, createdAt: "2026-01-02T00:00:00.000Z", updatedAt: "2026-01-02T00:00:00.000Z", memberCount: 1, }); const user = userEvent.setup(); render(); expect(await screen.findByText("Your Workspaces (1)")).toBeInTheDocument(); await user.type(screen.getByPlaceholderText("Enter workspace name..."), "New Workspace"); await user.click(screen.getByRole("button", { name: "Create Workspace" })); await waitFor(() => { expect(createWorkspaceMock).toHaveBeenCalledWith({ name: "New Workspace" }); }); await waitFor(() => { expect(fetchUserWorkspacesMock).toHaveBeenCalledTimes(2); }); expect(await screen.findByText("Your Workspaces (2)")).toBeInTheDocument(); }); it("shows create errors in the UI", async () => { fetchUserWorkspacesMock.mockResolvedValue([baseWorkspace]); createWorkspaceMock.mockRejectedValue(new Error("Workspace creation failed")); const user = userEvent.setup(); render(); expect(await screen.findByText("Your Workspaces (1)")).toBeInTheDocument(); await user.type(screen.getByPlaceholderText("Enter workspace name..."), "Bad Workspace"); await user.click(screen.getByRole("button", { name: "Create Workspace" })); expect(await screen.findByText("Workspace creation failed")).toBeInTheDocument(); }); });