From ac52d2aeef97c94c8edf288100e9d8cf81252f09 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 11 Dec 2025 17:47:11 -0800 Subject: [PATCH] tests --- .../components/UsagePageView.test.tsx | 87 +++++++++++++++---- .../UsageViewSelect/UsageViewSelect.test.tsx | 70 +++++++++++++++ 2 files changed, 140 insertions(+), 17 deletions(-) diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.test.tsx index 265103b5cc..a34d129a20 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.test.tsx @@ -69,6 +69,60 @@ vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({ default: vi.fn(), })); +vi.mock("antd", async () => { + const React = await import("react"); + + function Select(props: any) { + const { value, onChange, options, ...rest } = props; + return React.createElement( + "select", + { + ...rest, + value, + onChange: (e: any) => onChange?.(e.target.value), + role: "combobox", + }, + options?.map((opt: any) => React.createElement("option", { key: opt.value, value: opt.value }, opt.label)), + ); + } + (Select as any).displayName = "AntdSelect"; + + function Alert(props: any) { + const { message, description, type, closable, onClose, ...rest } = props; + return React.createElement( + "div", + { ...rest, "data-testid": "antd-alert", "data-type": type }, + message && React.createElement("div", null, message), + description && React.createElement("div", null, description), + closable && React.createElement("button", { onClick: onClose, "aria-label": "Close" }, "×"), + ); + } + (Alert as any).displayName = "AntdAlert"; + + return { Select, Alert }; +}); + +vi.mock("@ant-design/icons", async () => { + const React = await import("react"); + + function Icon() { + return React.createElement("span"); + } + + return { + GlobalOutlined: Icon, + BankOutlined: Icon, + TeamOutlined: Icon, + ShoppingCartOutlined: Icon, + TagsOutlined: Icon, + RobotOutlined: Icon, + LineChartOutlined: Icon, + BarChartOutlined: Icon, + ClockCircleOutlined: Icon, + CalendarOutlined: Icon, + }; +}); + describe("NewUsage", () => { const mockUserDailyActivityAggregatedCall = vi.mocked(networking.userDailyActivityAggregatedCall); const mockTagListCall = vi.mocked(networking.tagListCall); @@ -301,20 +355,20 @@ describe("NewUsage", () => { expect(screen.getByText("Top Virtual Keys")).toBeInTheDocument(); }); - it("should switch between tabs correctly", async () => { + it("should switch between usage views correctly", async () => { render(); await waitFor(() => { expect(mockUserDailyActivityAggregatedCall).toHaveBeenCalled(); }); - // Default tab should show Global Usage (for admin) + // Default view should show Global Usage (for admin) expect(screen.getByText("Daily Spend")).toBeInTheDocument(); - // Switch to Team Usage tab - const teamUsageTab = screen.getByText("Team Usage"); + // Switch to Team Usage view + const usageSelect = screen.getByRole("combobox"); act(() => { - fireEvent.click(teamUsageTab); + fireEvent.change(usageSelect, { target: { value: "team" } }); }); // Should render EntityUsage component @@ -323,10 +377,9 @@ describe("NewUsage", () => { expect(entityUsageElements.length).toBeGreaterThan(0); }); - // Switch to Tag Usage tab (admin only) - const tagUsageTab = screen.getByText("Tag Usage"); + // Switch to Tag Usage view (admin only) act(() => { - fireEvent.click(tagUsageTab); + fireEvent.change(usageSelect, { target: { value: "tag" } }); }); // Should still render EntityUsage component for tags @@ -336,16 +389,16 @@ describe("NewUsage", () => { }); }); - it("should show organization usage banner and tab for admins", async () => { + it("should show organization usage banner and view for admins", async () => { render(); await waitFor(() => { expect(mockUserDailyActivityAggregatedCall).toHaveBeenCalled(); }); - const organizationTab = screen.getByText("Organization Usage"); + const usageSelect = screen.getByRole("combobox"); act(() => { - fireEvent.click(organizationTab); + fireEvent.change(usageSelect, { target: { value: "organization" } }); }); await waitFor(() => { @@ -355,7 +408,7 @@ describe("NewUsage", () => { }); }); - it("should show customer usage tab for admins", async () => { + it("should show customer usage view for admins", async () => { mockUseCustomers.mockReturnValue({ data: mockCustomers, isLoading: false, @@ -368,9 +421,9 @@ describe("NewUsage", () => { expect(mockUserDailyActivityAggregatedCall).toHaveBeenCalled(); }); - const customerTab = screen.getByText("Customer Usage"); + const usageSelect = screen.getByRole("combobox"); act(() => { - fireEvent.click(customerTab); + fireEvent.change(usageSelect, { target: { value: "customer" } }); }); await waitFor(() => { @@ -379,7 +432,7 @@ describe("NewUsage", () => { }); }); - it("should show agent usage tab for admins", async () => { + it("should show agent usage view for admins", async () => { mockUseAgents.mockReturnValue({ data: { agents: mockAgents }, isLoading: false, @@ -392,9 +445,9 @@ describe("NewUsage", () => { expect(mockUserDailyActivityAggregatedCall).toHaveBeenCalled(); }); - const agentTab = screen.getByText("Agent Usage"); + const usageSelect = screen.getByRole("combobox"); act(() => { - fireEvent.click(agentTab); + fireEvent.change(usageSelect, { target: { value: "agent" } }); }); await waitFor(() => { diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/UsageViewSelect/UsageViewSelect.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/UsageViewSelect/UsageViewSelect.test.tsx index e69de29bb2..dede260dae 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/UsageViewSelect/UsageViewSelect.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/UsageViewSelect/UsageViewSelect.test.tsx @@ -0,0 +1,70 @@ +import { render, screen, fireEvent, act } from "@testing-library/react"; +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { UsageViewSelect, UsageOption } from "./UsageViewSelect"; + +vi.mock("antd", async () => { + const React = await import("react"); + + function Select(props: any) { + const { value, onChange, options, ...rest } = props; + return React.createElement( + "select", + { + ...rest, + value, + onChange: (e: any) => onChange?.(e.target.value), + role: "combobox", + }, + options?.map((opt: any) => React.createElement("option", { key: opt.value, value: opt.value }, opt.label)), + ); + } + (Select as any).displayName = "AntdSelect"; + + return { Select }; +}); + +vi.mock("@ant-design/icons", async () => { + const React = await import("react"); + + function Icon(props: any) { + return React.createElement("span", { "data-testid": "antd-icon" }); + } + + return { + GlobalOutlined: Icon, + BankOutlined: Icon, + TeamOutlined: Icon, + ShoppingCartOutlined: Icon, + TagsOutlined: Icon, + RobotOutlined: Icon, + LineChartOutlined: Icon, + BarChartOutlined: Icon, + }; +}); + +describe("UsageViewSelect", () => { + const mockOnChange = vi.fn(); + + beforeEach(() => { + mockOnChange.mockClear(); + }); + + it("should render", () => { + render(); + + expect(screen.getByText("Usage View")).toBeInTheDocument(); + expect(screen.getByText("Select the usage data you want to view")).toBeInTheDocument(); + expect(screen.getByRole("combobox")).toBeInTheDocument(); + }); + + it("should call onChange when value changes", () => { + render(); + + const select = screen.getByRole("combobox"); + act(() => { + fireEvent.change(select, { target: { value: "team" } }); + }); + + expect(mockOnChange).toHaveBeenCalledWith("team"); + }); +});