diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx index 3c5101fc2dc..7551553f3d7 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx @@ -1,6 +1,7 @@ /* @vitest-environment jsdom */ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { act, fireEvent, render } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; import ModelsAndEndpointsView from "./ModelsAndEndpointsView"; @@ -24,6 +25,10 @@ Object.defineProperty(window, "localStorage", { value: localStorageMock }); // Minimal stubs to avoid Next.js router and network usage during render vi.mock("@/components/networking", () => ({ + proxyBaseUrl: "", + getGlobalLitellmHeaderName: () => "", + deriveErrorMessage: () => "", + handleError: vi.fn(), credentialListCall: vi.fn().mockResolvedValue({ credentials: [] }), modelInfoCall: vi.fn().mockResolvedValue({ data: [] }), modelCostMap: vi.fn().mockResolvedValue({}), @@ -239,10 +244,9 @@ describe("ModelsAndEndpointsView", () => { , ); + const user = userEvent.setup(); const healthStatusTab = getByRole("tab", { name: "Health Status" }); - await act(async () => { - healthStatusTab.click(); - }); + await user.click(healthStatusTab); expect(mockHealthCheckComponent).toHaveBeenCalled(); const healthCheckProps = mockHealthCheckComponent.mock.calls[0][0]; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/ModelRetrySettingsTab.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/ModelRetrySettingsTab.test.tsx index 5b756a833d8..2ff3f8d7a4d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/ModelRetrySettingsTab.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/ModelRetrySettingsTab.test.tsx @@ -63,7 +63,7 @@ describe("ModelRetrySettingsTab", () => { // All 6 spinbutton inputs should show the defaultRetry value const inputs = screen.getAllByRole("spinbutton"); inputs.forEach((input) => { - expect(input).toHaveValue("3"); + expect(input).toHaveValue(3); }); }); @@ -76,10 +76,10 @@ describe("ModelRetrySettingsTab", () => { // The RateLimitError row is the 4th entry in the map const inputs = screen.getAllByRole("spinbutton"); const rateLimitInput = inputs[3]; // 0-indexed: Bad(0), Auth(1), Timeout(2), Rate(3) - expect(rateLimitInput).toHaveValue("5"); + expect(rateLimitInput).toHaveValue(5); // Unset entries fall back to defaultRetry (0) - expect(inputs[0]).toHaveValue("0"); + expect(inputs[0]).toHaveValue(0); }); it("should fall back to globalRetryPolicy when no model-specific value is set (model scope)", () => { @@ -99,10 +99,10 @@ describe("ModelRetrySettingsTab", () => { // The TimeoutError row is 3rd (index 2) const inputs = screen.getAllByRole("spinbutton"); - expect(inputs[2]).toHaveValue("7"); + expect(inputs[2]).toHaveValue(7); // Rows without a global value fall back to defaultRetry - expect(inputs[0]).toHaveValue("1"); + expect(inputs[0]).toHaveValue(1); }); it("should prefer model-specific retry count over the global value (model scope)", () => { @@ -125,7 +125,7 @@ describe("ModelRetrySettingsTab", () => { // The model-specific value (9) should win over global (3) const inputs = screen.getAllByRole("spinbutton"); - expect(inputs[3]).toHaveValue("9"); + expect(inputs[3]).toHaveValue(9); }); it("should show the global reference value text for each row in model-specific scope", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/organizations/OrganizationFilters.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/organizations/OrganizationFilters.test.tsx index 814625ff6be..bc200e88e8f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/organizations/OrganizationFilters.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/organizations/OrganizationFilters.test.tsx @@ -119,7 +119,9 @@ describe("OrganizationFilters", () => { ); const filtersButton = screen.getByRole("button", { name: /^filters$/i }); - const badgeWrapper = filtersButton.closest(".ant-badge"); - expect(badgeWrapper).toBeInTheDocument(); + const wrapper = filtersButton.parentElement; + expect(wrapper).not.toBeNull(); + const badgeIndicator = wrapper!.querySelector("span.rounded-full"); + expect(badgeIndicator).toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsPage.test.tsx b/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsPage.test.tsx index 6aa35f349dd..7e70ef0cdda 100644 --- a/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsPage.test.tsx +++ b/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsPage.test.tsx @@ -237,7 +237,7 @@ describe("AccessGroupsPage", () => { name: "Delete access group", }); await user.click(deleteButtons[0]); - const dialog = screen.getByRole("dialog", { name: "Delete Access Group" }); + const dialog = await screen.findByRole("alertdialog", { name: "Delete Access Group" }); expect(dialog).toBeInTheDocument(); expect( within(dialog).getByText( @@ -256,9 +256,9 @@ describe("AccessGroupsPage", () => { name: "Delete access group", }); await user.click(deleteButtons[0]); - const dialog = screen.getByRole("dialog", { name: "Delete Access Group" }); + const dialog = await screen.findByRole("alertdialog", { name: "Delete Access Group" }); await user.click(within(dialog).getByRole("button", { name: "Cancel" })); - expect(screen.queryByRole("dialog", { name: "Delete Access Group" })).not.toBeInTheDocument(); + expect(screen.queryByRole("alertdialog", { name: "Delete Access Group" })).not.toBeInTheDocument(); }); it("should call delete mutation when delete is confirmed", async () => { @@ -271,7 +271,7 @@ describe("AccessGroupsPage", () => { name: "Delete access group", }); await user.click(deleteButtons[0]); - const dialog = screen.getByRole("dialog", { name: "Delete Access Group" }); + const dialog = await screen.findByRole("alertdialog", { name: "Delete Access Group" }); const deleteConfirmButton = within(dialog).getByRole("button", { name: /delete/i }); await user.click(deleteConfirmButton); expect(mockMutate).toHaveBeenCalledWith("ag-1", expect.any(Object)); diff --git a/ui/litellm-dashboard/src/components/CostTrackingSettings/pricing_calculator/multi_export_dropdown.test.tsx b/ui/litellm-dashboard/src/components/CostTrackingSettings/pricing_calculator/multi_export_dropdown.test.tsx index be1a89cf77f..9b3f87e7962 100644 --- a/ui/litellm-dashboard/src/components/CostTrackingSettings/pricing_calculator/multi_export_dropdown.test.tsx +++ b/ui/litellm-dashboard/src/components/CostTrackingSettings/pricing_calculator/multi_export_dropdown.test.tsx @@ -1,6 +1,6 @@ import React from "react"; import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import { screen, fireEvent } from "@testing-library/react"; +import { screen, fireEvent, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { renderWithProviders } from "../../../../tests/test-utils"; import MultiExportDropdown from "./multi_export_dropdown"; @@ -91,8 +91,10 @@ describe("MultiExportDropdown", () => { await user.click(screen.getByRole("button", { name: /^export$/i })); expect(screen.getByText("Export as PDF")).toBeInTheDocument(); - await user.click(screen.getByRole("button", { name: /^export$/i })); - expect(screen.queryByText("Export as PDF")).not.toBeInTheDocument(); + await user.keyboard("{Escape}"); + await waitFor(() => { + expect(screen.queryByText("Export as PDF")).not.toBeInTheDocument(); + }); }); it("should call exportMultiToPDF and close the menu when Export as PDF is clicked", async () => { @@ -140,7 +142,13 @@ describe("MultiExportDropdown", () => { await user.click(screen.getByRole("button", { name: /^export$/i })); expect(screen.getByText("Export as PDF")).toBeInTheDocument(); - fireEvent.mouseDown(screen.getByTestId("outside")); - expect(screen.queryByText("Export as PDF")).not.toBeInTheDocument(); + fireEvent.pointerDown(screen.getByTestId("outside"), { + button: 0, + ctrlKey: false, + pointerType: "mouse", + }); + await waitFor(() => { + expect(screen.queryByText("Export as PDF")).not.toBeInTheDocument(); + }); }); }); diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx index 793e4c6e3cf..4ebef6f0d2f 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx @@ -2,35 +2,6 @@ import { render, screen } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import EndpointUsageTable from "./EndpointUsageTable"; -vi.mock("antd", async () => { - const React = await import("react"); - - function Table({ columns, dataSource }: any) { - return React.createElement( - "div", - { "data-testid": "antd-table" }, - columns?.map((col: any) => - React.createElement("div", { key: col.key, "data-testid": `column-${col.key}` }, col.title), - ), - dataSource?.map((row: any) => - React.createElement( - "div", - { key: row.key, "data-testid": `row-${row.key}` }, - React.createElement("div", null, row.endpoint), - ), - ), - ); - } - (Table as any).displayName = "Table"; - - function Progress({ percent }: any) { - return React.createElement("div", { "data-testid": "antd-progress", "data-percent": percent }); - } - (Progress as any).displayName = "Progress"; - - return { Table, Progress }; -}); - vi.mock("@/utils/dataUtils", () => ({ formatNumberWithCommas: (value: number, decimals?: number) => { return value.toFixed(decimals || 0); @@ -59,12 +30,17 @@ describe("EndpointUsageTable", () => { render(); - expect(screen.getByTestId("antd-table")).toBeInTheDocument(); - expect(screen.getByTestId("column-endpoint")).toBeInTheDocument(); - expect(screen.getByTestId("column-requests")).toBeInTheDocument(); - expect(screen.getByTestId("column-api_requests")).toBeInTheDocument(); - expect(screen.getByTestId("column-successRate")).toBeInTheDocument(); - expect(screen.getByTestId("column-total_tokens")).toBeInTheDocument(); - expect(screen.getByTestId("column-spend")).toBeInTheDocument(); + expect(screen.getByRole("table")).toBeInTheDocument(); + expect(screen.getByRole("columnheader", { name: /Endpoint/i })).toBeInTheDocument(); + expect(screen.getByRole("columnheader", { name: /Successful \/ Failed/i })).toBeInTheDocument(); + expect(screen.getByRole("columnheader", { name: /Total Request/i })).toBeInTheDocument(); + expect(screen.getByRole("columnheader", { name: /Success Rate/i })).toBeInTheDocument(); + expect(screen.getByRole("columnheader", { name: /Total Tokens/i })).toBeInTheDocument(); + expect(screen.getByRole("columnheader", { name: /Spend/i })).toBeInTheDocument(); + + expect(screen.getByText("endpoint-1")).toBeInTheDocument(); + expect(screen.getByText("95.00%")).toBeInTheDocument(); + expect(screen.getByText("8,000")).toBeInTheDocument(); + expect(screen.getByText(/\$\s*100\.50/)).toBeInTheDocument(); }); });