children; getByText with exact:false
- // matches against the element's full textContent across child nodes
- expect(screen.getByText((_, el) =>
- el?.tagName === "P" && el.textContent === "In: text"
- )).toBeInTheDocument();
- expect(screen.getByText((_, el) =>
- el?.tagName === "P" && el.textContent === "Out: text, image"
- )).toBeInTheDocument();
+ // The label and value are sibling text nodes within the same container div.
+ const inLabel = screen.getByText("In:");
+ expect(inLabel.parentElement?.textContent).toContain("text");
+ const outLabel = screen.getByText("Out:");
+ expect(outLabel.parentElement?.textContent).toContain("text, image");
});
it("should display 'Yes' badge for public agents", () => {
diff --git a/ui/litellm-dashboard/src/components/agent_management/AgentSelector.test.tsx b/ui/litellm-dashboard/src/components/agent_management/AgentSelector.test.tsx
index 26f903cd310..7d3f5d2edac 100644
--- a/ui/litellm-dashboard/src/components/agent_management/AgentSelector.test.tsx
+++ b/ui/litellm-dashboard/src/components/agent_management/AgentSelector.test.tsx
@@ -2,41 +2,11 @@ import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi, beforeEach } from "vitest";
-// Mock networking module
const mockGetAgentsList = vi.fn();
vi.mock("../networking", () => ({
getAgentsList: (...args: any[]) => mockGetAgentsList(...args),
}));
-// Mock antd Select
-vi.mock("antd", () => {
- const SelectComponent = ({ children, onChange, value, mode, placeholder, loading, disabled, ...props }: any) => (
-
-
- {loading && Loading}
-
- );
-
- SelectComponent.Option = ({ children, value, ...props }: any) => (
-
- );
-
- return { Select: SelectComponent };
-});
-
import AgentSelector from "./AgentSelector";
describe("AgentSelector", () => {
@@ -50,95 +20,125 @@ describe("AgentSelector", () => {
mockGetAgentsList.mockResolvedValue({
agents: [
{ agent_id: "agent-1", agent_name: "Agent One" },
- { agent_id: "agent-2", agent_name: "Agent Two", agent_access_groups: ["group-a", "group-b"] },
+ {
+ agent_id: "agent-2",
+ agent_name: "Agent Two",
+ agent_access_groups: ["group-a", "group-b"],
+ },
],
});
});
- it("renders the selector", () => {
+ it("should render the selector trigger", () => {
render();
- expect(screen.getByTestId("agent-select")).toBeInTheDocument();
+ expect(screen.getByRole("button", { name: /select agents/i })).toBeInTheDocument();
});
- it("fetches agents on mount with access token", async () => {
+ it("should fetch agents on mount with access token", async () => {
render();
await waitFor(() => {
expect(mockGetAgentsList).toHaveBeenCalledWith("test-token");
});
});
- it("does not fetch when accessToken is empty", () => {
+ it("should not fetch when accessToken is empty", () => {
render();
expect(mockGetAgentsList).not.toHaveBeenCalled();
});
- it("shows loading state while fetching", async () => {
- // Keep the promise pending
- let resolve: any;
- mockGetAgentsList.mockReturnValue(new Promise((r) => { resolve = r; }));
-
+ it("should render agent options after fetch when popover is opened", async () => {
+ const user = userEvent.setup();
render();
- expect(screen.getByTestId("agent-select")).toHaveAttribute("data-loading", "true");
-
- // Resolve to clean up
- resolve({ agents: [] });
await waitFor(() => {
- expect(screen.getByTestId("agent-select")).toHaveAttribute("data-loading", "false");
+ expect(mockGetAgentsList).toHaveBeenCalled();
});
- });
- it("renders agent options after fetch", async () => {
- render();
+ await user.click(screen.getByRole("button", { name: /select agents/i }));
+
await waitFor(() => {
expect(screen.getByText("Agent One")).toBeInTheDocument();
expect(screen.getByText("Agent Two")).toBeInTheDocument();
});
});
- it("renders access group options with group prefix", async () => {
+ it("should render access group options with group prefix when popover is opened", async () => {
+ const user = userEvent.setup();
render();
+ await waitFor(() => {
+ expect(mockGetAgentsList).toHaveBeenCalled();
+ });
+
+ await user.click(screen.getByRole("button", { name: /select agents/i }));
+
await waitFor(() => {
expect(screen.getByText("group-a")).toBeInTheDocument();
expect(screen.getByText("group-b")).toBeInTheDocument();
});
});
- it("respects disabled prop", () => {
+ it("should respect disabled prop", () => {
render();
- expect(screen.getByTestId("agent-select")).toHaveAttribute("data-disabled", "true");
+ expect(screen.getByRole("button", { name: /select agents/i })).toBeDisabled();
});
- it("handles API error gracefully", async () => {
+ it("should handle API error gracefully", async () => {
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
mockGetAgentsList.mockRejectedValue(new Error("API error"));
render();
await waitFor(() => {
- expect(consoleSpy).toHaveBeenCalledWith("Error fetching agents:", expect.any(Error));
+ expect(consoleSpy).toHaveBeenCalledWith(
+ "Error fetching agents:",
+ expect.any(Error),
+ );
});
consoleSpy.mockRestore();
});
- it("passes value as flattened selectedValues", async () => {
+ it("should render selected agents and access groups as badges", async () => {
render(
+ />,
);
+
await waitFor(() => {
- const select = screen.getByTestId("select-input");
- // The value should contain agent-1 and group:group-a
- expect(select).toBeInTheDocument();
+ expect(mockGetAgentsList).toHaveBeenCalled();
+ });
+
+ // Labels are resolved from the options list once the fetch completes
+ await waitFor(() => {
+ expect(screen.getByText("Agent One")).toBeInTheDocument();
+ expect(screen.getByText("group-a")).toBeInTheDocument();
});
});
- it("handles null response from API", async () => {
+ it("should handle null response from API without error", async () => {
mockGetAgentsList.mockResolvedValue(null);
render();
await waitFor(() => {
- expect(screen.getByTestId("agent-select")).toHaveAttribute("data-loading", "false");
+ expect(mockGetAgentsList).toHaveBeenCalled();
+ });
+ // Trigger still renders with placeholder
+ expect(screen.getByRole("button", { name: /select agents/i })).toBeInTheDocument();
+ });
+
+ it("should call onChange when an option is selected", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render();
+ await waitFor(() => {
+ expect(mockGetAgentsList).toHaveBeenCalled();
+ });
+
+ await user.click(screen.getByRole("button", { name: /select agents/i }));
+ await user.click(await screen.findByText("Agent One"));
+
+ expect(onChange).toHaveBeenCalledWith({
+ agents: ["agent-1"],
+ accessGroups: [],
});
});
});
diff --git a/ui/litellm-dashboard/src/components/agents/agent_card.tsx b/ui/litellm-dashboard/src/components/agents/agent_card.tsx
index b93b7d34738..5d36c24302c 100644
--- a/ui/litellm-dashboard/src/components/agents/agent_card.tsx
+++ b/ui/litellm-dashboard/src/components/agents/agent_card.tsx
@@ -85,6 +85,7 @@ const AgentCard: React.FC = ({