diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx
index e1b3b358300..3c5101fc2dc 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx
@@ -131,7 +131,7 @@ describe("ModelsAndEndpointsView", () => {
,
);
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", () => {
,
);
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();
diff --git a/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx b/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx
index 9a4659da9d3..03d982ca7c2 100644
--- a/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx
+++ b/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx
@@ -51,7 +51,7 @@ function renderWithProviders(ui: React.ReactElement) {
return render({ui});
}
-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(
- ,
- );
- expect(screen.getByRole("button", { name: /create user/i })).toBeInTheDocument();
- });
+ describe("rendering and visibility", () => {
+ 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 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 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 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();
+ 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(
+ ,
+ );
+
+ 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();
- 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(
- ,
- );
- await userEvent.click(screen.getByRole("combobox", { name: /user role/i }));
- expect(screen.getByText("Admin")).toBeInTheDocument();
- expect(screen.getByText("User")).toBeInTheDocument();
- });
+ renderWithProviders(
+ ,
+ );
- 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(
- ,
- );
+ 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(
+ ,
+ );
+
+ 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 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(
- ,
- );
+ 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(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(
+ ,
+ );
+
+ 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(
- ,
- );
+ 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(
+ ,
+ );
+
+ 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(
+ ,
+ );
+
+ 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(
- ,
- );
-
- 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");
- });
- });
-
- 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(
- ,
- );
-
- 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(
- ,
- );
-
- 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();
- });
});
diff --git a/ui/litellm-dashboard/src/components/OldTeams.test.tsx b/ui/litellm-dashboard/src/components/OldTeams.test.tsx
index 651d1495c61..4b89820bad6 100644
--- a/ui/litellm-dashboard/src/components/OldTeams.test.tsx
+++ b/ui/litellm-dashboard/src/components/OldTeams.test.tsx
@@ -843,7 +843,7 @@ describe("OldTeams - access_group_ids in team create", () => {
}),
);
});
- }, { timeout: 30000 });
+ });
});
describe("OldTeams - models dropdown options", () => {
diff --git a/ui/litellm-dashboard/src/components/add_model/add_model_tab.test.tsx b/ui/litellm-dashboard/src/components/add_model/add_model_tab.test.tsx
index 197bcd6569f..0a8d2d9124e 100644
--- a/ui/litellm-dashboard/src/components/add_model/add_model_tab.test.tsx
+++ b/ui/litellm-dashboard/src/components/add_model/add_model_tab.test.tsx
@@ -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();
diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx
index c92956b430f..b4251267137 100644
--- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx
+++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx
@@ -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", () => {
diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx
index fb149458f61..f24b4e77e16 100644
--- a/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx
+++ b/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx
@@ -222,634 +222,640 @@ describe("TeamInfoView", () => {
vi.clearAllMocks();
});
- it("should render", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+ describe("display and rendering", () => {
+ it("should render", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
- renderWithProviders();
+ renderWithProviders();
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
- });
-
- it("should display loading state while fetching team data", () => {
- vi.mocked(networking.teamInfoCall).mockImplementation(() => new Promise(() => { }));
-
- renderWithProviders();
-
- expect(screen.getByText("Loading...")).toBeInTheDocument();
- });
-
- it("should display error message when team is not found", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue({
- team_id: "123",
- team_info: null as any,
- keys: [],
- team_memberships: [],
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
});
- renderWithProviders();
+ it("should display loading state while fetching team data", () => {
+ vi.mocked(networking.teamInfoCall).mockImplementation(() => new Promise(() => {}));
- await waitFor(() => {
- expect(screen.getByText("Team not found")).toBeInTheDocument();
- });
- });
+ renderWithProviders();
- it("should display budget information in overview", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- max_budget: 1000,
- spend: 250.5,
- budget_duration: "30d",
- })
- );
-
- renderWithProviders();
-
- await waitFor(() => {
- expect(screen.getByText("Budget Status")).toBeInTheDocument();
- });
- });
-
- it("should display guardrails in overview when present", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- guardrails: ["guardrail1", "guardrail2"],
- })
- );
-
- renderWithProviders();
-
- await waitFor(() => {
- expect(screen.getByText("Guardrails")).toBeInTheDocument();
- });
- });
-
- it("should display policies in overview when present", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- policies: ["policy1"],
- })
- );
- vi.mocked(networking.getPolicyInfoWithGuardrails).mockResolvedValue({
- resolved_guardrails: ["guardrail1"],
+ expect(screen.getByText("Loading...")).toBeInTheDocument();
});
- renderWithProviders();
+ it("should display error message when team is not found", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue({
+ team_id: "123",
+ team_info: null as any,
+ keys: [],
+ team_memberships: [],
+ });
- await waitFor(() => {
- expect(screen.getByText("Policies")).toBeInTheDocument();
- });
- });
+ renderWithProviders();
- it("should show members tab when user can edit team", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- expect(screen.getByRole("tab", { name: "Members" })).toBeInTheDocument();
- });
- });
-
- it("should not show members tab when user cannot edit team", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
+ await waitFor(() => {
+ expect(screen.getByText("Team not found")).toBeInTheDocument();
+ });
});
- expect(screen.queryByRole("tab", { name: "Members" })).not.toBeInTheDocument();
- });
-
- it("should show settings tab when user can edit team", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- expect(screen.getByRole("tab", { name: "Settings" })).toBeInTheDocument();
- });
- });
-
- it("should navigate to settings tab when clicked", async () => {
- const user = userEvent.setup({ delay: null });
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const settingsTab = screen.getByRole("tab", { name: "Settings" });
- await user.click(settingsTab);
-
- await waitFor(() => {
- expect(screen.getByText("Team Settings")).toBeInTheDocument();
- });
- });
-
- it("should open edit mode when edit button is clicked", async () => {
- const user = userEvent.setup({ delay: null });
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const settingsTab = screen.getByRole("tab", { name: "Settings" });
- await user.click(settingsTab);
-
- await waitFor(() => {
- expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
- });
-
- const editButton = screen.getByRole("button", { name: /edit settings/i });
- await user.click(editButton);
-
- await waitFor(() => {
- expect(screen.getByLabelText("Team Name")).toBeInTheDocument();
- });
- });
-
- it("should close edit mode when cancel button is clicked", { timeout: 15000 }, async () => {
- const user = userEvent.setup({ delay: null });
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const settingsTab = screen.getByRole("tab", { name: "Settings" });
- await user.click(settingsTab);
-
- await waitFor(() => {
- expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
- });
-
- const editButton = screen.getByRole("button", { name: /edit settings/i });
- await user.click(editButton);
-
- await waitFor(() => {
- expect(screen.getByLabelText("Team Name")).toBeInTheDocument();
- });
-
- const cancelButton = screen.getByRole("button", { name: /cancel/i });
- await user.click(cancelButton);
-
- await waitFor(() => {
- expect(screen.queryByLabelText("Team Name")).not.toBeInTheDocument();
- });
- });
-
- it("should call onClose when back button is clicked", async () => {
- const user = userEvent.setup({ delay: null });
- const onClose = vi.fn();
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const backButton = screen.getByRole("button", { name: /back to teams/i });
- await user.click(backButton);
-
- expect(onClose).toHaveBeenCalled();
- });
-
- it("should copy team ID to clipboard when copy button is clicked", async () => {
- const user = userEvent.setup({ delay: null });
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const copyButtons = screen.getAllByRole("button");
- const copyButton = copyButtons.find((btn) => btn.querySelector("svg"));
- expect(copyButton).toBeTruthy();
-
- if (copyButton) {
- await user.click(copyButton);
- }
- });
-
- it("should disable secret manager settings for non-premium users", async () => {
- const user = userEvent.setup({ delay: null });
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- metadata: {
- secret_manager_settings: { provider: "aws", secret_id: "abc" },
- },
- })
- );
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const settingsTab = screen.getByRole("tab", { name: "Settings" });
- await user.click(settingsTab);
-
- await waitFor(() => {
- expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
- });
-
- const editButton = screen.getByRole("button", { name: /edit settings/i });
- await user.click(editButton);
-
- const secretField = await screen.findByPlaceholderText(
- '{"namespace": "admin", "mount": "secret", "path_prefix": "litellm"}'
- );
- expect(secretField).toBeDisabled();
- });
-
- it("should allow premium users to edit secret manager settings", async () => {
- const user = userEvent.setup({ delay: null });
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- metadata: {
- secret_manager_settings: { provider: "aws", secret_id: "abc" },
- },
- })
- );
- vi.mocked(networking.teamUpdateCall).mockResolvedValue({ data: {}, team_id: "123" } as any);
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const settingsTab = screen.getByRole("tab", { name: "Settings" });
- await user.click(settingsTab);
-
- await waitFor(() => {
- expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
- });
-
- const editButton = screen.getByRole("button", { name: /edit settings/i });
- await user.click(editButton);
-
- const secretField = await screen.findByPlaceholderText(
- '{"namespace": "admin", "mount": "secret", "path_prefix": "litellm"}'
- );
- expect(secretField).not.toBeDisabled();
- });
-
- it("should add team member when form is submitted", async () => {
- const user = userEvent.setup({ delay: null });
- const onUpdate = vi.fn();
- const teamData = createMockTeamData();
- vi.mocked(networking.teamInfoCall).mockResolvedValue(teamData);
- vi.mocked(networking.teamMemberAddCall).mockResolvedValue({} as any);
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const membersTab = screen.getByRole("tab", { name: "Members" });
- await user.click(membersTab);
-
- await waitFor(() => {
- expect(screen.getByRole("button", { name: /add member/i })).toBeInTheDocument();
- });
-
- const addButton = screen.getByRole("button", { name: /add member/i });
- await user.click(addButton);
-
- await waitFor(() => {
- expect(screen.getByRole("button", { name: "Submit" })).toBeInTheDocument();
- });
-
- const submitButton = screen.getByRole("button", { name: "Submit" });
- await user.click(submitButton);
-
- await waitFor(() => {
- expect(networking.teamMemberAddCall).toHaveBeenCalled();
- });
- });
-
- it("should display team member budget information when present", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- team_member_budget_table: {
- max_budget: 500,
+ it("should display budget information in overview", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ max_budget: 1000,
+ spend: 250.5,
budget_duration: "30d",
- tpm_limit: 5000,
- rpm_limit: 50,
- },
- })
- );
-
- renderWithProviders();
-
- await waitFor(() => {
- expect(screen.getByText("Budget Status")).toBeInTheDocument();
- });
- });
-
- it("should display virtual keys information", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue({
- ...createMockTeamData(),
- keys: [
- { user_id: "user1", token: "key1" },
- { token: "key2" },
- ],
- });
-
- renderWithProviders();
-
- await waitFor(() => {
- expect(screen.getByRole("tab", { name: "Virtual Keys" })).toBeInTheDocument();
- });
- });
-
- it("should show Virtual Keys tab when user cannot edit team", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- expect(screen.getByRole("tab", { name: "Virtual Keys" })).toBeInTheDocument();
- });
- });
-
- it("should display X Members in Virtual Keys tab when navigated to", async () => {
- const user = userEvent.setup();
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
- const fiveKeys = Array.from({ length: 5 }, (_, i) => ({
- token: `sk-${i}`,
- token_id: `key-${i}`,
- key_alias: `key_${i}`,
- key_name: `sk-...${i}`,
- user_id: `user-${i}`,
- organization_id: null,
- user: { user_id: `user-${i}`, user_email: `user${i}@test.com` },
- created_at: "2024-01-01T00:00:00Z",
- team_id: "123",
- spend: 0,
- max_budget: 100,
- models: ["gpt-4"],
- }));
- mockUseKeys.mockReturnValue({
- data: { keys: fiveKeys, total_count: 5, current_page: 1, total_pages: 1 },
- isPending: false,
- isFetching: false,
- refetch: vi.fn(),
- } as any);
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const virtualKeysTab = screen.getByRole("tab", { name: "Virtual Keys" });
- await user.click(virtualKeysTab);
-
- await waitFor(() => {
- expect(screen.getByText("Page 1 of 1")).toBeInTheDocument();
- });
- });
-
- it("should show Filters and pagination controls in Virtual Keys tab", async () => {
- const user = userEvent.setup();
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
- mockUseKeys.mockReturnValue({
- data: {
- keys: [
- {
- token: "sk-1",
- token_id: "key-1",
- key_alias: "key1",
- key_name: "sk-...1",
- user_id: "user-1",
- organization_id: null,
- user: { user_id: "user-1", user_email: "user1@test.com" },
- created_at: "2024-01-01T00:00:00Z",
- team_id: "123",
- spend: 0,
- max_budget: 100,
- models: ["gpt-4"],
- },
- ],
- total_count: 1,
- current_page: 1,
- total_pages: 1,
- },
- isPending: false,
- isFetching: false,
- refetch: vi.fn(),
- } as any);
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const virtualKeysTab = screen.getByRole("tab", { name: "Virtual Keys" });
- await user.click(virtualKeysTab);
-
- await waitFor(() => {
- expect(screen.getByRole("button", { name: "Filters" })).toBeInTheDocument();
- });
- expect(screen.getByRole("button", { name: "Reset Filters" })).toBeInTheDocument();
- expect(screen.getByText("Page 1 of 1")).toBeInTheDocument();
- expect(screen.getByRole("button", { name: "Previous" })).toBeInTheDocument();
- expect(screen.getByRole("button", { name: "Next" })).toBeInTheDocument();
- });
-
- it("should display object permissions when present", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- object_permission: {
- object_permission_id: "perm-1",
- mcp_servers: ["server1"],
- vector_stores: ["store1"],
- },
- })
- );
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
- });
-
- it("should display soft budget in settings view when present", async () => {
- const user = userEvent.setup({ delay: null });
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- soft_budget: 500.75,
- max_budget: 1000,
- })
- );
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const settingsTab = screen.getByRole("tab", { name: "Settings" });
- await user.click(settingsTab);
-
- await waitFor(() => {
- expect(screen.getByText("Team Settings")).toBeInTheDocument();
- });
-
- await waitFor(() => {
- expect(screen.getByText(/Soft Budget:/)).toBeInTheDocument();
- expect(screen.getByText(/\$500\.75/)).toBeInTheDocument();
- });
- });
-
- it("should open Settings tab by default when editTeam is true and user can edit", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- expect(screen.getByText("Team Settings")).toBeInTheDocument();
- });
-
- it("should open Overview tab by default when editTeam is false", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- expect(screen.getByText("Budget Status")).toBeInTheDocument();
- });
-
- it("should open Overview tab by default when editTeam is true but user cannot edit", async () => {
- vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
-
- renderWithProviders(
-
- );
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- expect(screen.getByText("Budget Status")).toBeInTheDocument();
- });
-
- it("should display soft budget alerting emails in settings view when present", async () => {
- const user = userEvent.setup({ delay: null });
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- metadata: {
- soft_budget_alerting_emails: ["alert1@test.com", "alert2@test.com"],
- },
- })
- );
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const settingsTab = screen.getByRole("tab", { name: "Settings" });
- await user.click(settingsTab);
-
- await waitFor(() => {
- expect(screen.getByText("Team Settings")).toBeInTheDocument();
- });
-
- await waitFor(() => {
- expect(screen.getByText(/Soft Budget Alerting Emails:/)).toBeInTheDocument();
- expect(screen.getByText(/alert1@test\.com, alert2@test\.com/)).toBeInTheDocument();
- });
- });
-
- it("should pass access_group_ids to teamUpdateCall when saving team settings", async () => {
- const user = userEvent.setup({ delay: null });
- const accessGroupIds = ["ag-1", "ag-2"];
- vi.mocked(networking.teamInfoCall).mockResolvedValue(
- createMockTeamData({
- access_group_ids: accessGroupIds,
- models: ["gpt-4"],
- })
- );
- vi.mocked(networking.teamUpdateCall).mockResolvedValue({ data: {}, team_id: "123" } as any);
-
- renderWithProviders();
-
- await waitFor(() => {
- const teamNameElements = screen.queryAllByText("Test Team");
- expect(teamNameElements.length).toBeGreaterThan(0);
- });
-
- const settingsTab = screen.getByRole("tab", { name: "Settings" });
- await user.click(settingsTab);
-
- await waitFor(() => {
- expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
- });
-
- const editButton = screen.getByRole("button", { name: /edit settings/i });
- await user.click(editButton);
-
- await waitFor(() => {
- expect(screen.getByLabelText("Team Name")).toBeInTheDocument();
- });
-
- const saveButton = screen.getByRole("button", { name: /save changes/i });
- await user.click(saveButton);
-
- await waitFor(() => {
- expect(networking.teamUpdateCall).toHaveBeenCalledWith(
- "test-token",
- expect.objectContaining({
- access_group_ids: accessGroupIds,
- team_id: "123",
})
);
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(screen.getByText("Budget Status")).toBeInTheDocument();
+ });
+ });
+
+ it("should display guardrails in overview when present", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ guardrails: ["guardrail1", "guardrail2"],
+ })
+ );
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(screen.getByText("Guardrails")).toBeInTheDocument();
+ });
+ });
+
+ it("should display policies in overview when present", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ policies: ["policy1"],
+ })
+ );
+ vi.mocked(networking.getPolicyInfoWithGuardrails).mockResolvedValue({
+ resolved_guardrails: ["guardrail1"],
+ });
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(screen.getByText("Policies")).toBeInTheDocument();
+ });
+ });
+
+ it("should display team member budget information when present", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ team_member_budget_table: {
+ max_budget: 500,
+ budget_duration: "30d",
+ tpm_limit: 5000,
+ rpm_limit: 50,
+ },
+ })
+ );
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(screen.getByText("Budget Status")).toBeInTheDocument();
+ });
+ });
+
+ it("should display virtual keys information", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue({
+ ...createMockTeamData(),
+ keys: [
+ { user_id: "user1", token: "key1" },
+ { token: "key2" },
+ ],
+ });
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(screen.getByRole("tab", { name: "Virtual Keys" })).toBeInTheDocument();
+ });
+ });
+
+ it("should display object permissions when present", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ object_permission: {
+ object_permission_id: "perm-1",
+ mcp_servers: ["server1"],
+ vector_stores: ["store1"],
+ },
+ })
+ );
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+ });
+
+ it("should open Settings tab by default when editTeam is true and user can edit", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ expect(screen.getByText("Team Settings")).toBeInTheDocument();
+ });
+
+ it("should open Overview tab by default when editTeam is false", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ expect(screen.getByText("Budget Status")).toBeInTheDocument();
+ });
+
+ it("should open Overview tab by default when editTeam is true but user cannot edit", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders(
+
+ );
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ expect(screen.getByText("Budget Status")).toBeInTheDocument();
+ });
+ });
+
+ describe("tabs and navigation", () => {
+ it("should show members tab when user can edit team", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(screen.getByRole("tab", { name: "Members" })).toBeInTheDocument();
+ });
+ });
+
+ it("should not show members tab when user cannot edit team", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ expect(screen.queryByRole("tab", { name: "Members" })).not.toBeInTheDocument();
+ });
+
+ it("should show settings tab when user can edit team", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(screen.getByRole("tab", { name: "Settings" })).toBeInTheDocument();
+ });
+ });
+
+ it("should navigate to settings tab when clicked", async () => {
+ const user = userEvent.setup({ delay: null });
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const settingsTab = screen.getByRole("tab", { name: "Settings" });
+ await user.click(settingsTab);
+
+ await waitFor(() => {
+ expect(screen.getByText("Team Settings")).toBeInTheDocument();
+ });
+ });
+
+ it("should call onClose when back button is clicked", async () => {
+ const user = userEvent.setup({ delay: null });
+ const onClose = vi.fn();
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const backButton = screen.getByRole("button", { name: /back to teams/i });
+ await user.click(backButton);
+
+ expect(onClose).toHaveBeenCalled();
+ });
+
+ it("should copy team ID to clipboard when copy button is clicked", async () => {
+ const user = userEvent.setup({ delay: null });
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const copyButtons = screen.getAllByRole("button");
+ const copyButton = copyButtons.find((btn) => btn.querySelector("svg"));
+ expect(copyButton).toBeTruthy();
+
+ if (copyButton) {
+ await user.click(copyButton);
+ }
+ });
+
+ it("should show Virtual Keys tab when user cannot edit team", async () => {
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(screen.getByRole("tab", { name: "Virtual Keys" })).toBeInTheDocument();
+ });
+ });
+
+ it("should display X Members in Virtual Keys tab when navigated to", async () => {
+ const user = userEvent.setup();
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+ const fiveKeys = Array.from({ length: 5 }, (_, i) => ({
+ token: `sk-${i}`,
+ token_id: `key-${i}`,
+ key_alias: `key_${i}`,
+ key_name: `sk-...${i}`,
+ user_id: `user-${i}`,
+ organization_id: null,
+ user: { user_id: `user-${i}`, user_email: `user${i}@test.com` },
+ created_at: "2024-01-01T00:00:00Z",
+ team_id: "123",
+ spend: 0,
+ max_budget: 100,
+ models: ["gpt-4"],
+ }));
+ mockUseKeys.mockReturnValue({
+ data: { keys: fiveKeys, total_count: 5, current_page: 1, total_pages: 1 },
+ isPending: false,
+ isFetching: false,
+ refetch: vi.fn(),
+ } as any);
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const virtualKeysTab = screen.getByRole("tab", { name: "Virtual Keys" });
+ await user.click(virtualKeysTab);
+
+ await waitFor(() => {
+ expect(screen.getByText("Page 1 of 1")).toBeInTheDocument();
+ });
+ });
+
+ it("should show Filters and pagination controls in Virtual Keys tab", async () => {
+ const user = userEvent.setup();
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+ mockUseKeys.mockReturnValue({
+ data: {
+ keys: [
+ {
+ token: "sk-1",
+ token_id: "key-1",
+ key_alias: "key1",
+ key_name: "sk-...1",
+ user_id: "user-1",
+ organization_id: null,
+ user: { user_id: "user-1", user_email: "user1@test.com" },
+ created_at: "2024-01-01T00:00:00Z",
+ team_id: "123",
+ spend: 0,
+ max_budget: 100,
+ models: ["gpt-4"],
+ },
+ ],
+ total_count: 1,
+ current_page: 1,
+ total_pages: 1,
+ },
+ isPending: false,
+ isFetching: false,
+ refetch: vi.fn(),
+ } as any);
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const virtualKeysTab = screen.getByRole("tab", { name: "Virtual Keys" });
+ await user.click(virtualKeysTab);
+
+ await waitFor(() => {
+ expect(screen.getByRole("button", { name: "Filters" })).toBeInTheDocument();
+ });
+ expect(screen.getByRole("button", { name: "Reset Filters" })).toBeInTheDocument();
+ expect(screen.getByText("Page 1 of 1")).toBeInTheDocument();
+ expect(screen.getByRole("button", { name: "Previous" })).toBeInTheDocument();
+ expect(screen.getByRole("button", { name: "Next" })).toBeInTheDocument();
+ });
+ });
+
+ describe("settings and editing", () => {
+ it("should open edit mode when edit button is clicked", async () => {
+ const user = userEvent.setup({ delay: null });
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const settingsTab = screen.getByRole("tab", { name: "Settings" });
+ await user.click(settingsTab);
+
+ await waitFor(() => {
+ expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
+ });
+
+ const editButton = screen.getByRole("button", { name: /edit settings/i });
+ await user.click(editButton);
+
+ await waitFor(() => {
+ expect(screen.getByLabelText("Team Name")).toBeInTheDocument();
+ });
+ });
+
+ it("should close edit mode when cancel button is clicked", async () => {
+ const user = userEvent.setup({ delay: null });
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(createMockTeamData());
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const settingsTab = screen.getByRole("tab", { name: "Settings" });
+ await user.click(settingsTab);
+
+ await waitFor(() => {
+ expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
+ });
+
+ const editButton = screen.getByRole("button", { name: /edit settings/i });
+ await user.click(editButton);
+
+ await waitFor(() => {
+ expect(screen.getByLabelText("Team Name")).toBeInTheDocument();
+ });
+
+ const cancelButton = screen.getByRole("button", { name: /cancel/i });
+ await user.click(cancelButton);
+
+ await waitFor(() => {
+ expect(screen.queryByLabelText("Team Name")).not.toBeInTheDocument();
+ });
+ });
+
+ it("should disable secret manager settings for non-premium users", async () => {
+ const user = userEvent.setup({ delay: null });
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ metadata: {
+ secret_manager_settings: { provider: "aws", secret_id: "abc" },
+ },
+ })
+ );
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const settingsTab = screen.getByRole("tab", { name: "Settings" });
+ await user.click(settingsTab);
+
+ await waitFor(() => {
+ expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
+ });
+
+ const editButton = screen.getByRole("button", { name: /edit settings/i });
+ await user.click(editButton);
+
+ const secretField = await screen.findByPlaceholderText(
+ '{"namespace": "admin", "mount": "secret", "path_prefix": "litellm"}'
+ );
+ expect(secretField).toBeDisabled();
+ });
+
+ it("should allow premium users to edit secret manager settings", async () => {
+ const user = userEvent.setup({ delay: null });
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ metadata: {
+ secret_manager_settings: { provider: "aws", secret_id: "abc" },
+ },
+ })
+ );
+ vi.mocked(networking.teamUpdateCall).mockResolvedValue({ data: {}, team_id: "123" } as any);
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const settingsTab = screen.getByRole("tab", { name: "Settings" });
+ await user.click(settingsTab);
+
+ await waitFor(() => {
+ expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
+ });
+
+ const editButton = screen.getByRole("button", { name: /edit settings/i });
+ await user.click(editButton);
+
+ const secretField = await screen.findByPlaceholderText(
+ '{"namespace": "admin", "mount": "secret", "path_prefix": "litellm"}'
+ );
+ expect(secretField).not.toBeDisabled();
+ });
+
+ it("should add team member when form is submitted", async () => {
+ const user = userEvent.setup({ delay: null });
+ const onUpdate = vi.fn();
+ const teamData = createMockTeamData();
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(teamData);
+ vi.mocked(networking.teamMemberAddCall).mockResolvedValue({} as any);
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const membersTab = screen.getByRole("tab", { name: "Members" });
+ await user.click(membersTab);
+
+ await waitFor(() => {
+ expect(screen.getByRole("button", { name: /add member/i })).toBeInTheDocument();
+ });
+
+ const addButton = screen.getByRole("button", { name: /add member/i });
+ await user.click(addButton);
+
+ await waitFor(() => {
+ expect(screen.getByRole("button", { name: "Submit" })).toBeInTheDocument();
+ });
+
+ const submitButton = screen.getByRole("button", { name: "Submit" });
+ await user.click(submitButton);
+
+ await waitFor(() => {
+ expect(networking.teamMemberAddCall).toHaveBeenCalled();
+ });
+ });
+
+ it("should display soft budget in settings view when present", async () => {
+ const user = userEvent.setup({ delay: null });
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ soft_budget: 500.75,
+ max_budget: 1000,
+ })
+ );
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const settingsTab = screen.getByRole("tab", { name: "Settings" });
+ await user.click(settingsTab);
+
+ await waitFor(() => {
+ expect(screen.getByText("Team Settings")).toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(screen.getByText(/Soft Budget:/)).toBeInTheDocument();
+ expect(screen.getByText(/\$500\.75/)).toBeInTheDocument();
+ });
+ });
+
+ it("should display soft budget alerting emails in settings view when present", async () => {
+ const user = userEvent.setup({ delay: null });
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ metadata: {
+ soft_budget_alerting_emails: ["alert1@test.com", "alert2@test.com"],
+ },
+ })
+ );
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const settingsTab = screen.getByRole("tab", { name: "Settings" });
+ await user.click(settingsTab);
+
+ await waitFor(() => {
+ expect(screen.getByText("Team Settings")).toBeInTheDocument();
+ });
+
+ await waitFor(() => {
+ expect(screen.getByText(/Soft Budget Alerting Emails:/)).toBeInTheDocument();
+ expect(screen.getByText(/alert1@test\.com, alert2@test\.com/)).toBeInTheDocument();
+ });
+ });
+
+ it("should pass access_group_ids to teamUpdateCall when saving team settings", async () => {
+ const user = userEvent.setup({ delay: null });
+ const accessGroupIds = ["ag-1", "ag-2"];
+ vi.mocked(networking.teamInfoCall).mockResolvedValue(
+ createMockTeamData({
+ access_group_ids: accessGroupIds,
+ models: ["gpt-4"],
+ })
+ );
+ vi.mocked(networking.teamUpdateCall).mockResolvedValue({ data: {}, team_id: "123" } as any);
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ const teamNameElements = screen.queryAllByText("Test Team");
+ expect(teamNameElements.length).toBeGreaterThan(0);
+ });
+
+ const settingsTab = screen.getByRole("tab", { name: "Settings" });
+ await user.click(settingsTab);
+
+ await waitFor(() => {
+ expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument();
+ });
+
+ const editButton = screen.getByRole("button", { name: /edit settings/i });
+ await user.click(editButton);
+
+ await waitFor(() => {
+ expect(screen.getByLabelText("Team Name")).toBeInTheDocument();
+ });
+
+ const saveButton = screen.getByRole("button", { name: /save changes/i });
+ await user.click(saveButton);
+
+ await waitFor(() => {
+ expect(networking.teamUpdateCall).toHaveBeenCalledWith(
+ "test-token",
+ expect.objectContaining({
+ access_group_ids: accessGroupIds,
+ team_id: "123",
+ })
+ );
+ });
});
});
});
diff --git a/ui/litellm-dashboard/vitest.config.ts b/ui/litellm-dashboard/vitest.config.ts
index 7c52b88d3b6..d2b6e7b43bf 100644
--- a/ui/litellm-dashboard/vitest.config.ts
+++ b/ui/litellm-dashboard/vitest.config.ts
@@ -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"],