diff --git a/ui/litellm-dashboard/src/components/useful_links_management.test.tsx b/ui/litellm-dashboard/src/components/useful_links_management.test.tsx new file mode 100644 index 0000000000..b70d536359 --- /dev/null +++ b/ui/litellm-dashboard/src/components/useful_links_management.test.tsx @@ -0,0 +1,74 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { Modal } from "antd"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import UsefulLinksManagement from "./useful_links_management"; +import NotificationsManager from "./molecules/notifications_manager"; +import { getPublicModelHubInfo, updateUsefulLinksCall, getProxyBaseUrl } from "./networking"; + +vi.mock("./networking", () => ({ + getPublicModelHubInfo: vi.fn(), + updateUsefulLinksCall: vi.fn(), + getProxyBaseUrl: vi.fn(), +})); + +vi.mock("./molecules/notifications_manager", () => ({ + __esModule: true, + default: { + success: vi.fn(), + fromBackend: vi.fn(), + }, +})); + +const mockedGetPublicModelHubInfo = vi.mocked(getPublicModelHubInfo); +const mockedUpdateUsefulLinksCall = vi.mocked(updateUsefulLinksCall); +const mockedGetProxyBaseUrl = vi.mocked(getProxyBaseUrl); +const mockedNotifications = vi.mocked(NotificationsManager); + +let modalSuccessSpy: any; + +describe("UsefulLinksManagement", () => { + beforeEach(() => { + mockedGetPublicModelHubInfo.mockResolvedValue({ + docs_title: "Docs", + custom_docs_description: null, + litellm_version: "1.0.0", + useful_links: {}, + }); + mockedUpdateUsefulLinksCall.mockResolvedValue({}); + mockedGetProxyBaseUrl.mockReturnValue("https://proxy.example.com"); + modalSuccessSpy = vi.spyOn(Modal, "success").mockImplementation(() => ({ destroy: vi.fn() }) as any); + }); + + afterEach(() => { + modalSuccessSpy.mockRestore(); + vi.clearAllMocks(); + }); + + it("should render link management for admin users", async () => { + render(); + + expect(await screen.findByText("Link Management")).toBeInTheDocument(); + await waitFor(() => expect(mockedGetPublicModelHubInfo).toHaveBeenCalled()); + }); + + it("should add a new link when fields are valid", async () => { + const user = userEvent.setup(); + render(); + + const displayNameInput = await screen.findByPlaceholderText("Friendly name"); + const urlInput = screen.getByPlaceholderText("https://example.com"); + + await user.type(displayNameInput, "Docs"); + await user.type(urlInput, "https://docs.example.com"); + await user.click(screen.getByRole("button", { name: /add link/i })); + + await waitFor(() => + expect(mockedUpdateUsefulLinksCall).toHaveBeenCalledWith("token", { Docs: "https://docs.example.com" }), + ); + + expect(await screen.findByText("Docs")).toBeInTheDocument(); + expect(screen.getByText("https://docs.example.com")).toBeInTheDocument(); + expect(mockedNotifications.success).toHaveBeenCalledWith("Link added successfully"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/useful_links_management.tsx b/ui/litellm-dashboard/src/components/useful_links_management.tsx index 83f1c80788..8ea1655b1e 100644 --- a/ui/litellm-dashboard/src/components/useful_links_management.tsx +++ b/ui/litellm-dashboard/src/components/useful_links_management.tsx @@ -210,21 +210,6 @@ const UsefulLinksManagement: React.FC = ({ accessTok Add New Link - - URL - - setNewLink({ - ...newLink, - url: e.target.value, - }) - } - placeholder="https://example.com" - className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm" - /> - Display Name = ({ accessTok className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm" /> + + URL + + setNewLink({ + ...newLink, + url: e.target.value, + }) + } + placeholder="https://example.com" + className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm" + /> +