Files
stack/apps/web/src/components/workspace/MemberList.test.tsx
Jason Woltje ffc10c9a45
All checks were successful
ci/woodpecker/push/orchestrator Pipeline was successful
ci/woodpecker/push/web Pipeline was successful
ci/woodpecker/push/api Pipeline was successful
feat(api): add MS21 user fields for admin, local auth, and invitations (#553)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-02-28 17:47:03 +00:00

122 lines
4.3 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { WorkspaceMemberRole } from "@mosaic/shared";
import { MemberList } from "./MemberList";
import type { WorkspaceMemberWithUser } from "./MemberList";
const makeMember = (
overrides: Partial<WorkspaceMemberWithUser> & { userId: string }
): WorkspaceMemberWithUser => ({
workspaceId: overrides.workspaceId ?? "ws-1",
userId: overrides.userId,
role: overrides.role ?? WorkspaceMemberRole.MEMBER,
joinedAt: overrides.joinedAt ?? new Date("2025-01-01"),
user: overrides.user ?? {
id: overrides.userId,
name: `User ${overrides.userId}`,
email: `${overrides.userId}@example.com`,
emailVerified: true,
image: null,
authProviderId: `auth-${overrides.userId}`,
preferences: {},
deactivatedAt: null,
isLocalAuth: false,
passwordHash: null,
invitedBy: null,
invitationToken: null,
invitedAt: null,
createdAt: new Date("2025-01-01"),
updatedAt: new Date("2025-01-01"),
},
});
describe("MemberList", (): void => {
const mockOnRoleChange = vi.fn<(userId: string, newRole: WorkspaceMemberRole) => Promise<void>>();
const mockOnRemove = vi.fn<(userId: string) => Promise<void>>();
const defaultProps = {
currentUserId: "user-1",
currentUserRole: WorkspaceMemberRole.ADMIN,
workspaceOwnerId: "owner-1",
onRoleChange: mockOnRoleChange,
onRemove: mockOnRemove,
};
beforeEach((): void => {
mockOnRoleChange.mockReset();
mockOnRoleChange.mockResolvedValue(undefined);
mockOnRemove.mockReset();
mockOnRemove.mockResolvedValue(undefined);
});
it("should render member list with correct count", (): void => {
const members = [makeMember({ userId: "user-1" }), makeMember({ userId: "user-2" })];
render(<MemberList {...defaultProps} members={members} />);
expect(screen.getByText("Members (2)")).toBeInTheDocument();
});
it("should display member name and email", (): void => {
const members = [
makeMember({
userId: "user-2",
user: {
id: "user-2",
name: "Jane Doe",
email: "jane@example.com",
emailVerified: true,
image: null,
authProviderId: "auth-2",
preferences: {},
deactivatedAt: null,
isLocalAuth: false,
passwordHash: null,
invitedBy: null,
invitationToken: null,
invitedAt: null,
createdAt: new Date("2025-01-01"),
updatedAt: new Date("2025-01-01"),
},
}),
];
render(<MemberList {...defaultProps} members={members} />);
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
expect(screen.getByText("jane@example.com")).toBeInTheDocument();
});
it("should show (you) indicator for current user", (): void => {
const members = [makeMember({ userId: "user-1" })];
render(<MemberList {...defaultProps} members={members} />);
expect(screen.getByText("(you)")).toBeInTheDocument();
});
it("should call onRoleChange with validated role when admin changes a member role", async (): Promise<void> => {
const user = userEvent.setup();
const members = [
makeMember({ userId: "user-1" }),
makeMember({ userId: "user-2", role: WorkspaceMemberRole.MEMBER }),
];
render(<MemberList {...defaultProps} members={members} />);
const roleSelect = screen.getByDisplayValue("Member");
await user.selectOptions(roleSelect, WorkspaceMemberRole.GUEST);
expect(mockOnRoleChange).toHaveBeenCalledWith("user-2", WorkspaceMemberRole.GUEST);
});
it("should not show role select for the workspace owner", (): void => {
const members = [
makeMember({ userId: "owner-1", role: WorkspaceMemberRole.OWNER }),
makeMember({ userId: "user-1", role: WorkspaceMemberRole.ADMIN }),
];
render(<MemberList {...defaultProps} members={members} />);
expect(screen.getByText("OWNER")).toBeInTheDocument();
});
it("should not show remove button for the workspace owner", (): void => {
const members = [makeMember({ userId: "owner-1", role: WorkspaceMemberRole.OWNER })];
render(<MemberList {...defaultProps} members={members} />);
expect(screen.queryByLabelText("Remove member")).not.toBeInTheDocument();
});
});