mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
Merge pull request #25468 from BerriAI/litellm_/compassionate-shannon
[Test] UI - Unit tests: raise global vitest timeout and remove per-test overrides
This commit is contained in:
commit
9e6d2d2069
7 changed files with 1030 additions and 1032 deletions
|
|
@ -131,7 +131,7 @@ describe("ModelsAndEndpointsView", () => {
|
|||
</QueryClientProvider>,
|
||||
);
|
||||
expect(await findByText("Model Management", {}, { timeout: 10000 })).toBeInTheDocument();
|
||||
}, 15000);
|
||||
});
|
||||
|
||||
it("should show Missing provider banner by default", async () => {
|
||||
localStorageMock.clear();
|
||||
|
|
@ -149,7 +149,7 @@ describe("ModelsAndEndpointsView", () => {
|
|||
</QueryClientProvider>,
|
||||
);
|
||||
expect(await findByText("Missing a provider?", {}, { timeout: 10000 })).toBeInTheDocument();
|
||||
}, 15000);
|
||||
});
|
||||
|
||||
it("should hide Missing provider banner when dismiss button is clicked and persist to localStorage", async () => {
|
||||
localStorageMock.clear();
|
||||
|
|
@ -180,7 +180,7 @@ describe("ModelsAndEndpointsView", () => {
|
|||
|
||||
// LocalStorage should be updated
|
||||
expect(localStorageMock.getItem("hideMissingProviderBanner")).toBe("true");
|
||||
}, 15000);
|
||||
});
|
||||
|
||||
it("should show compact Request Provider button when banner is dismissed", async () => {
|
||||
// Set localStorage to hide banner
|
||||
|
|
@ -209,7 +209,7 @@ describe("ModelsAndEndpointsView", () => {
|
|||
const requestProviderLinks = document.querySelectorAll('a[href="https://models.litellm.ai/?request=true"]');
|
||||
// There should be a compact button when banner is hidden
|
||||
expect(requestProviderLinks.length).toBeGreaterThan(0);
|
||||
}, 15000);
|
||||
});
|
||||
|
||||
it("should pass model IDs (not model names) to HealthCheckComponent as all_models_on_proxy", async () => {
|
||||
mockHealthCheckComponent.mockClear();
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ function renderWithProviders(ui: React.ReactElement) {
|
|||
return render(<QueryClientProvider client={qc}>{ui}</QueryClientProvider>);
|
||||
}
|
||||
|
||||
describe("CreateUserButton", { timeout: 20000 }, () => {
|
||||
describe("CreateUserButton", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockGetProxyUISettings.mockResolvedValue({
|
||||
|
|
@ -62,288 +62,296 @@ describe("CreateUserButton", { timeout: 20000 }, () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("should render the create user form when embedded", () => {
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} isEmbedded />,
|
||||
);
|
||||
expect(screen.getByRole("button", { name: /create user/i })).toBeInTheDocument();
|
||||
});
|
||||
describe("rendering and visibility", () => {
|
||||
it("should render the create user form when embedded", () => {
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} isEmbedded />,
|
||||
);
|
||||
expect(screen.getByRole("button", { name: /create user/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the invite user button when not embedded", async () => {
|
||||
renderWithProviders(<CreateUserButton {...defaultProps} />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument();
|
||||
it("should render the invite user button when not embedded", async () => {
|
||||
renderWithProviders(<CreateUserButton {...defaultProps} />);
|
||||
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(<CreateUserButton {...defaultProps} />);
|
||||
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(<CreateUserButton {...defaultProps} isEmbedded />);
|
||||
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(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={possibleUIRoles} isEmbedded />,
|
||||
);
|
||||
await userEvent.click(screen.getByRole("combobox", { name: /user role/i }));
|
||||
expect(screen.getByText("Admin")).toBeInTheDocument();
|
||||
expect(screen.getByText("User")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should close modal when cancel is clicked in standalone mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<CreateUserButton {...defaultProps} />);
|
||||
|
||||
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 open the invite modal when invite user button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<CreateUserButton {...defaultProps} />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument();
|
||||
describe("embedded mode submission", () => {
|
||||
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(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
|
||||
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",
|
||||
}));
|
||||
});
|
||||
});
|
||||
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(<CreateUserButton {...defaultProps} isEmbedded />);
|
||||
expect(screen.getByText("Email invitations")).toBeInTheDocument();
|
||||
});
|
||||
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" } });
|
||||
|
||||
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(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={possibleUIRoles} isEmbedded />,
|
||||
);
|
||||
await userEvent.click(screen.getByRole("combobox", { name: /user role/i }));
|
||||
expect(screen.getByText("Admin")).toBeInTheDocument();
|
||||
expect(screen.getByText("User")).toBeInTheDocument();
|
||||
});
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} onUserCreated={onUserCreated} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
|
||||
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);
|
||||
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 }));
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(onUserCreated).toHaveBeenCalledWith("new-user-456");
|
||||
});
|
||||
});
|
||||
|
||||
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 }));
|
||||
it("should show error notification when user creation fails", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockRejectedValue({ response: { data: { detail: "Email already exists" } } });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUserCreateCall).toHaveBeenCalledWith("token", null, expect.objectContaining({
|
||||
user_email: "test@example.com",
|
||||
user_role: "proxy_user",
|
||||
}));
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
|
||||
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(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
|
||||
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 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" } });
|
||||
describe("standalone mode submission", () => {
|
||||
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(
|
||||
<CreateUserButton {...defaultProps} onUserCreated={onUserCreated} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
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(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument();
|
||||
});
|
||||
await user.click(screen.getByRole("button", { name: /\+ invite user/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onUserCreated).toHaveBeenCalledWith("new-user-456");
|
||||
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 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(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
describe("organizations", () => {
|
||||
it("should send organizations list in POST body when organizations are selected", async () => {
|
||||
const { useOrganizations } = await import("@/app/(dashboard)/hooks/organizations/useOrganizations");
|
||||
vi.mocked(useOrganizations).mockReturnValue({
|
||||
data: [{ organization_id: "org-1", organization_alias: "My Org" }],
|
||||
isLoading: false,
|
||||
} as any);
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockResolvedValue({ data: { user_id: "org-user" } });
|
||||
mockInvitationCreateCall.mockResolvedValue({
|
||||
id: "inv-org",
|
||||
user_id: "org-user",
|
||||
has_user_setup_sso: false,
|
||||
} as any);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument();
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
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), "org@example.com");
|
||||
await user.click(within(dialog).getByRole("combobox", { name: /global proxy role/i }));
|
||||
await user.click(screen.getByText("User"));
|
||||
|
||||
// Select org from the dropdown
|
||||
const orgSelect = within(dialog).getByRole("combobox", { name: /organization/i });
|
||||
await user.click(orgSelect);
|
||||
await user.click(screen.getByText("My Org (org-1)"));
|
||||
|
||||
await user.click(within(dialog).getByRole("button", { name: /invite user/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUserCreateCall).toHaveBeenCalledWith("token", null, expect.objectContaining({
|
||||
organizations: ["org-1"],
|
||||
}));
|
||||
});
|
||||
});
|
||||
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 }));
|
||||
it("should not call organizationMemberAddCall after user creation", async () => {
|
||||
const { useOrganizations } = await import("@/app/(dashboard)/hooks/organizations/useOrganizations");
|
||||
vi.mocked(useOrganizations).mockReturnValue({
|
||||
data: [{ organization_id: "org-1", organization_alias: "My Org" }],
|
||||
isLoading: false,
|
||||
} as any);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockNotificationsManager.success).toHaveBeenCalledWith("API user Created");
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockResolvedValue({ data: { user_id: "no-member-add-user" } });
|
||||
mockInvitationCreateCall.mockResolvedValue({
|
||||
id: "inv-nma",
|
||||
user_id: "no-member-add-user",
|
||||
has_user_setup_sso: false,
|
||||
} as any);
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
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), "nomemberadd@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(mockUserCreateCall).toHaveBeenCalled();
|
||||
});
|
||||
expect(mockOrganizationMemberAddCall).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should show error notification when user creation fails", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockRejectedValue({ response: { data: { detail: "Email already exists" } } });
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
|
||||
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(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
|
||||
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(<CreateUserButton {...defaultProps} />);
|
||||
|
||||
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(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
||||
it("should send organizations list in POST body when organizations are selected", async () => {
|
||||
const { useOrganizations } = await import("@/app/(dashboard)/hooks/organizations/useOrganizations");
|
||||
vi.mocked(useOrganizations).mockReturnValue({
|
||||
data: [{ organization_id: "org-1", organization_alias: "My Org" }],
|
||||
isLoading: false,
|
||||
} as any);
|
||||
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockResolvedValue({ data: { user_id: "org-user" } });
|
||||
mockInvitationCreateCall.mockResolvedValue({
|
||||
id: "inv-org",
|
||||
user_id: "org-user",
|
||||
has_user_setup_sso: false,
|
||||
} as any);
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
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), "org@example.com");
|
||||
await user.click(within(dialog).getByRole("combobox", { name: /global proxy role/i }));
|
||||
await user.click(screen.getByText("User"));
|
||||
|
||||
// Select org from the dropdown
|
||||
const orgSelect = within(dialog).getByRole("combobox", { name: /organization/i });
|
||||
await user.click(orgSelect);
|
||||
await user.click(screen.getByText("My Org (org-1)"));
|
||||
|
||||
await user.click(within(dialog).getByRole("button", { name: /invite user/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUserCreateCall).toHaveBeenCalledWith("token", null, expect.objectContaining({
|
||||
organizations: ["org-1"],
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it("should not call organizationMemberAddCall after user creation", async () => {
|
||||
const { useOrganizations } = await import("@/app/(dashboard)/hooks/organizations/useOrganizations");
|
||||
vi.mocked(useOrganizations).mockReturnValue({
|
||||
data: [{ organization_id: "org-1", organization_alias: "My Org" }],
|
||||
isLoading: false,
|
||||
} as any);
|
||||
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockResolvedValue({ data: { user_id: "no-member-add-user" } });
|
||||
mockInvitationCreateCall.mockResolvedValue({
|
||||
id: "inv-nma",
|
||||
user_id: "no-member-add-user",
|
||||
has_user_setup_sso: false,
|
||||
} as any);
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
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), "nomemberadd@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(mockUserCreateCall).toHaveBeenCalled();
|
||||
});
|
||||
expect(mockOrganizationMemberAddCall).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -843,7 +843,7 @@ describe("OldTeams - access_group_ids in team create", () => {
|
|||
}),
|
||||
);
|
||||
});
|
||||
}, { timeout: 30000 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("OldTeams - models dropdown options", () => {
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ describe("Add Model Tab", () => {
|
|||
);
|
||||
|
||||
expect(await screen.findByRole("tab", { name: "Add Model" })).toBeInTheDocument();
|
||||
}, 10000); // This test is flaky, adding a timeout until we find a better solution
|
||||
});
|
||||
|
||||
it("should display both Add Model and Add Auto Router tabs", async () => {
|
||||
const props = createTestProps();
|
||||
|
|
@ -269,7 +269,7 @@ describe("Add Model Tab", () => {
|
|||
},
|
||||
{ timeout: 10000 },
|
||||
);
|
||||
}, 15000); // 15 second timeout to allow waitFor to complete
|
||||
});
|
||||
|
||||
it("should show team selection when team-only switch is enabled", async () => {
|
||||
const props = createTestProps();
|
||||
|
|
|
|||
|
|
@ -150,151 +150,139 @@ describe("CreateMCPServer", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it(
|
||||
"should not require auth value when creating a server with API Key auth type",
|
||||
{ timeout: 15000 },
|
||||
async () => {
|
||||
await selectHttpTransport();
|
||||
it("should not require auth value when creating a server with API Key auth type", async () => {
|
||||
await selectHttpTransport();
|
||||
|
||||
const user = userEvent.setup({ delay: null });
|
||||
const user = userEvent.setup({ delay: null });
|
||||
|
||||
// Fill in server name (use id to avoid duplicate placeholder)
|
||||
const nameInput = getServerNameInput();
|
||||
await user.type(nameInput, "Test_Server");
|
||||
// Fill in server name (use id to avoid duplicate placeholder)
|
||||
const nameInput = getServerNameInput();
|
||||
await user.type(nameInput, "Test_Server");
|
||||
|
||||
// Fill in URL
|
||||
const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com");
|
||||
await user.type(urlInput, "https://example.com/mcp");
|
||||
// Fill in URL
|
||||
const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com");
|
||||
await user.type(urlInput, "https://example.com/mcp");
|
||||
|
||||
// Select API Key auth type
|
||||
await selectAntOption("Authentication", "API Key");
|
||||
// Select API Key auth type
|
||||
await selectAntOption("Authentication", "API Key");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Authentication Value")).toBeInTheDocument();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Authentication Value")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Leave auth value empty and submit
|
||||
vi.mocked(networking.createMCPServer).mockResolvedValue({
|
||||
server_id: "new-server-1",
|
||||
server_name: "Test_Server",
|
||||
alias: "Test_Server",
|
||||
url: "https://example.com/mcp",
|
||||
transport: "http",
|
||||
auth_type: "api_key",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user-1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user-1",
|
||||
});
|
||||
// Leave auth value empty and submit
|
||||
vi.mocked(networking.createMCPServer).mockResolvedValue({
|
||||
server_id: "new-server-1",
|
||||
server_name: "Test_Server",
|
||||
alias: "Test_Server",
|
||||
url: "https://example.com/mcp",
|
||||
transport: "http",
|
||||
auth_type: "api_key",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user-1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user-1",
|
||||
});
|
||||
|
||||
const submitButton = screen.getByRole("button", { name: "Add MCP Server" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
const submitButton = screen.getByRole("button", { name: "Add MCP Server" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
// The form should submit without validation error on auth_value
|
||||
await waitFor(() => {
|
||||
expect(networking.createMCPServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
);
|
||||
// The form should submit without validation error on auth_value
|
||||
await waitFor(() => {
|
||||
expect(networking.createMCPServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it(
|
||||
"should not require auth value when creating a server with Bearer Token auth type",
|
||||
{ timeout: 15000 },
|
||||
async () => {
|
||||
await selectHttpTransport();
|
||||
it("should not require auth value when creating a server with Bearer Token auth type", async () => {
|
||||
await selectHttpTransport();
|
||||
|
||||
const user = userEvent.setup({ delay: null });
|
||||
const user = userEvent.setup({ delay: null });
|
||||
|
||||
const nameInput = getServerNameInput();
|
||||
await user.type(nameInput, "Test_Server");
|
||||
const nameInput = getServerNameInput();
|
||||
await user.type(nameInput, "Test_Server");
|
||||
|
||||
const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com");
|
||||
await user.type(urlInput, "https://example.com/mcp");
|
||||
const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com");
|
||||
await user.type(urlInput, "https://example.com/mcp");
|
||||
|
||||
await selectAntOption("Authentication", "Bearer Token");
|
||||
await selectAntOption("Authentication", "Bearer Token");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Authentication Value")).toBeInTheDocument();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Authentication Value")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Leave auth value empty and submit
|
||||
vi.mocked(networking.createMCPServer).mockResolvedValue({
|
||||
server_id: "new-server-1",
|
||||
server_name: "Test_Server",
|
||||
alias: "Test_Server",
|
||||
url: "https://example.com/mcp",
|
||||
transport: "http",
|
||||
auth_type: "bearer_token",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user-1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user-1",
|
||||
});
|
||||
// Leave auth value empty and submit
|
||||
vi.mocked(networking.createMCPServer).mockResolvedValue({
|
||||
server_id: "new-server-1",
|
||||
server_name: "Test_Server",
|
||||
alias: "Test_Server",
|
||||
url: "https://example.com/mcp",
|
||||
transport: "http",
|
||||
auth_type: "bearer_token",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user-1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user-1",
|
||||
});
|
||||
|
||||
const submitButton = screen.getByRole("button", { name: "Add MCP Server" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
const submitButton = screen.getByRole("button", { name: "Add MCP Server" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(networking.createMCPServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(networking.createMCPServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it(
|
||||
"should successfully create a server when auth value is provided",
|
||||
{ timeout: 15000 },
|
||||
async () => {
|
||||
await selectHttpTransport();
|
||||
it("should successfully create a server when auth value is provided", async () => {
|
||||
await selectHttpTransport();
|
||||
|
||||
const user = userEvent.setup({ delay: null });
|
||||
const user = userEvent.setup({ delay: null });
|
||||
|
||||
const nameInput = getServerNameInput();
|
||||
await user.type(nameInput, "My_Server");
|
||||
const nameInput = getServerNameInput();
|
||||
await user.type(nameInput, "My_Server");
|
||||
|
||||
const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com");
|
||||
await user.type(urlInput, "https://example.com/mcp");
|
||||
const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com");
|
||||
await user.type(urlInput, "https://example.com/mcp");
|
||||
|
||||
await selectAntOption("Authentication", "API Key");
|
||||
await selectAntOption("Authentication", "API Key");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Authentication Value")).toBeInTheDocument();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Authentication Value")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Fill in auth value
|
||||
const authInput = screen.getByPlaceholderText("Enter token or secret");
|
||||
await user.type(authInput, "my-secret-key");
|
||||
// Fill in auth value
|
||||
const authInput = screen.getByPlaceholderText("Enter token or secret");
|
||||
await user.type(authInput, "my-secret-key");
|
||||
|
||||
vi.mocked(networking.createMCPServer).mockResolvedValue({
|
||||
server_id: "new-server-1",
|
||||
server_name: "My_Server",
|
||||
alias: "My_Server",
|
||||
url: "https://example.com/mcp",
|
||||
transport: "http",
|
||||
auth_type: "api_key",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user-1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user-1",
|
||||
});
|
||||
vi.mocked(networking.createMCPServer).mockResolvedValue({
|
||||
server_id: "new-server-1",
|
||||
server_name: "My_Server",
|
||||
alias: "My_Server",
|
||||
url: "https://example.com/mcp",
|
||||
transport: "http",
|
||||
auth_type: "api_key",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user-1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user-1",
|
||||
});
|
||||
|
||||
const submitButton = screen.getByRole("button", { name: "Add MCP Server" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
const submitButton = screen.getByRole("button", { name: "Add MCP Server" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(networking.createMCPServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(networking.createMCPServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const [token, payload] = vi.mocked(networking.createMCPServer).mock.calls[0];
|
||||
expect(token).toBe("test-token");
|
||||
expect(payload.credentials).toEqual({ auth_value: "my-secret-key" });
|
||||
},
|
||||
);
|
||||
const [token, payload] = vi.mocked(networking.createMCPServer).mock.calls[0];
|
||||
expect(token).toBe("test-token");
|
||||
expect(payload.credentials).toEqual({ auth_value: "my-secret-key" });
|
||||
});
|
||||
|
||||
it("should not show auth value field when None auth type is selected", async () => {
|
||||
await selectHttpTransport();
|
||||
|
|
@ -307,50 +295,46 @@ describe("CreateMCPServer", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it(
|
||||
"should successfully create a server with no auth",
|
||||
{ timeout: 15000 },
|
||||
async () => {
|
||||
await selectHttpTransport();
|
||||
it("should successfully create a server with no auth", async () => {
|
||||
await selectHttpTransport();
|
||||
|
||||
const user = userEvent.setup({ delay: null });
|
||||
const user = userEvent.setup({ delay: null });
|
||||
|
||||
const nameInput = getServerNameInput();
|
||||
await user.type(nameInput, "No_Auth_Server");
|
||||
const nameInput = getServerNameInput();
|
||||
await user.type(nameInput, "No_Auth_Server");
|
||||
|
||||
const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com");
|
||||
await user.type(urlInput, "https://example.com/mcp");
|
||||
const urlInput = screen.getByPlaceholderText("https://your-mcp-server.com");
|
||||
await user.type(urlInput, "https://example.com/mcp");
|
||||
|
||||
await selectAntOption("Authentication", "None");
|
||||
await selectAntOption("Authentication", "None");
|
||||
|
||||
vi.mocked(networking.createMCPServer).mockResolvedValue({
|
||||
server_id: "new-server-1",
|
||||
server_name: "No_Auth_Server",
|
||||
alias: "No_Auth_Server",
|
||||
url: "https://example.com/mcp",
|
||||
transport: "http",
|
||||
auth_type: "none",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user-1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user-1",
|
||||
});
|
||||
vi.mocked(networking.createMCPServer).mockResolvedValue({
|
||||
server_id: "new-server-1",
|
||||
server_name: "No_Auth_Server",
|
||||
alias: "No_Auth_Server",
|
||||
url: "https://example.com/mcp",
|
||||
transport: "http",
|
||||
auth_type: "none",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user-1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user-1",
|
||||
});
|
||||
|
||||
const submitButton = screen.getByRole("button", { name: "Add MCP Server" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
const submitButton = screen.getByRole("button", { name: "Add MCP Server" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(networking.createMCPServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(networking.createMCPServer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const [, payload] = vi.mocked(networking.createMCPServer).mock.calls[0];
|
||||
expect(payload.auth_type).toBe("none");
|
||||
// No credentials should be sent for "none" auth
|
||||
expect(payload.credentials).toBeUndefined();
|
||||
},
|
||||
);
|
||||
const [, payload] = vi.mocked(networking.createMCPServer).mock.calls[0];
|
||||
expect(payload.auth_type).toBe("none");
|
||||
// No credentials should be sent for "none" auth
|
||||
expect(payload.credentials).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when OAuth interactive auth is selected", () => {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,7 +7,7 @@ export default defineConfig({
|
|||
setupFiles: ["tests/setupTests.ts"],
|
||||
globals: true,
|
||||
css: true, // lets you import CSS/modules without extra mocks
|
||||
testTimeout: 10000,
|
||||
testTimeout: 30000,
|
||||
coverage: {
|
||||
provider: "v8",
|
||||
reporter: ["text", "lcov"],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue