All checks were successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
160 lines
4.7 KiB
TypeScript
160 lines
4.7 KiB
TypeScript
import type { UserWorkspace, WorkspaceMemberEntry } 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, fetchWorkspaceMembers } from "@/lib/api/workspaces";
|
|
import WorkspacesPage from "./page";
|
|
|
|
vi.mock("next/link", () => ({
|
|
default: function LinkMock({
|
|
children,
|
|
href,
|
|
}: {
|
|
children: ReactNode;
|
|
href: string;
|
|
}): ReactElement {
|
|
return <a href={href}>{children}</a>;
|
|
},
|
|
}));
|
|
|
|
vi.mock("@/lib/api/workspaces", () => ({
|
|
fetchUserWorkspaces: vi.fn(),
|
|
createWorkspace: vi.fn(),
|
|
fetchWorkspaceMembers: vi.fn(),
|
|
addWorkspaceMember: vi.fn(),
|
|
removeWorkspaceMember: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/lib/api/admin", () => ({
|
|
fetchAdminUsers: vi.fn(),
|
|
}));
|
|
|
|
const fetchUserWorkspacesMock = vi.mocked(fetchUserWorkspaces);
|
|
const createWorkspaceMock = vi.mocked(createWorkspace);
|
|
const fetchWorkspaceMembersMock = vi.mocked(fetchWorkspaceMembers);
|
|
|
|
const workspaceA: UserWorkspace = {
|
|
id: "workspace-1",
|
|
name: "Personal Workspace",
|
|
ownerId: "owner-1",
|
|
role: WorkspaceMemberRole.OWNER,
|
|
createdAt: "2026-01-01T00:00:00.000Z",
|
|
};
|
|
|
|
const workspaceB: UserWorkspace = {
|
|
id: "workspace-2",
|
|
name: "Client Workspace",
|
|
ownerId: "owner-2",
|
|
role: WorkspaceMemberRole.ADMIN,
|
|
createdAt: "2026-01-02T00:00:00.000Z",
|
|
};
|
|
|
|
const membersA: WorkspaceMemberEntry[] = [
|
|
{
|
|
workspaceId: "workspace-1",
|
|
userId: "user-a",
|
|
role: WorkspaceMemberRole.OWNER,
|
|
joinedAt: "2026-01-03T00:00:00.000Z",
|
|
user: {
|
|
id: "user-a",
|
|
email: "alice@example.com",
|
|
name: "Alice",
|
|
image: null,
|
|
},
|
|
},
|
|
];
|
|
|
|
const membersB: WorkspaceMemberEntry[] = [
|
|
{
|
|
workspaceId: "workspace-2",
|
|
userId: "user-b",
|
|
role: WorkspaceMemberRole.MEMBER,
|
|
joinedAt: "2026-01-04T00:00:00.000Z",
|
|
user: {
|
|
id: "user-b",
|
|
email: "bob@example.com",
|
|
name: "Bob",
|
|
image: null,
|
|
},
|
|
},
|
|
];
|
|
|
|
describe("WorkspacesPage", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("loads workspaces and fetches members for the first workspace", async () => {
|
|
fetchUserWorkspacesMock.mockResolvedValue([workspaceA, workspaceB]);
|
|
fetchWorkspaceMembersMock.mockResolvedValue(membersA);
|
|
|
|
render(<WorkspacesPage />);
|
|
|
|
expect(await screen.findByText("Your Workspaces (2)")).toBeInTheDocument();
|
|
expect(await screen.findByText("Personal Workspace Members")).toBeInTheDocument();
|
|
|
|
await waitFor(() => {
|
|
expect(fetchWorkspaceMembersMock).toHaveBeenCalledWith("workspace-1");
|
|
});
|
|
|
|
expect(screen.getByText("alice@example.com")).toBeInTheDocument();
|
|
});
|
|
|
|
it("switches selected workspace and reloads member list", async () => {
|
|
fetchUserWorkspacesMock.mockResolvedValue([workspaceA, workspaceB]);
|
|
fetchWorkspaceMembersMock.mockResolvedValueOnce(membersA).mockResolvedValueOnce(membersB);
|
|
|
|
const user = userEvent.setup();
|
|
render(<WorkspacesPage />);
|
|
|
|
expect(await screen.findByText("Personal Workspace Members")).toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole("button", { name: /client workspace/i }));
|
|
|
|
await waitFor(() => {
|
|
expect(fetchWorkspaceMembersMock).toHaveBeenLastCalledWith("workspace-2");
|
|
});
|
|
|
|
expect(await screen.findByText("Client Workspace Members")).toBeInTheDocument();
|
|
expect(screen.getByText("bob@example.com")).toBeInTheDocument();
|
|
});
|
|
|
|
it("creates a workspace and refreshes the list", async () => {
|
|
fetchUserWorkspacesMock
|
|
.mockResolvedValueOnce([workspaceA])
|
|
.mockResolvedValueOnce([workspaceA, workspaceB]);
|
|
fetchWorkspaceMembersMock.mockResolvedValue(membersA);
|
|
createWorkspaceMock.mockResolvedValue({
|
|
id: "workspace-2",
|
|
name: "Client Workspace",
|
|
ownerId: "owner-2",
|
|
settings: {},
|
|
createdAt: "2026-01-02T00:00:00.000Z",
|
|
updatedAt: "2026-01-02T00:00:00.000Z",
|
|
memberCount: 1,
|
|
});
|
|
|
|
const user = userEvent.setup();
|
|
render(<WorkspacesPage />);
|
|
|
|
expect(await screen.findByText("Your Workspaces (1)")).toBeInTheDocument();
|
|
|
|
await user.type(screen.getByPlaceholderText("Enter workspace name..."), "Client Workspace");
|
|
await user.click(screen.getByRole("button", { name: "Create Workspace" }));
|
|
|
|
await waitFor(() => {
|
|
expect(createWorkspaceMock).toHaveBeenCalledWith({ name: "Client Workspace" });
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(fetchUserWorkspacesMock).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
expect(await screen.findByText("Your Workspaces (2)")).toBeInTheDocument();
|
|
});
|
|
});
|