import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, fireEvent } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { WorkspaceMemberRole } from "@mosaic/shared"; import { InviteMember } from "./InviteMember"; /** * Helper to get the invite form element from the rendered component. * The form wraps the submit button, so we locate it via the button. */ function getForm(): HTMLFormElement { const button = screen.getByRole("button", { name: /send invitation/i }); const form = button.closest("form"); if (!form) { throw new Error("Could not locate
element in InviteMember"); } return form; } describe("InviteMember", (): void => { const mockOnInvite = vi.fn<(email: string, role: WorkspaceMemberRole) => Promise>(); beforeEach((): void => { mockOnInvite.mockReset(); mockOnInvite.mockResolvedValue(undefined); vi.spyOn(window, "alert").mockImplementation((): undefined => undefined); }); it("should render the invite form", (): void => { render(); expect(screen.getByLabelText(/email address/i)).toBeInTheDocument(); expect(screen.getByLabelText(/role/i)).toBeInTheDocument(); expect(screen.getByRole("button", { name: /send invitation/i })).toBeInTheDocument(); }); it("should show error for empty email", async (): Promise => { render(); fireEvent.submit(getForm()); expect(await screen.findByText("Email is required")).toBeInTheDocument(); expect(mockOnInvite).not.toHaveBeenCalled(); }); it("should show error for invalid email without domain", async (): Promise => { render(); const emailInput = screen.getByLabelText(/email address/i); fireEvent.change(emailInput, { target: { value: "notanemail" } }); fireEvent.submit(getForm()); expect(await screen.findByText("Please enter a valid email address")).toBeInTheDocument(); expect(mockOnInvite).not.toHaveBeenCalled(); }); it("should show error for email with only @ sign", async (): Promise => { render(); const emailInput = screen.getByLabelText(/email address/i); fireEvent.change(emailInput, { target: { value: "user@" } }); fireEvent.submit(getForm()); expect(await screen.findByText("Please enter a valid email address")).toBeInTheDocument(); expect(mockOnInvite).not.toHaveBeenCalled(); }); it("should accept valid email and invoke onInvite", async (): Promise => { const user = userEvent.setup(); render(); await user.type(screen.getByLabelText(/email address/i), "valid@example.com"); await user.click(screen.getByRole("button", { name: /send invitation/i })); expect(mockOnInvite).toHaveBeenCalledWith("valid@example.com", WorkspaceMemberRole.MEMBER); }); it("should allow selecting a different role", async (): Promise => { const user = userEvent.setup(); render(); await user.type(screen.getByLabelText(/email address/i), "admin@example.com"); await user.selectOptions(screen.getByLabelText(/role/i), WorkspaceMemberRole.ADMIN); await user.click(screen.getByRole("button", { name: /send invitation/i })); expect(mockOnInvite).toHaveBeenCalledWith("admin@example.com", WorkspaceMemberRole.ADMIN); }); it("should show error message when onInvite rejects", async (): Promise => { mockOnInvite.mockRejectedValueOnce(new Error("Invite failed")); const user = userEvent.setup(); render(); await user.type(screen.getByLabelText(/email address/i), "user@example.com"); await user.click(screen.getByRole("button", { name: /send invitation/i })); expect(await screen.findByText("Invite failed")).toBeInTheDocument(); }); it("should reset form after successful invite", async (): Promise => { const user = userEvent.setup(); render(); const emailInput = screen.getByLabelText(/email address/i); await user.type(emailInput, "user@example.com"); await user.click(screen.getByRole("button", { name: /send invitation/i })); expect(emailInput).toHaveValue(""); }); });