mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-11 17:05:43 +00:00
Remove modal in useful links
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||
import { getProxyBaseUrl, getPublicModelHubInfo, updateUsefulLinksCall } from "@/components/networking";
|
||||
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 "./UsefulLinksManagement";
|
||||
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||
import { getPublicModelHubInfo, updateUsefulLinksCall, getProxyBaseUrl } from "@/components/networking";
|
||||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
getPublicModelHubInfo: vi.fn(),
|
||||
@@ -25,8 +24,6 @@ const mockedUpdateUsefulLinksCall = vi.mocked(updateUsefulLinksCall);
|
||||
const mockedGetProxyBaseUrl = vi.mocked(getProxyBaseUrl);
|
||||
const mockedNotifications = vi.mocked(NotificationsManager);
|
||||
|
||||
let modalSuccessSpy: any;
|
||||
|
||||
describe("UsefulLinksManagement", () => {
|
||||
beforeEach(() => {
|
||||
mockedGetPublicModelHubInfo.mockResolvedValue({
|
||||
@@ -37,11 +34,9 @@ describe("UsefulLinksManagement", () => {
|
||||
});
|
||||
mockedUpdateUsefulLinksCall.mockResolvedValue({});
|
||||
mockedGetProxyBaseUrl.mockReturnValue("https://proxy.example.com");
|
||||
modalSuccessSpy = vi.spyOn(Modal, "success").mockImplementation(() => ({ destroy: vi.fn() }) as any);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
modalSuccessSpy.mockRestore();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -108,4 +103,153 @@ describe("UsefulLinksManagement", () => {
|
||||
|
||||
expect(mockedNotifications.success).toHaveBeenCalledWith("Link order saved successfully");
|
||||
});
|
||||
|
||||
it("should display the Model Hub link", async () => {
|
||||
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
|
||||
|
||||
expect(await screen.findByRole("link", { name: /public model hub/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should edit a link when edit button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockedGetPublicModelHubInfo.mockResolvedValue({
|
||||
docs_title: "Docs",
|
||||
custom_docs_description: null,
|
||||
litellm_version: "1.0.0",
|
||||
useful_links: {
|
||||
"Test Link": "https://test.example.com",
|
||||
},
|
||||
});
|
||||
|
||||
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Test Link")).toBeInTheDocument());
|
||||
|
||||
// Click edit button
|
||||
const editButton = screen.getByTestId("edit-link-0-Test Link");
|
||||
await user.click(editButton);
|
||||
|
||||
// Should show input fields in edit mode
|
||||
expect(screen.getByDisplayValue("Test Link")).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue("https://test.example.com")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should update a link when save is clicked in edit mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockedGetPublicModelHubInfo.mockResolvedValue({
|
||||
docs_title: "Docs",
|
||||
custom_docs_description: null,
|
||||
litellm_version: "1.0.0",
|
||||
useful_links: {
|
||||
"Test Link": "https://test.example.com",
|
||||
},
|
||||
});
|
||||
|
||||
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Test Link")).toBeInTheDocument());
|
||||
|
||||
// Click edit button
|
||||
const editButton = screen.getByTestId("edit-link-0-Test Link");
|
||||
await user.click(editButton);
|
||||
|
||||
// Update the display name
|
||||
const displayNameInput = screen.getByDisplayValue("Test Link");
|
||||
await user.clear(displayNameInput);
|
||||
await user.type(displayNameInput, "Updated Link");
|
||||
|
||||
// Click save
|
||||
await user.click(screen.getByRole("button", { name: /save/i }));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockedUpdateUsefulLinksCall).toHaveBeenCalledWith("token", {
|
||||
"Updated Link": { url: "https://test.example.com", index: 0 },
|
||||
}),
|
||||
);
|
||||
|
||||
expect(mockedNotifications.success).toHaveBeenCalledWith("Link updated successfully");
|
||||
});
|
||||
|
||||
it("should cancel editing when cancel button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockedGetPublicModelHubInfo.mockResolvedValue({
|
||||
docs_title: "Docs",
|
||||
custom_docs_description: null,
|
||||
litellm_version: "1.0.0",
|
||||
useful_links: {
|
||||
"Test Link": "https://test.example.com",
|
||||
},
|
||||
});
|
||||
|
||||
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Test Link")).toBeInTheDocument());
|
||||
|
||||
// Click edit button
|
||||
const editButton = screen.getByTestId("edit-link-0-Test Link");
|
||||
await user.click(editButton);
|
||||
|
||||
// Update the display name
|
||||
const displayNameInput = screen.getByDisplayValue("Test Link");
|
||||
await user.clear(displayNameInput);
|
||||
await user.type(displayNameInput, "Updated Link");
|
||||
|
||||
// Click cancel
|
||||
await user.click(screen.getByRole("button", { name: /cancel/i }));
|
||||
|
||||
// Should go back to normal view
|
||||
expect(screen.getByText("Test Link")).toBeInTheDocument();
|
||||
expect(screen.queryByDisplayValue("Updated Link")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not move down the last item in rearrange mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockedGetPublicModelHubInfo.mockResolvedValue({
|
||||
docs_title: "Docs",
|
||||
custom_docs_description: null,
|
||||
litellm_version: "1.0.0",
|
||||
useful_links: {
|
||||
"First Link": "https://first.example.com",
|
||||
"Second Link": "https://second.example.com",
|
||||
},
|
||||
});
|
||||
|
||||
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
|
||||
|
||||
await waitFor(() => expect(screen.getByText("First Link")).toBeInTheDocument());
|
||||
|
||||
// Enter rearrange mode
|
||||
await user.click(screen.getByRole("button", { name: /rearrange order/i }));
|
||||
|
||||
// Try to move down the last item (should not do anything)
|
||||
const secondLinkMoveDownButton = screen.getByTestId("move-down-1-Second Link");
|
||||
await user.click(secondLinkMoveDownButton);
|
||||
|
||||
// Links should remain in same order
|
||||
const linksAfter = screen.getAllByText(/First Link|Second Link/);
|
||||
expect(linksAfter[0]).toHaveTextContent("First Link");
|
||||
expect(linksAfter[1]).toHaveTextContent("Second Link");
|
||||
});
|
||||
|
||||
it("should expand and collapse the component", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Link Management")).toBeInTheDocument());
|
||||
|
||||
// Initially expanded
|
||||
expect(screen.getByText("Manage Existing Links")).toBeInTheDocument();
|
||||
|
||||
// Click to collapse
|
||||
await user.click(screen.getByText("Link Management"));
|
||||
|
||||
// Should be collapsed
|
||||
expect(screen.queryByText("Manage Existing Links")).not.toBeInTheDocument();
|
||||
|
||||
// Click to expand again
|
||||
await user.click(screen.getByText("Link Management"));
|
||||
|
||||
// Should be expanded
|
||||
expect(screen.getByText("Manage Existing Links")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Modal } from "antd";
|
||||
import { PlusCircleIcon, ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline";
|
||||
import { isAdminRole } from "@/utils/roles";
|
||||
import { getPublicModelHubInfo, updateUsefulLinksCall, getProxyBaseUrl } from "../networking";
|
||||
import { Card, Title, Text, Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react";
|
||||
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||
import TableIconActionButton from "@/components/common_components/IconActionButton/TableIconActionButtons/TableIconActionButton";
|
||||
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||
import { isAdminRole } from "@/utils/roles";
|
||||
import { ChevronDownIcon, ChevronRightIcon, ExternalLinkIcon, PlusCircleIcon } from "@heroicons/react/outline";
|
||||
import { Card, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text, Title } from "@tremor/react";
|
||||
import Link from "next/link";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { getProxyBaseUrl, getPublicModelHubInfo, updateUsefulLinksCall } from "../networking";
|
||||
|
||||
interface UsefulLinksManagementProps {
|
||||
accessToken: string | null;
|
||||
@@ -102,32 +102,6 @@ const UsefulLinksManagement: React.FC<UsefulLinksManagementProps> = ({ accessTok
|
||||
});
|
||||
|
||||
await updateUsefulLinksCall(accessToken, linksObject);
|
||||
// show success modal with public model hub link
|
||||
Modal.success({
|
||||
title: "Links Saved Successfully",
|
||||
content: (
|
||||
<div className="py-4">
|
||||
<p className="text-gray-600 mb-4">
|
||||
Your useful links have been saved and are now visible on the public model hub.
|
||||
</p>
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
||||
<p className="text-sm text-blue-800 mb-2 font-medium">View your updated model hub:</p>
|
||||
<a
|
||||
href={`${getProxyBaseUrl()}/ui/model_hub_table`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center text-blue-600 hover:text-blue-800 underline text-sm font-medium"
|
||||
>
|
||||
Open Public Model Hub →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
width: 500,
|
||||
okText: "Close",
|
||||
maskClosable: true,
|
||||
keyboard: true,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -319,29 +293,41 @@ const UsefulLinksManagement: React.FC<UsefulLinksManagementProps> = ({ accessTok
|
||||
</div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Text className="text-sm font-medium text-gray-700">Manage Existing Links</Text>
|
||||
{!isRearranging ? (
|
||||
<button
|
||||
onClick={handleStartRearranging}
|
||||
className="text-xs bg-purple-50 text-purple-600 px-3 py-1.5 rounded hover:bg-purple-100 flex items-center"
|
||||
<div className="flex items-center space-x-2">
|
||||
<Link
|
||||
href={`${getProxyBaseUrl()}/ui/model_hub_table`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs bg-blue-50 text-blue-600 px-3 py-1.5 rounded hover:bg-blue-100 flex items-center"
|
||||
title="Open Public Model Hub"
|
||||
>
|
||||
Rearrange Order
|
||||
</button>
|
||||
) : (
|
||||
<div className="flex space-x-2">
|
||||
Public Model Hub
|
||||
<ExternalLinkIcon className="w-4 h-4 ml-1" />
|
||||
</Link>
|
||||
{!isRearranging ? (
|
||||
<button
|
||||
onClick={handleSaveRearranging}
|
||||
className="text-xs bg-green-600 text-white px-3 py-1.5 rounded hover:bg-green-700"
|
||||
onClick={handleStartRearranging}
|
||||
className="text-xs bg-purple-50 text-purple-600 px-3 py-1.5 rounded hover:bg-purple-100 flex items-center"
|
||||
>
|
||||
Save Order
|
||||
Rearrange Order
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCancelRearranging}
|
||||
className="text-xs bg-gray-50 text-gray-600 px-3 py-1.5 rounded hover:bg-gray-100"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
) : (
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={handleSaveRearranging}
|
||||
className="text-xs bg-green-600 text-white px-3 py-1.5 rounded hover:bg-green-700"
|
||||
>
|
||||
Save Order
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCancelRearranging}
|
||||
className="text-xs bg-gray-50 text-gray-600 px-3 py-1.5 rounded hover:bg-gray-100"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-lg custom-border relative">
|
||||
<div className="overflow-x-auto">
|
||||
|
||||
Reference in New Issue
Block a user