import { render, screen, waitFor, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { CreateUserButton } from "./CreateUserButton"; import * as networking from "./networking"; import NotificationsManager from "./molecules/notifications_manager"; vi.mock("./networking", () => ({ userCreateCall: vi.fn(), modelAvailableCall: vi.fn().mockResolvedValue({ data: [] }), invitationCreateCall: vi.fn(), getProxyUISettings: vi.fn().mockResolvedValue({ PROXY_BASE_URL: null, PROXY_LOGOUT_URL: null, DEFAULT_TEAM_DISABLED: false, SSO_ENABLED: false, }), getProxyBaseUrl: vi.fn().mockReturnValue("http://localhost"), })); vi.mock("./bulk_create_users_button", () => ({ default: () =>
Bulk Create Users
, })); const mockUserCreateCall = vi.mocked(networking.userCreateCall); const mockInvitationCreateCall = vi.mocked(networking.invitationCreateCall); const mockGetProxyUISettings = vi.mocked(networking.getProxyUISettings); const mockNotificationsManager = vi.mocked(NotificationsManager); const createQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: 0 } }, }); const defaultProps = { userID: "123", accessToken: "token", teams: [], possibleUIRoles: null as Record> | null, }; function renderWithProviders(ui: React.ReactElement) { const qc = createQueryClient(); return render({ui}); } describe("CreateUserButton", () => { beforeEach(() => { vi.clearAllMocks(); mockGetProxyUISettings.mockResolvedValue({ PROXY_BASE_URL: null, PROXY_LOGOUT_URL: null, DEFAULT_TEAM_DISABLED: false, SSO_ENABLED: false, }); }); it("should render the create user form when embedded", () => { renderWithProviders( , ); expect(screen.getByRole("button", { name: /create user/i })).toBeInTheDocument(); }); it("should render the invite user button when not embedded", async () => { renderWithProviders(); await waitFor(() => { expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument(); }); }); it("should open the invite modal when invite user button is clicked", async () => { const user = userEvent.setup(); renderWithProviders(); await waitFor(() => { expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument(); }); await user.click(screen.getByRole("button", { name: /\+ invite user/i })); const dialog = screen.getByRole("dialog", { name: /invite user/i }); expect(dialog).toBeInTheDocument(); expect(within(dialog).getByRole("button", { name: /invite user/i })).toBeInTheDocument(); }); it("should display email invitations info message in embedded mode", () => { renderWithProviders(); expect(screen.getByText("Email invitations")).toBeInTheDocument(); }); it("should display user role options when possibleUIRoles is provided", async () => { const possibleUIRoles = { proxy_admin: { ui_label: "Admin", description: "Full access" }, proxy_user: { ui_label: "User", description: "Limited access" }, }; renderWithProviders( , ); await userEvent.click(screen.getByRole("combobox", { name: /user role/i })); expect(screen.getByText("Admin")).toBeInTheDocument(); expect(screen.getByText("User")).toBeInTheDocument(); }); it("should call userCreateCall when form is submitted in embedded mode", async () => { const user = userEvent.setup(); mockUserCreateCall.mockResolvedValue({ data: { user_id: "new-user-123" } }); mockInvitationCreateCall.mockResolvedValue({ id: "inv-1", user_id: "new-user-123", has_user_setup_sso: false, } as any); renderWithProviders( , ); await user.type(screen.getByLabelText(/user email/i), "test@example.com"); await user.click(screen.getByRole("combobox", { name: /user role/i })); await user.click(screen.getByText("User")); await user.click(screen.getByRole("button", { name: /create user/i })); await waitFor(() => { expect(mockUserCreateCall).toHaveBeenCalledWith("token", null, expect.objectContaining({ user_email: "test@example.com", user_role: "proxy_user", })); }); }); it("should call onUserCreated callback when user is created in embedded mode", async () => { const user = userEvent.setup(); const onUserCreated = vi.fn(); mockUserCreateCall.mockResolvedValue({ data: { user_id: "new-user-456" } }); renderWithProviders( , ); await user.type(screen.getByLabelText(/user email/i), "embedded@example.com"); await user.click(screen.getByRole("combobox", { name: /user role/i })); await user.click(screen.getByText("User")); await user.click(screen.getByRole("button", { name: /create user/i })); await waitFor(() => { expect(onUserCreated).toHaveBeenCalledWith("new-user-456"); }); }); it("should show success notification when user is created successfully in standalone mode", async () => { const user = userEvent.setup(); mockUserCreateCall.mockResolvedValue({ data: { user_id: "new-user-789" } }); mockInvitationCreateCall.mockResolvedValue({ id: "inv-2", user_id: "new-user-789", has_user_setup_sso: false, } as any); renderWithProviders( , ); await waitFor(() => { expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument(); }); await user.click(screen.getByRole("button", { name: /\+ invite user/i })); const dialog = screen.getByRole("dialog", { name: /invite user/i }); await user.type(within(dialog).getByLabelText(/user email/i), "standalone@example.com"); await user.click(within(dialog).getByRole("combobox", { name: /global proxy role/i })); await user.click(screen.getByText("User")); await user.click(within(dialog).getByRole("button", { name: /invite user/i })); await waitFor(() => { expect(mockNotificationsManager.success).toHaveBeenCalledWith("API user Created"); }); }); it("should show error notification when user creation fails", async () => { const user = userEvent.setup(); mockUserCreateCall.mockRejectedValue({ response: { data: { detail: "Email already exists" } } }); renderWithProviders( , ); await user.type(screen.getByLabelText(/user email/i), "duplicate@example.com"); await user.click(screen.getByRole("combobox", { name: /user role/i })); await user.click(screen.getByText("User")); await user.click(screen.getByRole("button", { name: /create user/i })); await waitFor(() => { expect(mockNotificationsManager.fromBackend).toHaveBeenCalledWith("Email already exists"); }); }); it("should show info notification when making API call", async () => { const user = userEvent.setup(); mockUserCreateCall.mockResolvedValue({ data: { user_id: "new-user" } }); mockInvitationCreateCall.mockResolvedValue({ id: "inv-3", user_id: "new-user", has_user_setup_sso: false, } as any); renderWithProviders( , ); await user.type(screen.getByLabelText(/user email/i), "info@example.com"); await user.click(screen.getByRole("combobox", { name: /user role/i })); await user.click(screen.getByText("User")); await user.click(screen.getByRole("button", { name: /create user/i })); await waitFor(() => { expect(mockNotificationsManager.info).toHaveBeenCalledWith("Making API Call"); }); }); it("should close modal when cancel is clicked in standalone mode", async () => { const user = userEvent.setup(); renderWithProviders(); await waitFor(() => { expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument(); }); await user.click(screen.getByRole("button", { name: /\+ invite user/i })); expect(screen.getByRole("dialog", { name: /invite user/i })).toBeInTheDocument(); const dialog = screen.getByRole("dialog", { name: /invite user/i }); await user.click(within(dialog).getByRole("button", { name: /close/i })); expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); }); it("should show onboarding modal when user is created and SSO is disabled", async () => { const user = userEvent.setup(); mockUserCreateCall.mockResolvedValue({ data: { user_id: "sso-user" } }); mockInvitationCreateCall.mockResolvedValue({ id: "inv-sso", user_id: "sso-user", has_user_setup_sso: false, } as any); renderWithProviders( , ); await waitFor(() => { expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument(); }); await user.click(screen.getByRole("button", { name: /\+ invite user/i })); const dialog = screen.getByRole("dialog", { name: /invite user/i }); await user.type(within(dialog).getByLabelText(/user email/i), "sso@example.com"); await user.click(within(dialog).getByRole("combobox", { name: /global proxy role/i })); await user.click(screen.getByText("User")); await user.click(within(dialog).getByRole("button", { name: /invite user/i })); await waitFor(() => { expect(mockInvitationCreateCall).toHaveBeenCalledWith("token", "sso-user"); }); await waitFor(() => { expect(mockNotificationsManager.success).toHaveBeenCalledWith("API user Created"); }); }); });