mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
refactor pt3
This commit is contained in:
parent
e0a07eda92
commit
a0a8f0b8b4
14 changed files with 1651 additions and 33 deletions
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import ModelHubTable from "@/components/AIHub/model_hub_table";
|
||||
import ModelHubTable from "@/components/AIHub/ModelHubTable";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const ModelHubPage = () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import ModelHubTable from "@/components/AIHub/model_hub_table";
|
||||
import ModelHubTable from "@/components/AIHub/ModelHubTable";
|
||||
|
||||
export default function PublicModelHubTable() {
|
||||
const searchParams = useSearchParams()!;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import GeneralSettings from "@/components/general_settings";
|
|||
import GuardrailsPanel from "@/components/guardrails";
|
||||
import { Team } from "@/components/key_team_helpers/key_list";
|
||||
import { MCPServers } from "@/components/mcp_tools";
|
||||
import ModelHubTable from "@/components/AIHub/model_hub_table";
|
||||
import ModelHubTable from "@/components/AIHub/ModelHubTable";
|
||||
import Navbar from "@/components/navbar";
|
||||
import { getUiConfig, Organization, proxyBaseUrl, setGlobalLitellmHeaderName } from "@/components/networking";
|
||||
import NewUsagePage from "@/components/UsagePage/components/UsagePageView";
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export interface AgentHubData {
|
|||
[key: string]: any;
|
||||
}
|
||||
|
||||
export const agentHubColumns = (
|
||||
export const getAgentHubTableColumns = (
|
||||
showModal: (agent: AgentHubData) => void,
|
||||
copyToClipboard: (text: string) => void,
|
||||
publicPage: boolean = false,
|
||||
|
|
@ -69,11 +69,7 @@ export const agentHubColumns = (
|
|||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
|
||||
return (
|
||||
<Text className="text-xs line-clamp-2">
|
||||
{agent.description || "-"}
|
||||
</Text>
|
||||
);
|
||||
return <Text className="text-xs line-clamp-2">{agent.description || "-"}</Text>;
|
||||
},
|
||||
meta: {
|
||||
className: "hidden md:table-cell",
|
||||
|
|
@ -105,11 +101,7 @@ export const agentHubColumns = (
|
|||
cell: ({ row }) => {
|
||||
const agent = row.original;
|
||||
|
||||
return (
|
||||
<Text className="text-xs">
|
||||
{agent.protocolVersion || "-"}
|
||||
</Text>
|
||||
);
|
||||
return <Text className="text-xs">{agent.protocolVersion || "-"}</Text>;
|
||||
},
|
||||
meta: {
|
||||
className: "hidden lg:table-cell",
|
||||
|
|
@ -135,9 +127,7 @@ export const agentHubColumns = (
|
|||
{skill.name}
|
||||
</Tag>
|
||||
))}
|
||||
{skills.length > 2 && (
|
||||
<Text className="text-xs text-gray-500">+{skills.length - 2}</Text>
|
||||
)}
|
||||
{skills.length > 2 && <Text className="text-xs text-gray-500">+{skills.length - 2}</Text>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -240,4 +230,3 @@ export const agentHubColumns = (
|
|||
|
||||
return allColumns;
|
||||
};
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import * as networking from "@/components/networking";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import ModelHubTable from "./model_hub_table";
|
||||
import ModelHubTable from "./ModelHubTable";
|
||||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
getUiConfig: vi.fn(),
|
||||
|
|
@ -19,7 +19,7 @@ vi.mock("next/navigation", () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
vi.mock("./public_model_hub", () => ({
|
||||
vi.mock("@/components/public_model_hub", () => ({
|
||||
default: () => <div>Public Model Hub</div>,
|
||||
}));
|
||||
|
||||
|
|
@ -51,7 +51,12 @@ describe("ModelHubTable", () => {
|
|||
const getUiConfigMock = vi.mocked(networking.getUiConfig);
|
||||
const modelHubPublicModelsCallMock = vi.mocked(networking.modelHubPublicModelsCall);
|
||||
|
||||
getUiConfigMock.mockResolvedValue({ server_root_path: "/", proxy_base_url: "http://localhost:4000" });
|
||||
getUiConfigMock.mockResolvedValue({
|
||||
server_root_path: "/",
|
||||
proxy_base_url: "http://localhost:4000",
|
||||
auto_redirect_to_sso: false,
|
||||
admin_ui_disabled: false,
|
||||
});
|
||||
modelHubPublicModelsCallMock.mockResolvedValue([]);
|
||||
|
||||
render(<ModelHubTable accessToken={null} publicPage={true} premiumUser={false} userRole={null} />);
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import { agentHubColumns, AgentHubData } from "@/components/AIHub/agent_hub_table_columns";
|
||||
import MakeAgentPublicForm from "@/components/AIHub/forms/make_agent_public_form";
|
||||
import MakeMCPPublicForm from "@/components/AIHub/forms/make_mcp_public_form";
|
||||
import MakeModelPublicForm from "@/components/AIHub/forms/make_model_public_form";
|
||||
import { AgentHubData, getAgentHubTableColumns } from "@/components/AIHub/AgentHubTableColumns";
|
||||
import MakeAgentPublicForm from "@/components/AIHub/forms/MakeAgentPublicForm";
|
||||
import MakeMCPPublicForm from "@/components/AIHub/forms/MakeMCPPublicForm";
|
||||
import MakeModelPublicForm from "@/components/AIHub/forms/MakeModelPublicForm";
|
||||
import { mcpHubColumns, MCPServerData } from "@/components/mcp_hub_table_columns";
|
||||
import { modelHubColumns } from "@/components/model_hub_table_columns";
|
||||
import UsefulLinksManagement from "@/components/AIHub/useful_links_management";
|
||||
import UsefulLinksManagement from "@/components/AIHub/UsefulLinksManagement";
|
||||
import { ModelDataTable } from "@/components/model_dashboard/table";
|
||||
import ModelFilters from "@/components/model_filters";
|
||||
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||
|
|
@ -423,7 +423,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
|
||||
{/* Agent Table */}
|
||||
<ModelDataTable
|
||||
columns={agentHubColumns(showAgentModal, copyToClipboard, publicPage)}
|
||||
columns={getAgentHubTableColumns(showAgentModal, copyToClipboard, publicPage)}
|
||||
data={agentHubData || []}
|
||||
isLoading={agentLoading}
|
||||
defaultSorting={[{ id: "name", desc: false }]}
|
||||
|
|
@ -2,17 +2,17 @@ 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";
|
||||
import UsefulLinksManagement from "./UsefulLinksManagement";
|
||||
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||
import { getPublicModelHubInfo, updateUsefulLinksCall, getProxyBaseUrl } from "@/components/networking";
|
||||
|
||||
vi.mock("./networking", () => ({
|
||||
vi.mock("@/components/networking", () => ({
|
||||
getPublicModelHubInfo: vi.fn(),
|
||||
updateUsefulLinksCall: vi.fn(),
|
||||
getProxyBaseUrl: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./molecules/notifications_manager", () => ({
|
||||
vi.mock("@/components/molecules/notifications_manager", () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
success: vi.fn(),
|
||||
|
|
@ -0,0 +1,505 @@
|
|||
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import MakeAgentPublicForm from "./MakeAgentPublicForm";
|
||||
import { AgentHubData } from "@/components/AIHub/AgentHubTableColumns";
|
||||
|
||||
// Mock the networking function
|
||||
vi.mock("../../networking", () => ({
|
||||
makeAgentsPublicCall: vi.fn(),
|
||||
}));
|
||||
|
||||
// Import the mocked function
|
||||
import { makeAgentsPublicCall } from "../../networking";
|
||||
const mockMakeAgentsPublicCall = vi.mocked(makeAgentsPublicCall);
|
||||
|
||||
// Mock antd components
|
||||
vi.mock("antd", () => ({
|
||||
Modal: ({ open, title, children, onCancel, footer }: any) =>
|
||||
open ? (
|
||||
<div data-testid="modal">
|
||||
<div>{title}</div>
|
||||
{children}
|
||||
{footer}
|
||||
</div>
|
||||
) : null,
|
||||
Form: Object.assign(({ children, form }: any) => <form data-testid="form">{children}</form>, {
|
||||
useForm: () => [
|
||||
{
|
||||
resetFields: vi.fn(),
|
||||
validateFields: vi.fn(),
|
||||
getFieldsValue: vi.fn(),
|
||||
setFieldsValue: vi.fn(),
|
||||
},
|
||||
vi.fn(),
|
||||
],
|
||||
Item: ({ children }: any) => <div>{children}</div>,
|
||||
}),
|
||||
Steps: Object.assign(
|
||||
({ children, current, className }: any) => (
|
||||
<div data-testid="steps" className={className}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
{
|
||||
Step: ({ title }: any) => <div>{title}</div>,
|
||||
},
|
||||
),
|
||||
Button: ({ children, onClick, disabled, loading, ...props }: any) => (
|
||||
<button onClick={onClick} disabled={disabled || loading} data-loading={loading} {...props}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
Checkbox: ({ checked, indeterminate, onChange, children, disabled }: any) => (
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={(e) => onChange({ target: { checked: e.target.checked } })}
|
||||
disabled={disabled}
|
||||
data-indeterminate={indeterminate}
|
||||
/>
|
||||
{children}
|
||||
</label>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock @tremor/react components
|
||||
vi.mock("@tremor/react", () => ({
|
||||
Text: ({ children, className }: any) => <span className={className}>{children}</span>,
|
||||
Title: ({ children }: any) => <h3>{children}</h3>,
|
||||
Badge: ({ children, color, size }: any) => (
|
||||
<span data-color={color} data-size={size}>
|
||||
{children}
|
||||
</span>
|
||||
),
|
||||
}));
|
||||
|
||||
describe("MakeAgentPublicForm", () => {
|
||||
const mockProps = {
|
||||
visible: true,
|
||||
onClose: vi.fn(),
|
||||
accessToken: "test-token",
|
||||
agentHubData: [
|
||||
{
|
||||
agent_id: "agent-1",
|
||||
name: "Test Agent 1",
|
||||
description: "Description 1",
|
||||
version: "1.0",
|
||||
is_public: false,
|
||||
skills: [
|
||||
{ id: "skill-1", name: "Skill 1", description: "Skill desc" },
|
||||
{ id: "skill-2", name: "Skill 2", description: "Skill desc" },
|
||||
],
|
||||
protocolVersion: "1.0",
|
||||
},
|
||||
{
|
||||
agent_id: "agent-2",
|
||||
name: "Test Agent 2",
|
||||
description: "Description 2",
|
||||
version: "2.0",
|
||||
is_public: true,
|
||||
skills: [],
|
||||
protocolVersion: "1.0",
|
||||
},
|
||||
] as AgentHubData[],
|
||||
onSuccess: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should render the component", () => {
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
expect(screen.getByText("Make Agents Public")).toBeInTheDocument();
|
||||
expect(screen.getByText("Select Agents to Make Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should initialize with correct state", () => {
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
// Check that the component renders with the correct title and content
|
||||
expect(screen.getByText("Make Agents Public")).toBeInTheDocument();
|
||||
expect(screen.getByText("Select Agents to Make Public")).toBeInTheDocument();
|
||||
|
||||
// Check that all agent checkboxes are present
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
expect(checkboxes).toHaveLength(3); // Select all + 2 agents
|
||||
|
||||
// Check that the Next button is enabled (agents are preselected)
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
expect(nextButton).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it("should handle agent selection and navigation", async () => {
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
// Initially on step 1
|
||||
expect(screen.getByText("Select Agents to Make Public")).toBeInTheDocument();
|
||||
|
||||
// Select all agents using the select all checkbox
|
||||
const selectAllCheckbox = screen.getByLabelText("Select All (2)");
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// Verify Next button is enabled
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
expect(nextButton).not.toBeDisabled();
|
||||
|
||||
// Click Next
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Should move to step 2
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Agents Public")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should submit selected agents successfully", async () => {
|
||||
mockMakeAgentsPublicCall.mockResolvedValueOnce({});
|
||||
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
// Select all agents
|
||||
const selectAllCheckbox = screen.getByLabelText("Select All (2)");
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Wait for navigation to complete
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Agents Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Submit
|
||||
const submitButton = screen.getByRole("button", { name: "Make Public" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockMakeAgentsPublicCall).toHaveBeenCalledWith("test-token", ["agent-1", "agent-2"]);
|
||||
expect(mockProps.onSuccess).toHaveBeenCalled();
|
||||
expect(mockProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle select all functionality", async () => {
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
const selectAllCheckbox = checkboxes[0];
|
||||
|
||||
// Select all
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// All checkboxes should be checked
|
||||
checkboxes.forEach((checkbox) => {
|
||||
expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
// Deselect all
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// All checkboxes should be unchecked except the indeterminate state
|
||||
expect(checkboxes[0]).not.toBeChecked();
|
||||
expect(checkboxes[1]).not.toBeChecked();
|
||||
expect(checkboxes[2]).not.toBeChecked();
|
||||
});
|
||||
|
||||
it("should show error when no agents selected", async () => {
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
// Deselect all agents first
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
await act(async () => {
|
||||
fireEvent.click(checkboxes[0]); // Click select all to select all
|
||||
fireEvent.click(checkboxes[0]); // Click select all again to deselect all
|
||||
});
|
||||
|
||||
// Try to go to next step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Should stay on same step
|
||||
expect(screen.getByText("Select Agents to Make Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display empty state when no agents are available", () => {
|
||||
const emptyProps = {
|
||||
...mockProps,
|
||||
agentHubData: [] as AgentHubData[],
|
||||
};
|
||||
|
||||
render(<MakeAgentPublicForm {...emptyProps} />);
|
||||
|
||||
expect(screen.getByText("No agents available.")).toBeInTheDocument();
|
||||
|
||||
// Select All checkbox should be disabled
|
||||
const selectAllCheckbox = screen.getByLabelText("Select All");
|
||||
expect(selectAllCheckbox).toBeDisabled();
|
||||
|
||||
// Next button should be disabled
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
expect(nextButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should handle Cancel button functionality", async () => {
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
// Click Cancel button
|
||||
const cancelButton = screen.getByRole("button", { name: "Cancel" });
|
||||
await act(async () => {
|
||||
fireEvent.click(cancelButton);
|
||||
});
|
||||
|
||||
// Should call onClose
|
||||
expect(mockProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should handle Previous button functionality", async () => {
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to step 1
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Verify we're on step 1
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Agents Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Click Previous button
|
||||
const previousButton = screen.getByRole("button", { name: "Previous" });
|
||||
await act(async () => {
|
||||
fireEvent.click(previousButton);
|
||||
});
|
||||
|
||||
// Should go back to step 0
|
||||
expect(screen.getByText("Select Agents to Make Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle individual agent selection", async () => {
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
// Get all checkboxes (select all + individual agents)
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
expect(checkboxes).toHaveLength(3); // Select all + 2 agents
|
||||
|
||||
// Initially, agent-2 should be selected (it's already public)
|
||||
const agent1Checkbox = checkboxes[1]; // First agent checkbox
|
||||
const agent2Checkbox = checkboxes[2]; // Second agent checkbox
|
||||
|
||||
expect(agent2Checkbox).toBeChecked(); // agent-2 is already public
|
||||
|
||||
// Select agent-1
|
||||
await act(async () => {
|
||||
fireEvent.click(agent1Checkbox);
|
||||
});
|
||||
|
||||
expect(agent1Checkbox).toBeChecked();
|
||||
expect(agent2Checkbox).toBeChecked();
|
||||
|
||||
// Deselect agent-2
|
||||
await act(async () => {
|
||||
fireEvent.click(agent2Checkbox);
|
||||
});
|
||||
|
||||
expect(agent1Checkbox).toBeChecked();
|
||||
expect(agent2Checkbox).not.toBeChecked();
|
||||
|
||||
// Select all should be indeterminate now
|
||||
const selectAllCheckbox = checkboxes[0];
|
||||
expect(selectAllCheckbox).toHaveAttribute("data-indeterminate", "true");
|
||||
});
|
||||
|
||||
it("should display skills overflow text when agent has more than 3 skills", () => {
|
||||
const agentWithManySkills = {
|
||||
...mockProps.agentHubData[0],
|
||||
skills: [
|
||||
{ id: "skill-1", name: "Skill 1", description: "Skill desc" },
|
||||
{ id: "skill-2", name: "Skill 2", description: "Skill desc" },
|
||||
{ id: "skill-3", name: "Skill 3", description: "Skill desc" },
|
||||
{ id: "skill-4", name: "Skill 4", description: "Skill desc" },
|
||||
{ id: "skill-5", name: "Skill 5", description: "Skill desc" },
|
||||
],
|
||||
};
|
||||
|
||||
const propsWithManySkills = {
|
||||
...mockProps,
|
||||
agentHubData: [agentWithManySkills],
|
||||
};
|
||||
|
||||
render(<MakeAgentPublicForm {...propsWithManySkills} />);
|
||||
|
||||
// Should show first 3 skills as badges
|
||||
expect(screen.getByText("Skill 1")).toBeInTheDocument();
|
||||
expect(screen.getByText("Skill 2")).toBeInTheDocument();
|
||||
expect(screen.getByText("Skill 3")).toBeInTheDocument();
|
||||
|
||||
// Should show "+2 more" text for the remaining skills
|
||||
expect(screen.getByText("+2 more")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle submit error properly", async () => {
|
||||
const errorMessage = "Network error";
|
||||
mockMakeAgentsPublicCall.mockRejectedValueOnce(new Error(errorMessage));
|
||||
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Agents Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Submit
|
||||
const submitButton = screen.getByRole("button", { name: "Make Public" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
// Should handle error and show error notification
|
||||
await waitFor(() => {
|
||||
expect(mockMakeAgentsPublicCall).toHaveBeenCalledWith("test-token", ["agent-2"]);
|
||||
});
|
||||
|
||||
// Should not call onSuccess or onClose on error
|
||||
expect(mockProps.onSuccess).not.toHaveBeenCalled();
|
||||
expect(mockProps.onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should show loading state during submit", async () => {
|
||||
let resolvePromise: (value: any) => void = () => {};
|
||||
const pendingPromise = new Promise((resolve) => {
|
||||
resolvePromise = resolve;
|
||||
});
|
||||
mockMakeAgentsPublicCall.mockReturnValueOnce(pendingPromise);
|
||||
|
||||
render(<MakeAgentPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Agents Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Submit
|
||||
const submitButton = screen.getByRole("button", { name: "Make Public" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
// Check loading state
|
||||
expect(submitButton).toHaveAttribute("data-loading", "true");
|
||||
expect(submitButton).toBeDisabled();
|
||||
|
||||
// Resolve the promise
|
||||
resolvePromise({});
|
||||
await waitFor(() => {
|
||||
expect(mockProps.onSuccess).toHaveBeenCalled();
|
||||
expect(mockProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render modal when visible is false", () => {
|
||||
const invisibleProps = {
|
||||
...mockProps,
|
||||
visible: false,
|
||||
};
|
||||
|
||||
render(<MakeAgentPublicForm {...invisibleProps} />);
|
||||
|
||||
// Modal should not be rendered
|
||||
expect(screen.queryByTestId("modal")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Make Agents Public")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should preselect already public agents when modal opens", () => {
|
||||
// Test data where one agent is public and one is not
|
||||
const mixedPublicProps = {
|
||||
...mockProps,
|
||||
agentHubData: [
|
||||
{
|
||||
agent_id: "agent-1",
|
||||
name: "Test Agent 1",
|
||||
description: "Description 1",
|
||||
url: "http://example.com/agent1",
|
||||
version: "1.0",
|
||||
is_public: false, // Not public
|
||||
skills: [],
|
||||
protocolVersion: "1.0",
|
||||
},
|
||||
{
|
||||
agent_id: "agent-2",
|
||||
name: "Test Agent 2",
|
||||
description: "Description 2",
|
||||
url: "http://example.com/agent2",
|
||||
version: "2.0",
|
||||
is_public: true, // Already public
|
||||
skills: [],
|
||||
protocolVersion: "1.0",
|
||||
},
|
||||
{
|
||||
agent_id: "agent-3",
|
||||
name: "Test Agent 3",
|
||||
description: "Description 3",
|
||||
url: "http://example.com/agent3",
|
||||
version: "3.0",
|
||||
is_public: true, // Already public
|
||||
skills: [],
|
||||
protocolVersion: "1.0",
|
||||
},
|
||||
] as AgentHubData[],
|
||||
};
|
||||
|
||||
render(<MakeAgentPublicForm {...mixedPublicProps} />);
|
||||
|
||||
// Check that the correct checkboxes are selected
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
expect(checkboxes).toHaveLength(4); // Select all + 3 agents
|
||||
|
||||
// agent-2 and agent-3 should be checked (they're already public)
|
||||
const agent1Checkbox = checkboxes[1];
|
||||
const agent2Checkbox = checkboxes[2];
|
||||
const agent3Checkbox = checkboxes[3];
|
||||
|
||||
expect(agent1Checkbox).not.toBeChecked(); // agent-1 is not public
|
||||
expect(agent2Checkbox).toBeChecked(); // agent-2 is public
|
||||
expect(agent3Checkbox).toBeChecked(); // agent-3 is public
|
||||
|
||||
// Select all should be indeterminate
|
||||
const selectAllCheckbox = checkboxes[0];
|
||||
expect(selectAllCheckbox).toHaveAttribute("data-indeterminate", "true");
|
||||
});
|
||||
});
|
||||
|
|
@ -3,7 +3,7 @@ import { Modal, Form, Steps, Button, Checkbox } from "antd";
|
|||
import { Text, Title, Badge } from "@tremor/react";
|
||||
import { makeAgentsPublicCall } from "../../networking";
|
||||
import NotificationsManager from "../../molecules/notifications_manager";
|
||||
import { AgentHubData } from "@/components/AIHub/agent_hub_table_columns";
|
||||
import { AgentHubData } from "@/components/AIHub/AgentHubTableColumns";
|
||||
|
||||
const { Step } = Steps;
|
||||
|
||||
|
|
@ -0,0 +1,562 @@
|
|||
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import MakeMCPPublicForm from "./MakeMCPPublicForm";
|
||||
import { MCPServerData } from "../../mcp_hub_table_columns";
|
||||
|
||||
// Mock the networking function
|
||||
vi.mock("../../networking", () => ({
|
||||
makeMCPPublicCall: vi.fn(),
|
||||
}));
|
||||
|
||||
// Import the mocked function
|
||||
import { makeMCPPublicCall } from "../../networking";
|
||||
const mockMakeMCPPublicCall = vi.mocked(makeMCPPublicCall);
|
||||
|
||||
// Mock antd components
|
||||
vi.mock("antd", () => ({
|
||||
Modal: ({ open, title, children, onCancel, footer }: any) =>
|
||||
open ? (
|
||||
<div data-testid="modal">
|
||||
<div>{title}</div>
|
||||
{children}
|
||||
{footer}
|
||||
</div>
|
||||
) : null,
|
||||
Form: Object.assign(({ children, form }: any) => <form data-testid="form">{children}</form>, {
|
||||
useForm: () => [
|
||||
{
|
||||
resetFields: vi.fn(),
|
||||
validateFields: vi.fn(),
|
||||
getFieldsValue: vi.fn(),
|
||||
setFieldsValue: vi.fn(),
|
||||
},
|
||||
vi.fn(),
|
||||
],
|
||||
Item: ({ children }: any) => <div>{children}</div>,
|
||||
}),
|
||||
Steps: Object.assign(
|
||||
({ children, current, className }: any) => (
|
||||
<div data-testid="steps" className={className}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
{
|
||||
Step: ({ title }: any) => <div>{title}</div>,
|
||||
},
|
||||
),
|
||||
Button: ({ children, onClick, disabled, loading, ...props }: any) => (
|
||||
<button onClick={onClick} disabled={disabled || loading} data-loading={loading} {...props}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
Checkbox: ({ checked, indeterminate, onChange, children, disabled }: any) => (
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={(e) => onChange({ target: { checked: e.target.checked } })}
|
||||
disabled={disabled}
|
||||
data-indeterminate={indeterminate}
|
||||
/>
|
||||
{children}
|
||||
</label>
|
||||
),
|
||||
}));
|
||||
|
||||
// Additional @tremor/react mocks (Button is already mocked globally)
|
||||
vi.mock("@tremor/react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@tremor/react")>();
|
||||
return {
|
||||
...actual,
|
||||
Text: ({ children, className }: any) => <span className={className}>{children}</span>,
|
||||
Title: ({ children }: any) => <h3>{children}</h3>,
|
||||
Badge: ({ children, color, size }: any) => (
|
||||
<span data-color={color} data-size={size}>
|
||||
{children}
|
||||
</span>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
describe("MakeMCPPublicForm", () => {
|
||||
const mockProps = {
|
||||
visible: true,
|
||||
onClose: vi.fn(),
|
||||
accessToken: "test-token",
|
||||
mcpHubData: [
|
||||
{
|
||||
server_id: "server-1",
|
||||
server_name: "Test Server 1",
|
||||
description: "Description 1",
|
||||
url: "http://example.com/server1",
|
||||
transport: "http",
|
||||
status: "active",
|
||||
mcp_info: { is_public: false },
|
||||
allowed_tools: ["tool-1", "tool-2"],
|
||||
auth_type: "bearer",
|
||||
credentials: {},
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user1",
|
||||
teams: [],
|
||||
mcp_access_groups: [],
|
||||
extra_headers: [],
|
||||
static_headers: {},
|
||||
args: [],
|
||||
env: {},
|
||||
},
|
||||
{
|
||||
server_id: "server-2",
|
||||
server_name: "Test Server 2",
|
||||
description: "Description 2",
|
||||
url: "http://example.com/server2",
|
||||
transport: "websocket",
|
||||
status: "inactive",
|
||||
mcp_info: { is_public: true },
|
||||
allowed_tools: [],
|
||||
auth_type: "none",
|
||||
credentials: {},
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user2",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user2",
|
||||
teams: [],
|
||||
mcp_access_groups: [],
|
||||
extra_headers: [],
|
||||
static_headers: {},
|
||||
args: [],
|
||||
env: {},
|
||||
},
|
||||
] as MCPServerData[],
|
||||
onSuccess: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should render the component", () => {
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
expect(screen.getByText("Make MCP Servers Public")).toBeInTheDocument();
|
||||
expect(screen.getByText("Select MCP Servers to Make Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should initialize with correct state", () => {
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
// Check that the component renders with the correct title and content
|
||||
expect(screen.getByText("Make MCP Servers Public")).toBeInTheDocument();
|
||||
expect(screen.getByText("Select MCP Servers to Make Public")).toBeInTheDocument();
|
||||
|
||||
// Check that all server checkboxes are present
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
expect(checkboxes).toHaveLength(3); // Select all + 2 servers
|
||||
|
||||
// Check that the Next button is enabled (servers are preselected)
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
expect(nextButton).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it("should handle server selection and navigation", async () => {
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
// Initially on step 1
|
||||
expect(screen.getByText("Select MCP Servers to Make Public")).toBeInTheDocument();
|
||||
|
||||
// Select all servers using the select all checkbox
|
||||
const selectAllCheckbox = screen.getByLabelText("Select All (2)");
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// Verify Next button is enabled
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
expect(nextButton).not.toBeDisabled();
|
||||
|
||||
// Click Next
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Should move to step 2
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making MCP Servers Public")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should submit selected servers successfully", async () => {
|
||||
mockMakeMCPPublicCall.mockResolvedValueOnce({});
|
||||
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
// Select all servers
|
||||
const selectAllCheckbox = screen.getByLabelText("Select All (2)");
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Wait for navigation to complete
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making MCP Servers Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Submit
|
||||
const submitButton = screen.getByRole("button", { name: "Make Public" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockMakeMCPPublicCall).toHaveBeenCalledWith("test-token", ["server-1", "server-2"]);
|
||||
expect(mockProps.onSuccess).toHaveBeenCalled();
|
||||
expect(mockProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle select all functionality", async () => {
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
const selectAllCheckbox = checkboxes[0];
|
||||
|
||||
// Select all
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// All checkboxes should be checked
|
||||
checkboxes.forEach((checkbox) => {
|
||||
expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
// Deselect all
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// All checkboxes should be unchecked except the indeterminate state
|
||||
expect(checkboxes[0]).not.toBeChecked();
|
||||
expect(checkboxes[1]).not.toBeChecked();
|
||||
expect(checkboxes[2]).not.toBeChecked();
|
||||
});
|
||||
|
||||
it("should show error when no servers selected", async () => {
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
// Deselect all servers first
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
await act(async () => {
|
||||
fireEvent.click(checkboxes[0]); // Click select all to select all
|
||||
fireEvent.click(checkboxes[0]); // Click select all again to deselect all
|
||||
});
|
||||
|
||||
// Try to go to next step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Should stay on same step
|
||||
expect(screen.getByText("Select MCP Servers to Make Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display empty state when no servers are available", () => {
|
||||
const emptyProps = {
|
||||
...mockProps,
|
||||
mcpHubData: [] as MCPServerData[],
|
||||
};
|
||||
|
||||
render(<MakeMCPPublicForm {...emptyProps} />);
|
||||
|
||||
expect(screen.getByText("No MCP servers available.")).toBeInTheDocument();
|
||||
|
||||
// Select All checkbox should be disabled
|
||||
const selectAllCheckbox = screen.getByLabelText("Select All");
|
||||
expect(selectAllCheckbox).toBeDisabled();
|
||||
|
||||
// Next button should be disabled
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
expect(nextButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should handle Cancel button functionality", async () => {
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
// Click Cancel button
|
||||
const cancelButton = screen.getByRole("button", { name: "Cancel" });
|
||||
await act(async () => {
|
||||
fireEvent.click(cancelButton);
|
||||
});
|
||||
|
||||
// Should call onClose
|
||||
expect(mockProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should handle Previous button functionality", async () => {
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to step 1
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Verify we're on step 1
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making MCP Servers Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Click Previous button
|
||||
const previousButton = screen.getByRole("button", { name: "Previous" });
|
||||
await act(async () => {
|
||||
fireEvent.click(previousButton);
|
||||
});
|
||||
|
||||
// Should go back to step 0
|
||||
expect(screen.getByText("Select MCP Servers to Make Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle individual server selection", async () => {
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
// Get all checkboxes (select all + individual servers)
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
expect(checkboxes).toHaveLength(3); // Select all + 2 servers
|
||||
|
||||
// Initially, server-2 should be selected (it's already public)
|
||||
const server1Checkbox = checkboxes[1]; // First server checkbox
|
||||
const server2Checkbox = checkboxes[2]; // Second server checkbox
|
||||
|
||||
expect(server2Checkbox).toBeChecked(); // server-2 is already public
|
||||
|
||||
// Select server-1
|
||||
await act(async () => {
|
||||
fireEvent.click(server1Checkbox);
|
||||
});
|
||||
|
||||
expect(server1Checkbox).toBeChecked();
|
||||
expect(server2Checkbox).toBeChecked();
|
||||
|
||||
// Deselect server-2
|
||||
await act(async () => {
|
||||
fireEvent.click(server2Checkbox);
|
||||
});
|
||||
|
||||
expect(server1Checkbox).toBeChecked();
|
||||
expect(server2Checkbox).not.toBeChecked();
|
||||
|
||||
// Select all should be indeterminate now
|
||||
const selectAllCheckbox = checkboxes[0];
|
||||
expect(selectAllCheckbox).toHaveAttribute("data-indeterminate", "true");
|
||||
});
|
||||
|
||||
it("should display tools overflow text when server has more than 3 tools", () => {
|
||||
const serverWithManyTools = {
|
||||
...mockProps.mcpHubData[0],
|
||||
allowed_tools: ["tool-1", "tool-2", "tool-3", "tool-4", "tool-5"],
|
||||
};
|
||||
|
||||
const propsWithManyTools = {
|
||||
...mockProps,
|
||||
mcpHubData: [serverWithManyTools],
|
||||
};
|
||||
|
||||
render(<MakeMCPPublicForm {...propsWithManyTools} />);
|
||||
|
||||
// Should show first 3 tools as badges
|
||||
expect(screen.getByText("tool-1")).toBeInTheDocument();
|
||||
expect(screen.getByText("tool-2")).toBeInTheDocument();
|
||||
expect(screen.getByText("tool-3")).toBeInTheDocument();
|
||||
|
||||
// Should show "+2 more" text for the remaining tools
|
||||
expect(screen.getByText("+2 more")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle submit error properly", async () => {
|
||||
const errorMessage = "Network error";
|
||||
mockMakeMCPPublicCall.mockRejectedValueOnce(new Error(errorMessage));
|
||||
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making MCP Servers Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Submit
|
||||
const submitButton = screen.getByRole("button", { name: "Make Public" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
// Should handle error and show error notification
|
||||
await waitFor(() => {
|
||||
expect(mockMakeMCPPublicCall).toHaveBeenCalledWith("test-token", ["server-2"]);
|
||||
});
|
||||
|
||||
// Should not call onSuccess or onClose on error
|
||||
expect(mockProps.onSuccess).not.toHaveBeenCalled();
|
||||
expect(mockProps.onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should show loading state during submit", async () => {
|
||||
let resolvePromise: (value: any) => void = () => {};
|
||||
const pendingPromise = new Promise((resolve) => {
|
||||
resolvePromise = resolve;
|
||||
});
|
||||
mockMakeMCPPublicCall.mockReturnValueOnce(pendingPromise);
|
||||
|
||||
render(<MakeMCPPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making MCP Servers Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Submit
|
||||
const submitButton = screen.getByRole("button", { name: "Make Public" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
// Check loading state
|
||||
expect(submitButton).toHaveAttribute("data-loading", "true");
|
||||
expect(submitButton).toBeDisabled();
|
||||
|
||||
// Resolve the promise
|
||||
resolvePromise({});
|
||||
await waitFor(() => {
|
||||
expect(mockProps.onSuccess).toHaveBeenCalled();
|
||||
expect(mockProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render modal when visible is false", () => {
|
||||
const invisibleProps = {
|
||||
...mockProps,
|
||||
visible: false,
|
||||
};
|
||||
|
||||
render(<MakeMCPPublicForm {...invisibleProps} />);
|
||||
|
||||
// Modal should not be rendered
|
||||
expect(screen.queryByTestId("modal")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Make MCP Servers Public")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should preselect already public servers when modal opens", () => {
|
||||
// Test data where one server is public and one is not
|
||||
const mixedPublicProps = {
|
||||
...mockProps,
|
||||
mcpHubData: [
|
||||
{
|
||||
server_id: "server-1",
|
||||
server_name: "Test Server 1",
|
||||
description: "Description 1",
|
||||
url: "http://example.com/server1",
|
||||
transport: "http",
|
||||
status: "active",
|
||||
mcp_info: { is_public: false }, // Not public
|
||||
allowed_tools: [],
|
||||
auth_type: "bearer",
|
||||
credentials: {},
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user1",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user1",
|
||||
teams: [],
|
||||
mcp_access_groups: [],
|
||||
extra_headers: [],
|
||||
static_headers: {},
|
||||
args: [],
|
||||
env: {},
|
||||
},
|
||||
{
|
||||
server_id: "server-2",
|
||||
server_name: "Test Server 2",
|
||||
description: "Description 2",
|
||||
url: "http://example.com/server2",
|
||||
transport: "websocket",
|
||||
status: "inactive",
|
||||
mcp_info: { is_public: true }, // Already public
|
||||
allowed_tools: [],
|
||||
auth_type: "none",
|
||||
credentials: {},
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user2",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user2",
|
||||
teams: [],
|
||||
mcp_access_groups: [],
|
||||
extra_headers: [],
|
||||
static_headers: {},
|
||||
args: [],
|
||||
env: {},
|
||||
},
|
||||
{
|
||||
server_id: "server-3",
|
||||
server_name: "Test Server 3",
|
||||
description: "Description 3",
|
||||
url: "http://example.com/server3",
|
||||
transport: "sse",
|
||||
status: "healthy",
|
||||
mcp_info: { is_public: true }, // Already public
|
||||
allowed_tools: [],
|
||||
auth_type: "oauth",
|
||||
credentials: {},
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
created_by: "user3",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
updated_by: "user3",
|
||||
teams: [],
|
||||
mcp_access_groups: [],
|
||||
extra_headers: [],
|
||||
static_headers: {},
|
||||
args: [],
|
||||
env: {},
|
||||
},
|
||||
] as MCPServerData[],
|
||||
};
|
||||
|
||||
render(<MakeMCPPublicForm {...mixedPublicProps} />);
|
||||
|
||||
// Check that the correct checkboxes are selected
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
expect(checkboxes).toHaveLength(4); // Select all + 3 servers
|
||||
|
||||
// server-2 and server-3 should be checked (they're already public)
|
||||
const server1Checkbox = checkboxes[1];
|
||||
const server2Checkbox = checkboxes[2];
|
||||
const server3Checkbox = checkboxes[3];
|
||||
|
||||
expect(server1Checkbox).not.toBeChecked(); // server-1 is not public
|
||||
expect(server2Checkbox).toBeChecked(); // server-2 is public
|
||||
expect(server3Checkbox).toBeChecked(); // server-3 is public
|
||||
|
||||
// Select all should be indeterminate
|
||||
const selectAllCheckbox = checkboxes[0];
|
||||
expect(selectAllCheckbox).toHaveAttribute("data-indeterminate", "true");
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,557 @@
|
|||
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import MakeModelPublicForm from "./MakeModelPublicForm";
|
||||
|
||||
interface ModelGroupInfo {
|
||||
model_group: string;
|
||||
providers: string[];
|
||||
max_input_tokens?: number;
|
||||
max_output_tokens?: number;
|
||||
input_cost_per_token?: number;
|
||||
output_cost_per_token?: number;
|
||||
mode?: string;
|
||||
tpm?: number;
|
||||
rpm?: number;
|
||||
supports_parallel_function_calling: boolean;
|
||||
supports_vision: boolean;
|
||||
supports_function_calling: boolean;
|
||||
supported_openai_params?: string[];
|
||||
is_public_model_group: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// Mock the networking function
|
||||
vi.mock("../../networking", () => ({
|
||||
makeModelGroupPublic: vi.fn(),
|
||||
}));
|
||||
|
||||
// Import the mocked function
|
||||
import { makeModelGroupPublic } from "../../networking";
|
||||
const mockMakeModelGroupPublic = vi.mocked(makeModelGroupPublic);
|
||||
|
||||
// Mock antd components
|
||||
vi.mock("antd", () => ({
|
||||
Modal: ({ open, title, children, onCancel, footer }: any) =>
|
||||
open ? (
|
||||
<div data-testid="modal">
|
||||
<div>{title}</div>
|
||||
{children}
|
||||
{footer}
|
||||
</div>
|
||||
) : null,
|
||||
Form: Object.assign(({ children, form }: any) => <form data-testid="form">{children}</form>, {
|
||||
useForm: () => [
|
||||
{
|
||||
resetFields: vi.fn(),
|
||||
validateFields: vi.fn(),
|
||||
getFieldsValue: vi.fn(),
|
||||
setFieldsValue: vi.fn(),
|
||||
},
|
||||
vi.fn(),
|
||||
],
|
||||
Item: ({ children }: any) => <div>{children}</div>,
|
||||
}),
|
||||
Steps: Object.assign(
|
||||
({ children, current, className }: any) => (
|
||||
<div data-testid="steps" className={className}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
{
|
||||
Step: ({ title }: any) => <div>{title}</div>,
|
||||
},
|
||||
),
|
||||
Button: ({ children, onClick, disabled, loading, ...props }: any) => (
|
||||
<button onClick={onClick} disabled={disabled || loading} data-loading={loading} {...props}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
Checkbox: ({ checked, indeterminate, onChange, children, disabled }: any) => (
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={(e) => onChange({ target: { checked: e.target.checked } })}
|
||||
disabled={disabled}
|
||||
data-indeterminate={indeterminate}
|
||||
/>
|
||||
{children}
|
||||
</label>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock @tremor/react components
|
||||
vi.mock("@tremor/react", () => ({
|
||||
Text: ({ children, className }: any) => <span className={className}>{children}</span>,
|
||||
Title: ({ children }: any) => <h3>{children}</h3>,
|
||||
Badge: ({ children, color, size }: any) => (
|
||||
<span data-color={color} data-size={size}>
|
||||
{children}
|
||||
</span>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock ModelFilters component
|
||||
vi.mock("../../model_filters", () => ({
|
||||
default: ({ onFilteredDataChange, modelHubData }: any) => (
|
||||
<div data-testid="model-filters">
|
||||
<button data-testid="trigger-filter-change" onClick={() => onFilteredDataChange(modelHubData)}>
|
||||
Apply Filters
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock NotificationsManager
|
||||
vi.mock("../../molecules/notifications_manager", () => ({
|
||||
default: {
|
||||
fromBackend: vi.fn(),
|
||||
success: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("MakeModelPublicForm", () => {
|
||||
const mockProps = {
|
||||
visible: true,
|
||||
onClose: vi.fn(),
|
||||
accessToken: "test-token",
|
||||
modelHubData: [
|
||||
{
|
||||
model_group: "gpt-4",
|
||||
providers: ["openai"],
|
||||
max_input_tokens: 8192,
|
||||
max_output_tokens: 4096,
|
||||
input_cost_per_token: 0.03,
|
||||
output_cost_per_token: 0.06,
|
||||
mode: "chat",
|
||||
tpm: 10000,
|
||||
rpm: 200,
|
||||
supports_parallel_function_calling: true,
|
||||
supports_vision: false,
|
||||
supports_function_calling: true,
|
||||
supported_openai_params: ["temperature", "max_tokens"],
|
||||
is_public_model_group: false,
|
||||
},
|
||||
{
|
||||
model_group: "gpt-3.5-turbo",
|
||||
providers: ["openai"],
|
||||
max_input_tokens: 4096,
|
||||
max_output_tokens: 2048,
|
||||
input_cost_per_token: 0.0015,
|
||||
output_cost_per_token: 0.002,
|
||||
mode: "chat",
|
||||
tpm: 60000,
|
||||
rpm: 3500,
|
||||
supports_parallel_function_calling: false,
|
||||
supports_vision: false,
|
||||
supports_function_calling: true,
|
||||
supported_openai_params: ["temperature", "max_tokens"],
|
||||
is_public_model_group: true,
|
||||
},
|
||||
] as ModelGroupInfo[],
|
||||
onSuccess: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should render the component", () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
expect(screen.getByText("Make Models Public")).toBeInTheDocument();
|
||||
expect(screen.getByText("Select Models to Make Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should initialize with correct state", () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Check that the component renders with the correct title and content
|
||||
expect(screen.getByText("Make Models Public")).toBeInTheDocument();
|
||||
expect(screen.getByText("Select Models to Make Public")).toBeInTheDocument();
|
||||
|
||||
// Check that all model checkboxes are present
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
expect(checkboxes).toHaveLength(3); // Select all + 2 models
|
||||
|
||||
// Check that the Next button is enabled (models are preselected)
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
expect(nextButton).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it("should handle model selection and navigation", async () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Initially on step 1
|
||||
expect(screen.getByText("Select Models to Make Public")).toBeInTheDocument();
|
||||
|
||||
// Select all models using the select all checkbox
|
||||
const selectAllCheckbox = screen.getByLabelText("Select All (2)");
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// Verify Next button is enabled
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
expect(nextButton).not.toBeDisabled();
|
||||
|
||||
// Click Next
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Should move to step 2
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Models Public")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should submit selected models successfully", async () => {
|
||||
mockMakeModelGroupPublic.mockResolvedValueOnce({});
|
||||
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Select all models
|
||||
const selectAllCheckbox = screen.getByLabelText("Select All (2)");
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Wait for navigation to complete
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Models Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Submit
|
||||
const submitButton = screen.getByRole("button", { name: "Make Public" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockMakeModelGroupPublic).toHaveBeenCalledWith("test-token", ["gpt-4", "gpt-3.5-turbo"]);
|
||||
expect(mockProps.onSuccess).toHaveBeenCalled();
|
||||
expect(mockProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle select all functionality", async () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
const selectAllCheckbox = checkboxes[0];
|
||||
|
||||
// Select all
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// All checkboxes should be checked
|
||||
checkboxes.forEach((checkbox) => {
|
||||
expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
// Deselect all
|
||||
await act(async () => {
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
});
|
||||
|
||||
// All checkboxes should be unchecked except the indeterminate state
|
||||
expect(checkboxes[0]).not.toBeChecked();
|
||||
expect(checkboxes[1]).not.toBeChecked();
|
||||
expect(checkboxes[2]).not.toBeChecked();
|
||||
});
|
||||
|
||||
it("should show error when no models selected", async () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Deselect all models first
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
await act(async () => {
|
||||
fireEvent.click(checkboxes[0]); // Click select all to select all
|
||||
fireEvent.click(checkboxes[0]); // Click select all again to deselect all
|
||||
});
|
||||
|
||||
// Try to go to next step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Should stay on same step
|
||||
expect(screen.getByText("Select Models to Make Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display empty state when no models are available", () => {
|
||||
const emptyProps = {
|
||||
...mockProps,
|
||||
modelHubData: [] as ModelGroupInfo[],
|
||||
};
|
||||
|
||||
render(<MakeModelPublicForm {...emptyProps} />);
|
||||
|
||||
expect(screen.getByText("No models match the current filters.")).toBeInTheDocument();
|
||||
|
||||
// Select All checkbox should be disabled
|
||||
const selectAllCheckbox = screen.getByLabelText("Select All");
|
||||
expect(selectAllCheckbox).toBeDisabled();
|
||||
|
||||
// Next button should be disabled
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
expect(nextButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should handle Cancel button functionality", async () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Click Cancel button
|
||||
const cancelButton = screen.getByRole("button", { name: "Cancel" });
|
||||
await act(async () => {
|
||||
fireEvent.click(cancelButton);
|
||||
});
|
||||
|
||||
// Should call onClose
|
||||
expect(mockProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should handle Previous button functionality", async () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to step 1
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
// Verify we're on step 1
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Models Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Click Previous button
|
||||
const previousButton = screen.getByRole("button", { name: "Previous" });
|
||||
await act(async () => {
|
||||
fireEvent.click(previousButton);
|
||||
});
|
||||
|
||||
// Should go back to step 0
|
||||
expect(screen.getByText("Select Models to Make Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle individual model selection", async () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Get all checkboxes (select all + individual models)
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
expect(checkboxes).toHaveLength(3); // Select all + 2 models
|
||||
|
||||
// Initially, gpt-3.5-turbo should be selected (it's already public)
|
||||
const gpt4Checkbox = checkboxes[1]; // First model checkbox
|
||||
const gpt35Checkbox = checkboxes[2]; // Second model checkbox
|
||||
|
||||
expect(gpt35Checkbox).toBeChecked(); // gpt-3.5-turbo is already public
|
||||
|
||||
// Select gpt-4
|
||||
await act(async () => {
|
||||
fireEvent.click(gpt4Checkbox);
|
||||
});
|
||||
|
||||
expect(gpt4Checkbox).toBeChecked();
|
||||
expect(gpt35Checkbox).toBeChecked();
|
||||
|
||||
// Deselect gpt-3.5-turbo
|
||||
await act(async () => {
|
||||
fireEvent.click(gpt35Checkbox);
|
||||
});
|
||||
|
||||
expect(gpt4Checkbox).toBeChecked();
|
||||
expect(gpt35Checkbox).not.toBeChecked();
|
||||
|
||||
// Select all should be indeterminate now
|
||||
const selectAllCheckbox = checkboxes[0];
|
||||
expect(selectAllCheckbox).toHaveAttribute("data-indeterminate", "true");
|
||||
});
|
||||
|
||||
it("should display model badges and information", () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Should show model names
|
||||
expect(screen.getByText("gpt-4")).toBeInTheDocument();
|
||||
expect(screen.getByText("gpt-3.5-turbo")).toBeInTheDocument();
|
||||
|
||||
// Should show mode badges
|
||||
expect(screen.getAllByText("chat")).toHaveLength(2);
|
||||
|
||||
// Should show provider badges
|
||||
expect(screen.getAllByText("openai")).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should handle submit error properly", async () => {
|
||||
const errorMessage = "Network error";
|
||||
mockMakeModelGroupPublic.mockRejectedValueOnce(new Error(errorMessage));
|
||||
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Models Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Submit
|
||||
const submitButton = screen.getByRole("button", { name: "Make Public" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
// Should handle error and show error notification
|
||||
await waitFor(() => {
|
||||
expect(mockMakeModelGroupPublic).toHaveBeenCalledWith("test-token", ["gpt-3.5-turbo"]);
|
||||
});
|
||||
|
||||
// Should not call onSuccess or onClose on error
|
||||
expect(mockProps.onSuccess).not.toHaveBeenCalled();
|
||||
expect(mockProps.onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should show loading state during submit", async () => {
|
||||
let resolvePromise: (value: any) => void = () => {};
|
||||
const pendingPromise = new Promise((resolve) => {
|
||||
resolvePromise = resolve;
|
||||
});
|
||||
mockMakeModelGroupPublic.mockReturnValueOnce(pendingPromise);
|
||||
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Models Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Submit
|
||||
const submitButton = screen.getByRole("button", { name: "Make Public" });
|
||||
await act(async () => {
|
||||
fireEvent.click(submitButton);
|
||||
});
|
||||
|
||||
// Check loading state
|
||||
expect(submitButton).toHaveAttribute("data-loading", "true");
|
||||
expect(submitButton).toBeDisabled();
|
||||
|
||||
// Resolve the promise
|
||||
resolvePromise({});
|
||||
await waitFor(() => {
|
||||
expect(mockProps.onSuccess).toHaveBeenCalled();
|
||||
expect(mockProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render modal when visible is false", () => {
|
||||
const invisibleProps = {
|
||||
...mockProps,
|
||||
visible: false,
|
||||
};
|
||||
|
||||
render(<MakeModelPublicForm {...invisibleProps} />);
|
||||
|
||||
// Modal should not be rendered
|
||||
expect(screen.queryByTestId("modal")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Make Models Public")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should preselect already public models when modal opens", () => {
|
||||
// Test data where one model is public and one is not
|
||||
const mixedPublicProps = {
|
||||
...mockProps,
|
||||
modelHubData: [
|
||||
{
|
||||
model_group: "private-model",
|
||||
providers: ["openai"],
|
||||
is_public_model_group: false,
|
||||
mode: "chat",
|
||||
},
|
||||
{
|
||||
model_group: "public-model",
|
||||
providers: ["anthropic"],
|
||||
is_public_model_group: true,
|
||||
mode: "completion",
|
||||
},
|
||||
{
|
||||
model_group: "another-public-model",
|
||||
providers: ["cohere"],
|
||||
is_public_model_group: true,
|
||||
mode: "chat",
|
||||
},
|
||||
] as ModelGroupInfo[],
|
||||
};
|
||||
|
||||
render(<MakeModelPublicForm {...mixedPublicProps} />);
|
||||
|
||||
// Check that the correct checkboxes are selected
|
||||
const checkboxes = screen.getAllByRole("checkbox");
|
||||
expect(checkboxes).toHaveLength(4); // Select all + 3 models
|
||||
|
||||
// private-model should not be checked, public models should be checked
|
||||
const privateModelCheckbox = checkboxes[1];
|
||||
const publicModelCheckbox = checkboxes[2];
|
||||
const anotherPublicModelCheckbox = checkboxes[3];
|
||||
|
||||
expect(privateModelCheckbox).not.toBeChecked(); // private-model is not public
|
||||
expect(publicModelCheckbox).toBeChecked(); // public-model is public
|
||||
expect(anotherPublicModelCheckbox).toBeChecked(); // another-public-model is public
|
||||
|
||||
// Select all should be indeterminate
|
||||
const selectAllCheckbox = checkboxes[0];
|
||||
expect(selectAllCheckbox).toHaveAttribute("data-indeterminate", "true");
|
||||
});
|
||||
|
||||
it("should show selected count", () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Should show that 1 model is selected (gpt-3.5-turbo is preselected)
|
||||
expect(screen.getByText("1")).toBeInTheDocument();
|
||||
expect(screen.getByText("model selected")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show confirmation step with selected models", async () => {
|
||||
render(<MakeModelPublicForm {...mockProps} />);
|
||||
|
||||
// Navigate to confirm step
|
||||
const nextButton = screen.getByRole("button", { name: "Next" });
|
||||
await act(async () => {
|
||||
fireEvent.click(nextButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Confirm Making Models Public")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Should show the selected model
|
||||
expect(screen.getByText("gpt-3.5-turbo")).toBeInTheDocument();
|
||||
|
||||
// Should show the warning message
|
||||
expect(screen.getByText(/Warning:/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/model_hub_table/)).toBeInTheDocument();
|
||||
|
||||
// Should show total count (already verified by checking the presence of the confirmation step)
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue