From 6670061a33dd19328e2fdd2e76815a4dd50ca41b Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Sat, 15 Aug 2026 02:24:12 -0700 Subject: [PATCH] test(ui): assert presence and state so dashboard tests fail when behaviour breaks One test could not fail: it asserted on the Promise returned by an unawaited findAllByText, and a pending Promise is always truthy, so the organization detail view had no working coverage at all. Rewrites 316 assertions across 128 files onto matchers that describe what the user perceives, and awaits the queries that were being compared as Promises. Most of this was mechanical, but the fixers behind these rules are not trustworthy, so every site they damaged was repaired by hand. The quiet one worth naming: prefer-to-have-text-content wraps strings in new RegExp() without escaping, turning toContain("100K+ requests") into a pattern meaning "100 followed by one-or-more K". That compiles, lints clean, and keeps passing while no longer asserting what it claims. --- .../agentControlPlaneView.test.tsx | 4 +- .../agents/_components/AgentsTable.test.tsx | 10 ++-- .../_components/agent_card_discovery.test.tsx | 10 ++-- .../_components/APIReferenceView.test.tsx | 18 +++--- .../budgets/_components/BudgetTable.test.tsx | 4 +- .../CostOptimizationView.activity.test.tsx | 4 +- .../_components/UsageTab.test.tsx | 18 +++--- .../_components/add_margin_form.test.tsx | 4 +- .../_components/add_provider_form.test.tsx | 4 +- .../cost_tracking_settings.test.tsx | 2 +- .../_components/how_it_works.test.tsx | 2 +- .../multi_export_dropdown.test.tsx | 2 +- .../provider_margin_table.test.tsx | 2 +- .../GuardrailsMonitorView.test.tsx | 4 +- .../_components/add_guardrail_form.test.tsx | 2 +- .../ContentFilterManager.test.tsx | 2 +- .../guardrail_garden_card.test.tsx | 2 +- .../_components/guardrail_info.test.tsx | 2 +- .../_components/guardrail_table.test.tsx | 2 +- .../src/app/(dashboard)/layout.test.tsx | 18 +++--- .../CreateMCPServer.integration.test.tsx | 2 +- .../_components/MCPLogoSelector.test.tsx | 2 +- .../MCPPermissionManagement.test.tsx | 6 +- .../_components/MCPServerCard.test.tsx | 4 +- .../mcp_connection_status.test.tsx | 2 +- .../_components/mcp_server_edit.test.tsx | 23 ++++--- .../models-and-endpoints/page.test.tsx | 10 ++-- .../chat_ui/AdditionalModelSettings.test.tsx | 6 +- .../chat_ui/AgentBuilderView.test.tsx | 4 +- .../components/chat_ui/ChatComposer.test.tsx | 2 +- .../components/chat_ui/ChatUI.test.tsx | 20 +++---- .../chat_ui/CodeInterpreterOutput.test.tsx | 2 +- .../chat_ui/RealtimePlayground.test.tsx | 4 +- .../components/compareUI/CompareUI.test.tsx | 4 +- .../components/MessageInput.test.tsx | 4 +- .../_components/AttachmentTable.test.tsx | 6 +- .../_components/ai_suggestion_modal.test.tsx | 2 +- .../policies/_components/index.test.tsx | 2 +- .../template_parameter_modal.test.tsx | 4 +- .../_components/CreateSearchTools.test.tsx | 9 ++- .../_components/SearchToolTester.test.tsx | 12 ++-- .../_components/add_plugin_form.test.tsx | 2 +- .../TransformRequestPanel.test.tsx | 8 +-- .../EndpointUsageLineChart.test.tsx | 4 +- .../EntityUsage/EntityUsage.test.tsx | 4 +- .../users/_components/user_edit_view.test.tsx | 4 +- .../users/_components/view_users.test.tsx | 2 +- .../view_users/UsersTable.test.tsx | 4 +- .../_components/CreateVectorStore.test.tsx | 6 +- .../_components/IndexesTable.test.tsx | 2 +- .../_components/VectorStoreForm.test.tsx | 2 +- .../src/app/chat/page.integration.test.tsx | 6 +- .../AIHub/UsefulLinksManagement.test.tsx | 12 ++-- .../AIHub/forms/MakeAgentPublicForm.test.tsx | 4 +- .../AIHub/forms/MakeMCPPublicForm.test.tsx | 4 +- .../AIHub/forms/MakeModelPublicForm.test.tsx | 4 +- .../src/components/BetaBadge.test.tsx | 2 +- .../src/components/DashboardHeader.test.tsx | 4 +- .../Navbar/BlogDropdown/BlogDropdown.test.tsx | 20 +++---- .../components/Navbar/ViewSwitcher.test.tsx | 14 ++--- .../src/components/SSOModals.test.tsx | 18 ++---- .../MCPSemanticFilterSettings.test.tsx | 2 +- .../MCPSemanticFilterTestPanel.test.tsx | 2 +- .../Modals/BaseSSOSettingsForm.test.tsx | 42 +++++-------- .../SSOSettings/RoleMappings.test.tsx | 2 +- .../Fallbacks/AddFallbacks.test.tsx | 2 +- .../Fallbacks/Fallbacks.test.tsx | 2 +- .../src/components/TeamSSOSettings.test.tsx | 2 +- .../components/TeamsPage/TeamsTable.test.tsx | 2 +- .../EntityUsage/TopKeyView.test.tsx | 2 +- .../components/KeyModelUsageView.test.tsx | 2 +- .../VirtualKeysPage/VirtualKeysTable.test.tsx | 10 +--- ...lassifierPromptEditor.integration.test.tsx | 4 +- .../add_model/add_auto_router_tab.test.tsx | 12 ++-- .../auto_router_connection_test.test.tsx | 4 +- .../add_model/litellm_model_name.test.tsx | 2 +- .../provider_specific_fields.test.tsx | 60 +++++++++---------- .../chat/ConnectFlowBanner.test.tsx | 4 +- .../src/components/chat/MCPAppsPanel.test.tsx | 8 +-- .../components/chat/MCPConnectPicker.test.tsx | 4 +- .../DefaultProxyAdminTag.test.tsx | 2 +- .../DeleteResourceModal.test.tsx | 2 +- .../KeyLifecycleSettings.test.tsx | 10 ++-- .../common_components/NewBadge.test.tsx | 2 +- .../common_components/chartUtils.test.tsx | 4 +- .../routerSettingsWiring.test.tsx | 4 +- .../edit_auto_router_modal.test.tsx | 1 + .../src/components/email_settings.test.tsx | 4 +- .../BudgetFallbacksEditor.test.tsx | 18 +++--- .../src/components/leftnav.test.tsx | 4 +- .../components/logging_settings_view.test.tsx | 2 +- .../model_add/CredentialModal.test.tsx | 6 +- .../HealthCheckComponent.test.tsx | 4 +- .../src/components/model_info_view.test.tsx | 6 +- ...cost_optimization_feedback_banner.test.tsx | 4 +- .../components/molecules/logo/Logo.test.tsx | 10 ++-- .../src/components/navbar.test.tsx | 2 +- .../organisms/create_key_button.test.tsx | 2 +- .../organization/organization_view.test.tsx | 5 +- .../src/components/per_user_usage.test.tsx | 6 +- .../src/components/settings.test.tsx | 2 +- .../shared/DataTable/DataTable.test.tsx | 10 ++-- .../DataTable/DataTableFilterDrawer.test.tsx | 4 +- .../DataTable/DataTableSortHeader.test.tsx | 2 +- .../DataTable/DataTableToolbar.test.tsx | 4 +- .../shared/charts/area_chart.test.tsx | 4 +- .../shared/charts/bar_chart.test.tsx | 2 +- .../shared/charts/chart_tooltip.test.tsx | 6 +- .../components/shared/form/FormField.test.tsx | 2 +- .../shared/table_cells/id_cell.test.tsx | 14 ++--- .../shared/table_cells/identity_cell.test.tsx | 6 +- .../components/team/LoggingSettings.test.tsx | 2 +- .../src/components/team/TeamInfo.test.tsx | 2 +- .../team/TeamVirtualKeysTable.test.tsx | 2 +- .../templates/KeyInfoHeader.test.tsx | 2 +- .../components/user_agent_activity.test.tsx | 6 +- .../VectorStoreSelector.test.tsx | 6 +- .../view_logs/AuditLogsTable.test.tsx | 2 +- .../view_logs/ConfigInfoMessage.test.tsx | 2 +- .../CollapsibleMessage.test.tsx | 4 +- .../LogDetailsDrawer/HistoryTree.test.tsx | 2 +- .../LogDetailsDrawer/InputCard.test.tsx | 2 +- .../LogDetailsDrawer.test.tsx | 2 +- .../SimpleMessageBlock.test.tsx | 4 +- .../view_logs/RequestLogsPanel.test.tsx | 29 ++++----- .../RequestLogsTableColumns.test.tsx | 2 +- .../src/contexts/PluginModeContext.test.tsx | 10 ++-- .../src/utils/dataUtils.test.ts | 2 + 128 files changed, 371 insertions(+), 404 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/agentControlPlaneView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agentControlPlaneView.test.tsx index 26de1eed7b8..3e3b3d2b8bf 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/agentControlPlaneView.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agentControlPlaneView.test.tsx @@ -33,7 +33,7 @@ describe("AgentControlPlaneView iframe", () => { expect(src).toBe("http://localhost:3300/"); expect(src).not.toContain("/sessions"); // title comes from the plugin's display_name, not a hardcoded label - expect(iframe!.getAttribute("title")).toBe("Chat UI"); + expect(iframe!).toHaveAttribute("title", "Chat UI"); }); it("does not double the slash when the plugin url has a trailing slash", () => { @@ -43,7 +43,7 @@ describe("AgentControlPlaneView iframe", () => { url: "http://localhost:3300/", }; const { container } = render(); - expect(container.querySelector("iframe")!.getAttribute("src")).toBe("http://localhost:3300/"); + expect(container.querySelector("iframe")!).toHaveAttribute("src", "http://localhost:3300/"); pluginModeValue.activePlugin = { name: "litellm-platform-plugin", display_name: "Chat UI", diff --git a/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsTable.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsTable.test.tsx index f86ef16e836..06099a9fc22 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsTable.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/AgentsTable.test.tsx @@ -101,8 +101,8 @@ describe("AgentsTable", () => { ); const bodyRows = screen.getAllByRole("row").slice(1); - expect(bodyRows[0].textContent).toContain("Beta Agent"); - expect(bodyRows[1].textContent).toContain("Alpha Agent"); + expect(bodyRows[0]).toHaveTextContent(/Beta Agent/); + expect(bodyRows[1]).toHaveTextContent(/Alpha Agent/); }); it("sorts agents with no created_at last, never ahead of dated ones", () => { @@ -118,9 +118,9 @@ describe("AgentsTable", () => { ); const bodyRows = screen.getAllByRole("row").slice(1); - expect(bodyRows[0].textContent).toContain("Beta Agent"); - expect(bodyRows[1].textContent).toContain("Alpha Agent"); - expect(bodyRows[2].textContent).toContain("Undated Agent"); + expect(bodyRows[0]).toHaveTextContent(/Beta Agent/); + expect(bodyRows[1]).toHaveTextContent(/Alpha Agent/); + expect(bodyRows[2]).toHaveTextContent(/Undated Agent/); }); it("shows a rich empty state when there are no agents", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx index 7858bdb1cd4..e3c5e1dd94f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx @@ -196,12 +196,10 @@ describe("AgentCardDiscovery", () => { ); expect( - ( - screen.getByRole("button", { - name: /discover/i, - }) as HTMLButtonElement - ).disabled, - ).toBe(true); + screen.getByRole("button", { + name: /discover/i, + }), + ).toBeDisabled(); expect(mockDiscover).not.toHaveBeenCalled(); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx index ad1cf28dc54..b2e0a42eecb 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/_components/APIReferenceView.test.tsx @@ -16,7 +16,7 @@ describe("APIReferenceView", () => { const { getAllByTestId } = render(); const codeBlocks = getAllByTestId(codeBlockTestId); - expect(codeBlocks[0].textContent).toContain(apiDocUrl); + expect(codeBlocks[0]).toHaveTextContent(new RegExp(apiDocUrl)); }); it("falls back to the proxy base url when the docs url is missing", () => { @@ -24,7 +24,7 @@ describe("APIReferenceView", () => { const { getAllByTestId } = render(); const codeBlocks = getAllByTestId(codeBlockTestId); - expect(codeBlocks[0].textContent).toContain(proxyUrl); + expect(codeBlocks[0]).toHaveTextContent(new RegExp(proxyUrl)); }); it("prefers the docs url when both urls are provided", () => { @@ -49,12 +49,12 @@ describe("APIReferenceView", () => { it("renders the page title, blurb and docs link", () => { render(); - expect(screen.getByText("OpenAI Compatible Proxy: API Reference")).toBeTruthy(); - expect(screen.getByText(/LiteLLM is OpenAI Compatible/)).toBeTruthy(); + expect(screen.getByText("OpenAI Compatible Proxy: API Reference")).toBeInTheDocument(); + expect(screen.getByText(/LiteLLM is OpenAI Compatible/)).toBeInTheDocument(); const docsLink = screen.getByRole("link", { name: /API Reference Docs/ }); - expect(docsLink.getAttribute("href")).toBe("https://docs.litellm.ai/docs/proxy/user_keys"); - expect(docsLink.getAttribute("target")).toBe("_blank"); + expect(docsLink).toHaveAttribute("href", "https://docs.litellm.ai/docs/proxy/user_keys"); + expect(docsLink).toHaveAttribute("target", "_blank"); }); it("exposes the three SDK tabs with the first selected by default", () => { @@ -83,10 +83,10 @@ describe("APIReferenceView", () => { await user.click(screen.getByRole("tab", { name: tabName })); - expect(screen.getByRole("tab", { name: tabName }).getAttribute("aria-selected")).toBe("true"); + expect(screen.getByRole("tab", { name: tabName })).toHaveAttribute("aria-selected", "true"); const selectedPanel = screen.getByRole("tabpanel"); - expect(selectedPanel.textContent).toContain(marker); - expect(selectedPanel.textContent).toContain(proxyUrl); + expect(selectedPanel).toHaveTextContent(new RegExp(marker)); + expect(selectedPanel).toHaveTextContent(new RegExp(proxyUrl)); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/BudgetTable.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/BudgetTable.test.tsx index f78ba34770d..c425e766f2d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/BudgetTable.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/BudgetTable.test.tsx @@ -105,7 +105,7 @@ describe("BudgetTable", () => { const list = makeList({ rows: [makeBudget({ budget_id: budgetId })] }); renderWithProviders(); const idCell = screen.getByText(budgetId); - expect(idCell.className).not.toContain("truncate"); + expect(idCell).not.toHaveClass("truncate"); expect(idCell.className).not.toMatch(/max-w-\[\d+(ch|rem|px)\]/); }); @@ -113,7 +113,7 @@ describe("BudgetTable", () => { const budgetId = "ecc1869c-6231-4380-a56d-1a0be457477d"; const list = makeList({ rows: [makeBudget({ budget_id: budgetId })] }); renderWithProviders(); - expect(screen.getByText(budgetId).className).toContain("whitespace-nowrap"); + expect(screen.getByText(budgetId)).toHaveClass("whitespace-nowrap"); }); it("should copy the budget id from the cell's copy button", async () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.activity.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.activity.test.tsx index 96502cac953..70d7dade97a 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.activity.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.activity.test.tsx @@ -52,7 +52,7 @@ describe("CostOptimizationView daily activity", () => { useAuthorizedMock.mockReturnValue({ accessToken: "test-token", userId: "u1", userRole: "proxy_admin" }); const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); - const { getByRole, getByTestId } = render( + const { getByRole, getByTestId, findByTestId } = render( , @@ -61,7 +61,7 @@ describe("CostOptimizationView daily activity", () => { await waitFor(() => expect(mockUserDailyActivityCall).toHaveBeenCalledTimes(1)); fireEvent.click(getByRole("tab", { name: "Prompt Caching" })); - await waitFor(() => expect(getByTestId("caching-settings")).toBeInTheDocument()); + await findByTestId("caching-settings"); expect(mockUserDailyActivityCall).toHaveBeenCalledTimes(1); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx index 25be956f18b..20d57754857 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx @@ -218,7 +218,7 @@ describe("UsageTab", () => { // Per day switches to a bar chart of the unaccumulated daily savings, with no // synthetic anchor prepended. - expect(queryByTestId("area-chart")).toBeNull(); + expect(queryByTestId("area-chart")).not.toBeInTheDocument(); const series = readSeries(getByTestId("bar-chart")); expect(series).toHaveLength(2); expect(series[0]).toMatchObject({ Compression: 0.04, "Prompt caching": 0.006 }); @@ -264,7 +264,7 @@ describe("UsageTab", () => { await userEvent.click(getByRole("tab", { name: "Per day" })); const bars = getByTestId("bar-chart"); - expect(bars.getAttribute("data-stack")).toBe("false"); + expect(bars).toHaveAttribute("data-stack", "false"); expect(readSeries(bars)[0]).toMatchObject({ "Auto-router": -0.05 }); }); @@ -290,7 +290,7 @@ describe("UsageTab", () => { expect(before.action.contains(getByRole("tablist"))).toBe(true); // the subtitle lives outside that slot, so its length cannot reposition the controls expect(before.action.contains(before.description)).toBe(false); - expect(before.description.textContent).toContain("Running total saved"); + expect(before.description).toHaveTextContent(/Running total saved/); await userEvent.click(getByRole("tab", { name: "Per day" })); @@ -298,8 +298,8 @@ describe("UsageTab", () => { expect(after.action).toBe(before.action); expect(after.cardHeader).toBe(before.cardHeader); expect(after.action.contains(after.description)).toBe(false); - expect(after.description.textContent).toContain("Saved per day"); - expect(container.textContent).toContain("Savings"); + expect(after.description).toHaveTextContent(/Saved per day/); + expect(container).toHaveTextContent(/Savings/); }); it("subtracts a losing auto-router route from the total and keeps it out of the donut", () => { @@ -319,7 +319,7 @@ describe("UsageTab", () => { const slices = JSON.parse(getByTestId("donut-chart").getAttribute("data-slices") ?? "[]"); expect(slices.map((d: { driver: string }) => d.driver)).toEqual(["Compression", "Prompt caching"]); - expect(getByTestId("donut-chart").getAttribute("data-label")).toBe("$0.1200"); + expect(getByTestId("donut-chart")).toHaveAttribute("data-label", "$0.1200"); }); it("carries auto-router savings into the summary card, donut slice, and cumulative series", () => { @@ -370,7 +370,7 @@ describe("UsageTab", () => { expect(series[0]).toMatchObject({ tool_name: "search", spend: 4.0 }); // The 64px bar cap is this card's opt-in; the shared BarChart must not cap // by default (other consumers keep their pre-existing geometry). - expect(bars[0].getAttribute("data-max-bar-size")).toBe("64"); + expect(bars[0]).toHaveAttribute("data-max-bar-size", "64"); }); it("renders the tool legend once outside the charts, with both charts sharing the tool colors", async () => { @@ -387,8 +387,8 @@ describe("UsageTab", () => { const bars = await findAllByTestId("bar-chart"); const [totalByTool, dailyByTool] = bars.slice(-2); - expect(dailyByTool.getAttribute("data-show-legend")).toBe("false"); - expect(totalByTool.getAttribute("data-colors")).toBe(dailyByTool.getAttribute("data-colors")); + expect(dailyByTool).toHaveAttribute("data-show-legend", "false"); + expect(totalByTool).toHaveAttribute("data-colors", dailyByTool.getAttribute("data-colors")); const toolLegends = getAllByTestId("chart-legend").filter((legend) => legend.textContent === "search,read_file"); expect(toolLegends).toHaveLength(1); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/add_margin_form.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/add_margin_form.test.tsx index 1ededd9e4b1..63a0b3c355f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/add_margin_form.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/add_margin_form.test.tsx @@ -67,7 +67,7 @@ describe("AddMarginForm", () => { it("should enable the submit button when provider and percentage value are both provided", () => { renderWithProviders(); - expect(screen.getByRole("button", { name: /add provider margin/i })).not.toBeDisabled(); + expect(screen.getByRole("button", { name: /add provider margin/i })).toBeEnabled(); }); it("should disable the submit button in fixed mode when no fixed amount is provided", () => { @@ -81,7 +81,7 @@ describe("AddMarginForm", () => { renderWithProviders( , ); - expect(screen.getByRole("button", { name: /add provider margin/i })).not.toBeDisabled(); + expect(screen.getByRole("button", { name: /add provider margin/i })).toBeEnabled(); }); it("should call onAddProvider when the enabled submit button is clicked", async () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/add_provider_form.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/add_provider_form.test.tsx index 08fb63c32b9..084233b3670 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/add_provider_form.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/add_provider_form.test.tsx @@ -48,7 +48,7 @@ describe("AddProviderForm", () => { it("should enable the submit button when both a provider and a discount value are provided", () => { renderWithProviders(); - expect(screen.getByRole("button", { name: /add provider discount/i })).not.toBeDisabled(); + expect(screen.getByRole("button", { name: /add provider discount/i })).toBeEnabled(); }); it("should call onAddProvider when the enabled submit button is clicked", async () => { @@ -71,7 +71,7 @@ describe("AddProviderForm", () => { renderWithProviders(); const logo = await screen.findByRole("img", { name: `${Providers.OpenAI} logo` }); - expect(logo.getAttribute("src")).toBe(providerLogoMap[Providers.OpenAI]); + expect(logo).toHaveAttribute("src", providerLogoMap[Providers.OpenAI]); }); it("falls back to a letter avatar for a selected provider that has no bundled logo", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.test.tsx index 03cef2a66b8..9cdc8509fbf 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.test.tsx @@ -73,7 +73,7 @@ describe("CostTrackingSettings", () => { const { container } = renderWithProviders( , ); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should render the page title", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/how_it_works.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/how_it_works.test.tsx index a574f4b628e..6739beb81d9 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/how_it_works.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/how_it_works.test.tsx @@ -32,7 +32,7 @@ describe("HowItWorks", () => { it("should render the code block with a curl example", () => { renderWithProviders(); expect(screen.getByTestId("code-block")).toBeInTheDocument(); - expect(screen.getByTestId("code-block").textContent).toContain("curl"); + expect(screen.getByTestId("code-block")).toHaveTextContent(/curl/); }); it("should show the response header names for discount verification", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.test.tsx index 1c6800de7d2..52d5a3f9007 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.test.tsx @@ -63,7 +63,7 @@ describe("MultiExportDropdown", () => { it("should not render anything when no entries have results", () => { const { container } = renderWithProviders(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should render the Export button when at least one entry has a result", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/provider_margin_table.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/provider_margin_table.test.tsx index dd478571568..3663783347d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/provider_margin_table.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/provider_margin_table.test.tsx @@ -68,7 +68,7 @@ describe("ProviderMarginTable", () => { />, ); const logo = screen.getByRole("img", { name: `${Providers.OpenAI} logo` }); - expect(logo.getAttribute("src")).toBe(providerLogoMap[Providers.OpenAI]); + expect(logo).toHaveAttribute("src", providerLogoMap[Providers.OpenAI]); }); it("should fall back to a letter avatar for a provider with no bundled logo", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.test.tsx index b946749bf65..9f27daab6b3 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailsMonitorView.test.tsx @@ -32,7 +32,7 @@ describe("GuardrailsMonitorView", () => { render(, { wrapper }); - expect(await screen.findByRole("heading", { name: /Guardrails Monitor/i })).toBeDefined(); + expect(await screen.findByRole("heading", { name: /Guardrails Monitor/i })).toBeInTheDocument(); await waitFor(() => { expect(mockGetGuardrailsUsageOverview).toHaveBeenCalled(); }); @@ -40,6 +40,6 @@ describe("GuardrailsMonitorView", () => { it("should render without crashing when accessToken is null", async () => { render(, { wrapper }); - expect(await screen.findByRole("heading", { name: /Guardrails Monitor/i })).toBeDefined(); + expect(await screen.findByRole("heading", { name: /Guardrails Monitor/i })).toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/add_guardrail_form.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/add_guardrail_form.test.tsx index a386fca598e..e8463025e31 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/add_guardrail_form.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/add_guardrail_form.test.tsx @@ -53,6 +53,6 @@ describe("AddGuardrailForm provider options", () => { fireEvent.mouseDown(screen.getByLabelText("Guardrail Provider")); const logo = await screen.findByAltText("Presidio PII logo"); - expect(logo.getAttribute("src")).toContain("microsoft_azure.svg"); + expect(logo).toHaveAttribute("src", expect.stringContaining("microsoft_azure.svg")); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/ContentFilterManager.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/ContentFilterManager.test.tsx index c3fd2e3eb59..83ed695f34a 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/ContentFilterManager.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/ContentFilterManager.test.tsx @@ -124,7 +124,7 @@ describe("ContentFilterManager", () => { expect(screen.queryByTestId("content-filter-config")).not.toBeInTheDocument(); expect(screen.queryByTestId("content-filter-display")).not.toBeInTheDocument(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should render read-only display when isEditing is false", async () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_garden_card.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_garden_card.test.tsx index 2d1f35e456c..d81eaa5a4fc 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_garden_card.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_garden_card.test.tsx @@ -56,7 +56,7 @@ describe("GuardrailCard", () => { it("should render the logo through the shared Logo component with the card src", () => { render(); const img = screen.getByAltText("Test Guardrail logo"); - expect(img.getAttribute("src")).toContain("/logos/test.svg"); + expect(img).toHaveAttribute("src", expect.stringContaining("/logos/test.svg")); }); it("should pass a bundled static-import src through unchanged", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_info.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_info.test.tsx index aae1998644a..eca1cf4df8a 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_info.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_info.test.tsx @@ -109,7 +109,7 @@ describe("Guardrail Info", () => { ); const logo = await findByAltText("Presidio PII logo"); - expect(logo.getAttribute("src")).toContain("microsoft_azure.svg"); + expect(logo).toHaveAttribute("src", expect.stringContaining("microsoft_azure.svg")); }); it("should not render the edit button for config guardrails", async () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_table.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_table.test.tsx index 7612b702391..ee619dc7468 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_table.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/guardrail_table.test.tsx @@ -33,7 +33,7 @@ describe("GuardrailTable", () => { it("renders the provider logo from the bundled guardrail logo map", () => { render(); const logo = screen.getByAltText("Presidio PII logo"); - expect(logo.getAttribute("src")).toContain("microsoft_azure.svg"); + expect(logo).toHaveAttribute("src", expect.stringContaining("microsoft_azure.svg")); }); it("falls back to a letter avatar for an unknown provider slug", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx index 340077e9569..7f1f4cc4bd5 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx @@ -82,15 +82,15 @@ describe("(dashboard) Layout", () => { , ); - await waitFor(() => expect(screen.getByTestId("loading-screen")).toBeTruthy()); - expect(screen.queryByTestId("page-content")).toBeNull(); - expect(screen.queryByTestId("dashboard-header")).toBeNull(); + expect(await screen.findByTestId("loading-screen")).toBeInTheDocument(); + expect(screen.queryByTestId("page-content")).not.toBeInTheDocument(); + expect(screen.queryByTestId("dashboard-header")).not.toBeInTheDocument(); pendingUiConfig.resolve(); - await waitFor(() => expect(screen.getByTestId("page-content")).toBeTruthy()); - expect(screen.getByTestId("dashboard-header")).toBeTruthy(); - expect(screen.queryByTestId("loading-screen")).toBeNull(); + expect(await screen.findByTestId("page-content")).toBeInTheDocument(); + expect(screen.getByTestId("dashboard-header")).toBeInTheDocument(); + expect(screen.queryByTestId("loading-screen")).not.toBeInTheDocument(); }); it("redirects an invitation link to the onboarding route instead of rendering the dashboard shell", async () => { @@ -109,8 +109,8 @@ describe("(dashboard) Layout", () => { await waitFor(() => expect(replaceMock).toHaveBeenCalledWith(expect.stringContaining("/onboarding?invitation_id=abc123")), ); - expect(screen.queryByTestId("page-content")).toBeNull(); - expect(screen.queryByTestId("dashboard-header")).toBeNull(); - expect(screen.queryByTestId("sidebar")).toBeNull(); + expect(screen.queryByTestId("page-content")).not.toBeInTheDocument(); + expect(screen.queryByTestId("dashboard-header")).not.toBeInTheDocument(); + expect(screen.queryByTestId("sidebar")).not.toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.integration.test.tsx index c9007e29c3e..f08aacb5522 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.integration.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/CreateMCPServer.integration.test.tsx @@ -676,7 +676,7 @@ describe("CreateMCPServer", () => { it("clears the DCR ref and the upstream warning when the modal closes so nothing leaks to the next session", async () => { const { rerender } = render(); await selectAntOption("Transport Type", "Streamable HTTP"); - await waitFor(() => expect(screen.getByPlaceholderText("https://your-mcp-server.com")).toBeInTheDocument()); + expect(await screen.findByPlaceholderText("https://your-mcp-server.com")).toBeInTheDocument(); const user = userEvent.setup({ delay: null }); await user.type(getServerNameInput(), "Leak_Server"); await user.type(screen.getByPlaceholderText("https://your-mcp-server.com"), "https://example.com/mcp"); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.test.tsx index f126a030b9f..0f75f32dda5 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.test.tsx @@ -69,6 +69,6 @@ describe("MCPLogoSelector", () => { it("should preview a custom external URL untouched", () => { render(); - expect(screen.getByAltText("Selected logo").getAttribute("src")).toBe("https://cdn.example.com/logo.png"); + expect(screen.getByAltText("Selected logo")).toHaveAttribute("src", "https://cdn.example.com/logo.png"); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPPermissionManagement.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPPermissionManagement.test.tsx index a53681f6cd8..ee6aee86a4d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPPermissionManagement.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPPermissionManagement.test.tsx @@ -48,7 +48,7 @@ describe("MCPPermissionManagement", () => { // The first switch in the component is for allow_all_keys const switches = screen.getAllByRole("switch"); const toggle = switches[0]; - expect(toggle).toHaveAttribute("aria-checked", "false"); + expect(toggle).not.toBeChecked(); }); const renderWithInitialValues = (initialValues: Record, props = {}) => { @@ -122,9 +122,9 @@ describe("MCPPermissionManagement", () => { // The first switch in the component is for allow_all_keys const switches = screen.getAllByRole("switch"); const toggle = switches[0]; - expect(toggle).toHaveAttribute("aria-checked", "true"); + expect(toggle).toBeChecked(); await user.click(toggle); - expect(toggle).toHaveAttribute("aria-checked", "false"); + expect(toggle).not.toBeChecked(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPServerCard.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPServerCard.test.tsx index d6343afe219..8c7fe46a1a2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPServerCard.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPServerCard.test.tsx @@ -52,13 +52,13 @@ describe("MCPServerCard logo", () => { it("passes an external logo_url through untouched", () => { renderCard({ mcp_info: { server_name: "demo_server", logo_url: "https://cdn.example.com/logo.png" } }); - expect(screen.getByAltText("demo_server logo").getAttribute("src")).toBe("https://cdn.example.com/logo.png"); + expect(screen.getByAltText("demo_server logo")).toHaveAttribute("src", "https://cdn.example.com/logo.png"); }); it("prefixes a stored asset path with the server root path under a non-root mount", () => { setServerRootPath("/litellm"); renderCard({ mcp_info: { server_name: "demo_server", logo_url: "/ui/assets/logos/github.svg" } }); - expect(screen.getByAltText("demo_server logo").getAttribute("src")).toBe("/litellm/ui/assets/logos/github.svg"); + expect(screen.getByAltText("demo_server logo")).toHaveAttribute("src", "/litellm/ui/assets/logos/github.svg"); }); it("renders a letter avatar when no logo_url is set", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.test.tsx index 15e5d886d37..7511b8684a4 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.test.tsx @@ -21,7 +21,7 @@ describe("MCPConnectionStatus", () => { it("should render nothing when canFetchTools is false and no URL is set", () => { const { container } = render(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should show 'Complete required fields' message when URL is set but canFetchTools is false", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.test.tsx index b660c4bdb76..6972c33ea1b 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.test.tsx @@ -1214,8 +1214,9 @@ describe("MCPServerEdit (tool list fetch)", () => { ); await waitFor(() => { - expect(screen.getByTestId("mcp-tool-config").getAttribute("data-external-error")).toContain( - "Authorize with the upstream (browser-only", + expect(screen.getByTestId("mcp-tool-config")).toHaveAttribute( + "data-external-error", + expect.stringContaining("Authorize with the upstream (browser-only"), ); }); expect(networking.listMCPTools).not.toHaveBeenCalled(); @@ -1242,8 +1243,9 @@ describe("MCPServerEdit (tool list fetch)", () => { const { rerender } = render(); await waitFor(() => { - expect(screen.getByTestId("mcp-tool-config").getAttribute("data-external-error")).toContain( - "Authenticate with this server in the Tools tab", + expect(screen.getByTestId("mcp-tool-config")).toHaveAttribute( + "data-external-error", + expect.stringContaining("Authenticate with this server in the Tools tab"), ); }); expect(networking.listMCPTools).not.toHaveBeenCalled(); @@ -1277,8 +1279,9 @@ describe("MCPServerEdit (tool list fetch)", () => { ); await waitFor(() => { - expect(screen.getByTestId("mcp-tool-config").getAttribute("data-external-error")).toContain( - "Authenticate with this server in the Tools tab", + expect(screen.getByTestId("mcp-tool-config")).toHaveAttribute( + "data-external-error", + expect.stringContaining("Authenticate with this server in the Tools tab"), ); }); expect(networking.listMCPTools).not.toHaveBeenCalled(); @@ -1657,9 +1660,7 @@ describe("MCPServerEdit (OAuth token persistence on save)", () => { // Check "remove saved app" on server A. fireEvent.click(screen.getByRole("checkbox", { name: /Remove the saved OAuth app on save/ })); - expect( - (screen.getByRole("checkbox", { name: /Remove the saved OAuth app on save/ }) as HTMLInputElement).checked, - ).toBe(true); + expect(screen.getByRole("checkbox", { name: /Remove the saved OAuth app on save/ })).toBeChecked(); // Switch the panel to server B without unmounting. rerender( @@ -1674,9 +1675,7 @@ describe("MCPServerEdit (OAuth token persistence on save)", () => { ); // The checkbox must have reset, so saving server B does not send the explicit-null delete write. - expect( - (screen.getByRole("checkbox", { name: /Remove the saved OAuth app on save/ }) as HTMLInputElement).checked, - ).toBe(false); + expect(screen.getByRole("checkbox", { name: /Remove the saved OAuth app on save/ })).not.toBeChecked(); await act(async () => { fireEvent.click(screen.getAllByRole("button", { name: "Save Changes" })[0]); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.test.tsx index 222a2e04117..521f89a39f2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.test.tsx @@ -76,14 +76,14 @@ describe("ModelsAndEndpointsPage", () => { const { getByRole, getByTestId, queryByTestId } = renderPage(); await user.click(getByRole("tab", { name: "Health Status" })); expect(getByTestId("panel-health")).toBeInTheDocument(); - expect(queryByTestId("panel-all-models")).toBeNull(); + expect(queryByTestId("panel-all-models")).not.toBeInTheDocument(); }); it("renders the model detail overlay from the ?model drill-in and hides the tabs", () => { detailState.modelId = "abc-123"; const { getByTestId, queryByRole } = renderPage(); expect(getByTestId("model-info")).toHaveTextContent("model:abc-123"); - expect(queryByRole("tab", { name: "All Models" })).toBeNull(); + expect(queryByRole("tab", { name: "All Models" })).not.toBeInTheDocument(); }); it("renders the team detail overlay from the ?team drill-in", () => { @@ -95,8 +95,8 @@ describe("ModelsAndEndpointsPage", () => { it("hides admin-only tabs for a non-admin user", () => { mockUseAuthorized.mockReturnValue(NON_ADMIN); const { queryByRole } = renderPage(); - expect(queryByRole("tab", { name: "LLM Credentials" })).toBeNull(); - expect(queryByRole("tab", { name: "Health Status" })).toBeNull(); + expect(queryByRole("tab", { name: "LLM Credentials" })).not.toBeInTheDocument(); + expect(queryByRole("tab", { name: "Health Status" })).not.toBeInTheDocument(); }); // Auto-routers are excluded from the All Models table, so this tab is their home: the only @@ -125,7 +125,7 @@ describe("ModelsAndEndpointsPage", () => { mockUseAuthorized.mockReturnValue(NON_ADMIN); const { queryByRole } = renderPage(); - expect(queryByRole("tab", { name: /Auto-Routers/ })).toBeNull(); + expect(queryByRole("tab", { name: /Auto-Routers/ })).not.toBeInTheDocument(); }); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.test.tsx index 1c2cb426f23..4c0f2831c6e 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.test.tsx @@ -39,13 +39,13 @@ describe("AdditionalModelSettings", () => { await waitFor(() => { const sliders = screen.getAllByRole("slider"); expect(sliders.length).toBeGreaterThan(0); - expect(sliders[0]).not.toBeDisabled(); + expect(sliders[0]).toBeEnabled(); }); const temperatureSlider = screen.getAllByRole("slider")[0]; const maxTokensSlider = screen.getAllByRole("slider")[1]; - expect(temperatureSlider).not.toBeDisabled(); - expect(maxTokensSlider).not.toBeDisabled(); + expect(temperatureSlider).toBeEnabled(); + expect(maxTokensSlider).toBeEnabled(); }); it("should not show Stream responses when onStreamingChange is not provided", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.test.tsx index e30f87115cf..f2c2c3d2c8c 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.test.tsx @@ -132,7 +132,7 @@ describe("AgentBuilderView", () => { expect(screen.getByRole("button", { name: "research-agent litellm_agent" })).toBeInTheDocument(); expect(screen.getByText("Agent Builder")).toBeInTheDocument(); - await waitFor(() => expect(screen.getByDisplayValue("support-agent")).toBeInTheDocument()); + expect(await screen.findByDisplayValue("support-agent")).toBeInTheDocument(); expect(screen.getByDisplayValue("Be helpful.")).toBeInTheDocument(); expect(screen.getByDisplayValue("0.3")).toBeInTheDocument(); }); @@ -144,7 +144,7 @@ describe("AgentBuilderView", () => { await user.click(screen.getByRole("button", { name: "research-agent litellm_agent" })); - await waitFor(() => expect(screen.getByDisplayValue("research-agent")).toBeInTheDocument()); + expect(await screen.findByDisplayValue("research-agent")).toBeInTheDocument(); }); it("offers a blank draft and a save control for a new agent", async () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatComposer.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatComposer.test.tsx index f3efb0a9b0f..da2e4debb3c 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatComposer.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatComposer.test.tsx @@ -109,6 +109,6 @@ describe("ChatComposer", () => { fireEvent.click(addonOf(container)); - expect(document.activeElement).toBe(screen.getByTestId("chat-composer-input")); + expect(screen.getByTestId("chat-composer-input")).toHaveFocus(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.test.tsx index de079cdebc9..24c56b276af 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.test.tsx @@ -148,10 +148,10 @@ describe("ChatUI", () => { await waitFor(() => { expect(screen.getAllByText("ChatModel").length).toBeGreaterThan(0); expect(screen.getAllByText("NoModeModel").length).toBeGreaterThan(0); - expect(screen.queryByText("SpeechModel")).toBeNull(); - expect(screen.queryByText("ImageModel")).toBeNull(); - expect(screen.queryByText("ResponsesModel")).toBeNull(); - expect(screen.queryByText("RealtimeModel")).toBeNull(); + expect(screen.queryByText("SpeechModel")).not.toBeInTheDocument(); + expect(screen.queryByText("ImageModel")).not.toBeInTheDocument(); + expect(screen.queryByText("ResponsesModel")).not.toBeInTheDocument(); + expect(screen.queryByText("RealtimeModel")).not.toBeInTheDocument(); }); }); @@ -182,7 +182,7 @@ describe("ChatUI", () => { await waitFor(() => { expect(screen.getAllByText("RealtimeModel").length).toBeGreaterThan(0); expect(screen.getAllByText("NoModeModel").length).toBeGreaterThan(0); - expect(screen.queryByText("ChatModel")).toBeNull(); + expect(screen.queryByText("ChatModel")).not.toBeInTheDocument(); }); }); @@ -234,7 +234,7 @@ describe("ChatUI", () => { await selectComboboxOption("Select an endpoint", "/v1/chat/completions"); await waitFor(() => { - expect(mcpInput()).not.toBeDisabled(); + expect(mcpInput()).toBeEnabled(); }); }); @@ -432,7 +432,7 @@ describe("ChatUI", () => { }); await waitFor(() => { - expect(screen.queryByText("Fill")).toBeNull(); + expect(screen.queryByText("Fill")).not.toBeInTheDocument(); }); const customProxyInput = screen.getByPlaceholderText( @@ -461,7 +461,7 @@ describe("ChatUI", () => { const mcpInput = screen.getByLabelText("Select MCP servers"); expect(mcpInput).toBeInTheDocument(); - expect(mcpInput).not.toBeDisabled(); + expect(mcpInput).toBeEnabled(); await user.click(mcpInput); @@ -521,7 +521,7 @@ describe("ChatUI", () => { await waitFor(() => { expect(screen.getAllByText("ChatModel").length).toBeGreaterThan(0); }); - expect(screen.queryByText("SpeechModel")).toBeNull(); + expect(screen.queryByText("SpeechModel")).not.toBeInTheDocument(); }); it("should attach an audio file dropped on the transcription upload area", async () => { @@ -611,7 +611,7 @@ describe("ChatUI", () => { await user.clear(keyField); await waitFor(() => { - expect(screen.getByPlaceholderText("Select a Model")).not.toBeDisabled(); + expect(screen.getByPlaceholderText("Select a Model")).toBeEnabled(); }); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterOutput.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterOutput.test.tsx index a42bd42ed81..c9cfc3ac833 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterOutput.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterOutput.test.tsx @@ -235,7 +235,7 @@ describe("CodeInterpreterOutput", () => { it("should return null when no code and no annotations", () => { const { container } = render(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should handle multiple image formats", async () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/RealtimePlayground.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/RealtimePlayground.test.tsx index a72a02a59e9..f374582fb41 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/RealtimePlayground.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/RealtimePlayground.test.tsx @@ -1,4 +1,4 @@ -import { act, render, screen, waitFor } from "@testing-library/react"; +import { act, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import RealtimePlayground from "./RealtimePlayground"; @@ -261,7 +261,7 @@ describe("RealtimePlayground", () => { await user.click(screen.getByRole("button", { name: /Disconnect/i })); expect(socket.close).toHaveBeenCalled(); - await waitFor(() => expect(screen.getByRole("button", { name: /Connect/i })).toBeInTheDocument()); + expect(await screen.findByRole("button", { name: /Connect/i })).toBeInTheDocument(); expect(screen.queryByPlaceholderText("Type a message or use the mic...")).not.toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/compareUI/CompareUI.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/compareUI/CompareUI.test.tsx index a68513b5204..cb8a5c16d3b 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/compareUI/CompareUI.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/compareUI/CompareUI.test.tsx @@ -138,14 +138,14 @@ describe("CompareUI", () => { } await waitFor(() => { - expect(queryByTestId("has-attachment")).toBeInTheDocument(); + expect(getByTestId("has-attachment")).toBeInTheDocument(); }); const textarea = getByTestId("message-textarea"); await user.type(textarea, "Describe this image"); const sendButton = getByTestId("send-button"); - expect(sendButton).not.toBeDisabled(); + expect(sendButton).toBeEnabled(); await user.click(sendButton); await waitFor(() => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/compareUI/components/MessageInput.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/compareUI/components/MessageInput.test.tsx index 049b70b982c..f1db0e863fa 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/compareUI/components/MessageInput.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/compareUI/components/MessageInput.test.tsx @@ -41,7 +41,7 @@ describe("MessageInput", () => { render(); const send = screen.getByRole("button"); - expect(send).not.toBeDisabled(); + expect(send).toBeEnabled(); await user.click(send); expect(onSend).toHaveBeenCalledTimes(1); @@ -63,7 +63,7 @@ describe("MessageInput", () => { expect(screen.getByTestId("upload-component")).toBeInTheDocument(); const send = screen.getByRole("button"); - expect(send).not.toBeDisabled(); + expect(send).toBeEnabled(); await user.click(send); expect(onSend).toHaveBeenCalledTimes(1); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/AttachmentTable.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/AttachmentTable.test.tsx index b544c44d190..e746effd634 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/AttachmentTable.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/AttachmentTable.test.tsx @@ -135,9 +135,9 @@ describe("AttachmentTable", () => { const attachment = makeAttachment({ attachment_id: "att-abcdef1234567" }); renderWithProviders(); const idElement = screen.getByText("att-abcdef1234567"); - expect(idElement.className).toContain("font-mono"); - expect(idElement.className).toContain("truncate"); - expect(idElement.className).not.toContain("bg-blue-50"); + expect(idElement).toHaveClass("font-mono"); + expect(idElement).toHaveClass("truncate"); + expect(idElement).not.toHaveClass("bg-blue-50"); }); it("should render model chips when the attachment has models", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/ai_suggestion_modal.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/ai_suggestion_modal.test.tsx index 4db75fb1ced..c504d0759ae 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/ai_suggestion_modal.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/ai_suggestion_modal.test.tsx @@ -96,7 +96,7 @@ describe("AiSuggestionModal", () => { expect(screen.getByRole("button", { name: "Suggest Policies" })).toBeDisabled(); await pickModel(user); - expect(screen.getByRole("button", { name: "Suggest Policies" })).not.toBeDisabled(); + expect(screen.getByRole("button", { name: "Suggest Policies" })).toBeEnabled(); }); it("sends the examples, description and model to the suggest API", async () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/index.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/index.test.tsx index 3b6534ab0f0..f508f6866d6 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/index.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/index.test.tsx @@ -156,8 +156,8 @@ describe("PoliciesPanel attachment delete", () => { await waitFor(() => { expect(networkingMocks.deletePolicyAttachmentCall).toHaveBeenCalledTimes(1); - expect(networkingMocks.deletePolicyAttachmentCall).toHaveBeenCalledWith("test-token", EXPECTED_ATTACHMENT_ID); }); + expect(networkingMocks.deletePolicyAttachmentCall).toHaveBeenCalledWith("test-token", EXPECTED_ATTACHMENT_ID); }); it("should show mutation pending state while attachment delete is in flight", async () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/template_parameter_modal.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/template_parameter_modal.test.tsx index 5dcab5cec80..c3b55cd18ff 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/template_parameter_modal.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/template_parameter_modal.test.tsx @@ -95,7 +95,7 @@ describe("TemplateParameterModal", () => { await user.type(screen.getByPlaceholderText("e.g. Contoso"), "Contoso"); - expect(screen.getByRole("button", { name: "Continue" })).not.toBeDisabled(); + expect(screen.getByRole("button", { name: "Continue" })).toBeEnabled(); }); it("hands the entered parameters back to the caller", async () => { @@ -181,7 +181,7 @@ describe("TemplateParameterModal", () => { expect(await screen.findByText("Northwind")).toBeInTheDocument(); expect(screen.getByText("Fabrikam")).toBeInTheDocument(); await waitFor(() => { - expect(screen.getByRole("button", { name: "Continue" })).not.toBeDisabled(); + expect(screen.getByRole("button", { name: "Continue" })).toBeEnabled(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/CreateSearchTools.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/CreateSearchTools.test.tsx index 7f1d7edc97f..f860b1a8bd4 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/CreateSearchTools.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/CreateSearchTools.test.tsx @@ -12,17 +12,20 @@ describe("SearchProviderLabel", () => { it("renders the exa_ai logo file for the exa_ai slug", () => { render(); const img = screen.getByRole("img", { name: "Exa AI logo" }); - expect(img.getAttribute("src")).toContain("exa_ai.png"); + expect(img).toHaveAttribute("src", expect.stringContaining("exa_ai.png")); }); it("renders the google_pse logo file for the google_pse slug", () => { render(); - expect(screen.getByRole("img", { name: "Google PSE logo" }).getAttribute("src")).toContain("google_pse.png"); + expect(screen.getByRole("img", { name: "Google PSE logo" })).toHaveAttribute( + "src", + expect.stringContaining("google_pse.png"), + ); }); it("falls back to a letter avatar for a provider with no bundled logo", () => { render(); - expect(screen.queryByRole("img")).toBeNull(); + expect(screen.queryByRole("img")).not.toBeInTheDocument(); expect(screen.getByText("B")).toBeInTheDocument(); expect(screen.getByText("Brave Search")).toBeInTheDocument(); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx index fd944120624..3d4144a74e6 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchToolTester.test.tsx @@ -82,7 +82,7 @@ describe("SearchToolTester", () => { const input = screen.getByPlaceholderText("Enter your search query..."); await user.type(input, "test query"); const searchButton = screen.getByRole("button", { name: /search/i }); - expect(searchButton).not.toBeDisabled(); + expect(searchButton).toBeEnabled(); }); it("should call searchToolQueryCall when search button is clicked", async () => { @@ -416,11 +416,9 @@ describe("SearchToolTester", () => { await user.type(input, "test query"); const searchButton = screen.getByRole("button", { name: /search/i }); await user.click(searchButton); - await waitFor(() => { - const link = screen.getByRole("link", { name: "Test Result 1" }); - expect(link).toHaveAttribute("href", "https://example.com/result1"); - expect(link).toHaveAttribute("target", "_blank"); - expect(link).toHaveAttribute("rel", "noopener noreferrer"); - }); + const link = await screen.findByRole("link", { name: "Test Result 1" }); + expect(link).toHaveAttribute("href", "https://example.com/result1"); + expect(link).toHaveAttribute("rel", "noopener noreferrer"); + expect(link).toHaveAttribute("target", "_blank"); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/skills/_components/add_plugin_form.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/skills/_components/add_plugin_form.test.tsx index 74d1fb5facf..713cee7682c 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/skills/_components/add_plugin_form.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/skills/_components/add_plugin_form.test.tsx @@ -90,7 +90,7 @@ describe("AddPluginForm", () => { await waitFor(() => { expect(screen.getByText(/Git repo/)).toBeInTheDocument(); }); - expect(screen.getByPlaceholderText(SUBPATH_PLACEHOLDER)).not.toBeDisabled(); + expect(screen.getByPlaceholderText(SUBPATH_PLACEHOLDER)).toBeEnabled(); }); it("combines a repo URL with a subfolder into a git-subdir preview", async () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/transform-request/TransformRequestPanel.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/transform-request/TransformRequestPanel.test.tsx index a0add153116..d8d46c361a2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/transform-request/TransformRequestPanel.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/transform-request/TransformRequestPanel.test.tsx @@ -76,10 +76,10 @@ describe("TransformRequestPanel", () => { }); const output = await screen.findByText(/api\.anthropic\.com\/v1\/messages/); - expect(output.textContent).toContain("curl -X POST"); - expect(output.textContent).toContain("-H 'x-api-key: redacted'"); - expect(output.textContent).toContain('"model": "claude-opus-4-8"'); - expect(output.textContent).toContain('"max_tokens": 42'); + expect(output).toHaveTextContent(/curl \-X POST/); + expect(output).toHaveTextContent(/\-H 'x\-api\-key: redacted'/); + expect(output).toHaveTextContent(/"model": "claude\-opus\-4\-8"/); + expect(output).toHaveTextContent(/"max_tokens": 42/); expect(notify.success).toHaveBeenCalledWith("Request transformed successfully"); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageLineChart.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageLineChart.test.tsx index 33914e627dc..81ca90d8cb9 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageLineChart.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageLineChart.test.tsx @@ -68,8 +68,8 @@ describe("EndpointUsageLineChart", () => { const legend = container.querySelector(".recharts-legend-wrapper"); expect(legend).not.toBeNull(); - expect(legend!.textContent).toContain("/chat/completions"); - expect(legend!.textContent).toContain("/embeddings"); + expect(legend!).toHaveTextContent(/\/chat\/completions/); + expect(legend!).toHaveTextContent(/\/embeddings/); }); it("orders formatted dates oldest to newest on the x axis", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.test.tsx index 1f3dd5642ee..b11fbbee258 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.test.tsx @@ -838,7 +838,7 @@ describe("EntityUsage", () => { const sectors = container.querySelectorAll(".recharts-pie-sector path"); expect(sectors).toHaveLength(1); - expect(sectors[0].getAttribute("fill")).toBe("var(--color-cyan-500, #06b6d4)"); + expect(sectors[0]).toHaveAttribute("fill", "var(--color-cyan-500, #06b6d4)"); const centerLabels = Array.from(container.querySelectorAll("text.fill-foreground")).map((text) => text.textContent); expect(centerLabels).toContain("$100.50"); @@ -883,7 +883,7 @@ describe("EntityUsage", () => { render(); const logo = await screen.findByAltText("openai logo"); - expect(logo.getAttribute("src")).toContain("openai_small"); + expect(logo).toHaveAttribute("src", expect.stringContaining("openai_small")); }); describe("capability gating", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.test.tsx index 4d5ddaa1df6..b3d77a567f7 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.test.tsx @@ -241,7 +241,7 @@ describe("UserEditView", () => { await waitFor(() => { const modelsSelect = screen.getByRole("combobox", { name: /select models/i }); - expect(modelsSelect).not.toBeDisabled(); + expect(modelsSelect).toBeEnabled(); }); }); @@ -300,7 +300,7 @@ describe("UserEditView", () => { await waitFor(() => { const budgetInput = screen.getByRole("spinbutton", { name: /max budget/i }); - expect(budgetInput).not.toBeDisabled(); + expect(budgetInput).toBeEnabled(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.test.tsx index 1a3d517df3e..57a50f6cfc2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.test.tsx @@ -273,7 +273,7 @@ describe("ViewUserDashboard", () => { await user.click(screen.getByTestId("datatable-select-row-user-2")); expect(screen.getByTestId("bulk-edit-users")).toHaveTextContent("Bulk Edit (1 selected)"); - expect(screen.getByTestId("bulk-edit-users")).not.toBeDisabled(); + expect(screen.getByTestId("bulk-edit-users")).toBeEnabled(); await user.click(screen.getByTestId("datatable-select-all")); expect(screen.getByTestId("bulk-edit-users")).toHaveTextContent("Bulk Edit (2 selected)"); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/UsersTable.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/UsersTable.test.tsx index 6262fd60f70..860b1c35cfd 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/UsersTable.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/UsersTable.test.tsx @@ -119,7 +119,7 @@ describe("UsersTable", () => { "Created At", "Updated At", ].forEach((header) => { - expect(headerRow.textContent).toContain(header); + expect(headerRow).toHaveTextContent(header); }); }); @@ -251,7 +251,7 @@ describe("UsersTable", () => { await user.click(screen.getByTestId("datatable-select-row-user-1")); - expect(screen.getByTestId("datatable-select-all")).toHaveAttribute("aria-checked", "mixed"); + expect(screen.getByTestId("datatable-select-all")).toBePartiallyChecked(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/CreateVectorStore.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/CreateVectorStore.test.tsx index 6c7a647e2d3..048b2d05f1d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/CreateVectorStore.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/CreateVectorStore.test.tsx @@ -226,9 +226,9 @@ describe("CreateVectorStore", () => { // Check if S3-specific fields are displayed await waitFor(() => { - expect(screen.queryByText("Vector Bucket Name")).toBeInTheDocument(); - expect(screen.queryByText("AWS Region")).toBeInTheDocument(); - expect(screen.queryByText("Embedding Model")).toBeInTheDocument(); + expect(screen.getByText("Vector Bucket Name")).toBeInTheDocument(); + expect(screen.getByText("AWS Region")).toBeInTheDocument(); + expect(screen.getByText("Embedding Model")).toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/IndexesTable.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/IndexesTable.test.tsx index f31c51f3fba..e318efb103b 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/IndexesTable.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/IndexesTable.test.tsx @@ -87,7 +87,7 @@ describe("IndexesTable", () => { it("should link created_by to the user detail deep link", () => { render(); const link = screen.getByRole("link", { name: "admin@example.com" }); - expect(link.getAttribute("href")).toMatch(/\/users\?user=admin%40example\.com$/); + expect(link).toHaveAttribute("href", expect.stringMatching(/\/users\?user=admin%40example\.com$/)); }); it("should keep the dash fallback and render no link for a null created_by", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx index 3eb013d47ad..94511ea114d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx @@ -29,6 +29,6 @@ describe("VectorStoreForm", () => { renderForm(); const logo = screen.getByRole("img", { name: `${VectorStoreProviders.Bedrock} logo` }); - expect(logo.getAttribute("src")).toBe(providerLogoMap[Providers.Bedrock]); + expect(logo).toHaveAttribute("src", providerLogoMap[Providers.Bedrock]); }); }); diff --git a/ui/litellm-dashboard/src/app/chat/page.integration.test.tsx b/ui/litellm-dashboard/src/app/chat/page.integration.test.tsx index 718c692a7a6..1918d19dae6 100644 --- a/ui/litellm-dashboard/src/app/chat/page.integration.test.tsx +++ b/ui/litellm-dashboard/src/app/chat/page.integration.test.tsx @@ -69,7 +69,7 @@ const ON_TOTAL_LATENCY_INDEX = 24; async function sendOneMessage(): Promise { render(); - await waitFor(() => expect(screen.getByRole("button", { name: /gpt-5\.4-mini/ })).toBeInTheDocument()); + expect(await screen.findByRole("button", { name: /gpt-5\.4-mini/ })).toBeInTheDocument(); fireEvent.change(screen.getByPlaceholderText("How can I help you today?"), { target: { value: "How much did this cost?" }, }); @@ -98,7 +98,7 @@ describe("/ui/chat request metrics", () => { await sendOneMessage(); - await waitFor(() => expect(screen.getByLabelText("Total: 20")).toBeInTheDocument()); + expect(await screen.findByLabelText("Total: 20")).toBeInTheDocument(); expect(screen.getByLabelText("TTFT: 0.25s")).toBeInTheDocument(); expect(screen.getByLabelText("Total Latency: 1.20s")).toBeInTheDocument(); expect(screen.getByLabelText("In: 12")).toBeInTheDocument(); @@ -126,7 +126,7 @@ describe("/ui/chat request metrics", () => { await sendOneMessage(); - await waitFor(() => expect(screen.getByText("No usage here.")).toBeInTheDocument()); + expect(await screen.findByText("No usage here.")).toBeInTheDocument(); expect(document.querySelector(".response-metrics")).toBeNull(); }); }); diff --git a/ui/litellm-dashboard/src/components/AIHub/UsefulLinksManagement.test.tsx b/ui/litellm-dashboard/src/components/AIHub/UsefulLinksManagement.test.tsx index 0a859ca95f8..3a66efd9fe1 100644 --- a/ui/litellm-dashboard/src/components/AIHub/UsefulLinksManagement.test.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/UsefulLinksManagement.test.tsx @@ -84,7 +84,7 @@ describe("UsefulLinksManagement", () => { render(); - await waitFor(() => expect(screen.getByText("First Link")).toBeInTheDocument()); + expect(await screen.findByText("First Link")).toBeInTheDocument(); await user.click(screen.getByRole("button", { name: /rearrange order/i })); @@ -123,7 +123,7 @@ describe("UsefulLinksManagement", () => { render(); - await waitFor(() => expect(screen.getByText("Test Link")).toBeInTheDocument()); + expect(await screen.findByText("Test Link")).toBeInTheDocument(); // Click edit button const editButton = screen.getByTestId("edit-link-0-Test Link"); @@ -147,7 +147,7 @@ describe("UsefulLinksManagement", () => { render(); - await waitFor(() => expect(screen.getByText("Test Link")).toBeInTheDocument()); + expect(await screen.findByText("Test Link")).toBeInTheDocument(); // Click edit button const editButton = screen.getByTestId("edit-link-0-Test Link"); @@ -183,7 +183,7 @@ describe("UsefulLinksManagement", () => { render(); - await waitFor(() => expect(screen.getByText("Test Link")).toBeInTheDocument()); + expect(await screen.findByText("Test Link")).toBeInTheDocument(); // Click edit button const editButton = screen.getByTestId("edit-link-0-Test Link"); @@ -216,7 +216,7 @@ describe("UsefulLinksManagement", () => { render(); - await waitFor(() => expect(screen.getByText("First Link")).toBeInTheDocument()); + expect(await screen.findByText("First Link")).toBeInTheDocument(); // Enter rearrange mode await user.click(screen.getByRole("button", { name: /rearrange order/i })); @@ -235,7 +235,7 @@ describe("UsefulLinksManagement", () => { const user = userEvent.setup(); render(); - await waitFor(() => expect(screen.getByText("Link Management")).toBeInTheDocument()); + expect(await screen.findByText("Link Management")).toBeInTheDocument(); // Initially expanded expect(screen.getByText("Manage Existing Links")).toBeInTheDocument(); diff --git a/ui/litellm-dashboard/src/components/AIHub/forms/MakeAgentPublicForm.test.tsx b/ui/litellm-dashboard/src/components/AIHub/forms/MakeAgentPublicForm.test.tsx index a55beaf517f..882e059e327 100644 --- a/ui/litellm-dashboard/src/components/AIHub/forms/MakeAgentPublicForm.test.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/forms/MakeAgentPublicForm.test.tsx @@ -74,7 +74,7 @@ describe("MakeAgentPublicForm", () => { // Check that the Next button is enabled (agents are preselected) const nextButton = screen.getByRole("button", { name: "Next" }); - expect(nextButton).not.toBeDisabled(); + expect(nextButton).toBeEnabled(); }); it("should handle agent selection and navigation", async () => { @@ -91,7 +91,7 @@ describe("MakeAgentPublicForm", () => { // Verify Next button is enabled const nextButton = screen.getByRole("button", { name: "Next" }); - expect(nextButton).not.toBeDisabled(); + expect(nextButton).toBeEnabled(); // Click Next await act(async () => { diff --git a/ui/litellm-dashboard/src/components/AIHub/forms/MakeMCPPublicForm.test.tsx b/ui/litellm-dashboard/src/components/AIHub/forms/MakeMCPPublicForm.test.tsx index ff385b3ed7c..5b96e9ad194 100644 --- a/ui/litellm-dashboard/src/components/AIHub/forms/MakeMCPPublicForm.test.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/forms/MakeMCPPublicForm.test.tsx @@ -97,7 +97,7 @@ describe("MakeMCPPublicForm", () => { // Check that the Next button is enabled (servers are preselected) const nextButton = screen.getByRole("button", { name: "Next" }); - expect(nextButton).not.toBeDisabled(); + expect(nextButton).toBeEnabled(); }); it("should handle server selection and navigation", async () => { @@ -114,7 +114,7 @@ describe("MakeMCPPublicForm", () => { // Verify Next button is enabled const nextButton = screen.getByRole("button", { name: "Next" }); - expect(nextButton).not.toBeDisabled(); + expect(nextButton).toBeEnabled(); // Click Next await act(async () => { diff --git a/ui/litellm-dashboard/src/components/AIHub/forms/MakeModelPublicForm.test.tsx b/ui/litellm-dashboard/src/components/AIHub/forms/MakeModelPublicForm.test.tsx index ac0df137f6a..013d92ea463 100644 --- a/ui/litellm-dashboard/src/components/AIHub/forms/MakeModelPublicForm.test.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/forms/MakeModelPublicForm.test.tsx @@ -121,7 +121,7 @@ describe("MakeModelPublicForm", () => { // Check that the Next button is enabled (models are preselected) const nextButton = screen.getByRole("button", { name: "Next" }); - expect(nextButton).not.toBeDisabled(); + expect(nextButton).toBeEnabled(); }); it("should handle model selection and navigation", async () => { @@ -138,7 +138,7 @@ describe("MakeModelPublicForm", () => { // Verify Next button is enabled const nextButton = screen.getByRole("button", { name: "Next" }); - expect(nextButton).not.toBeDisabled(); + expect(nextButton).toBeEnabled(); // Click Next await act(async () => { diff --git a/ui/litellm-dashboard/src/components/BetaBadge.test.tsx b/ui/litellm-dashboard/src/components/BetaBadge.test.tsx index 7eaf414087d..888842feada 100644 --- a/ui/litellm-dashboard/src/components/BetaBadge.test.tsx +++ b/ui/litellm-dashboard/src/components/BetaBadge.test.tsx @@ -47,7 +47,7 @@ describe("BetaBadge", () => { const { container } = render(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should render badge with dot instead of text when dot prop is true", () => { diff --git a/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx b/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx index 6f08dc8ebd4..6c656382deb 100644 --- a/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx +++ b/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { act, fireEvent, render, screen } from "@testing-library/react"; import { DashboardHeader } from "./DashboardHeader"; const { mockUsePluginMode, mockUseUISettings, state } = vi.hoisted(() => { @@ -44,7 +44,7 @@ describe("DashboardHeader breadcrumb", () => { act(() => { fireEvent.click(selector); }); - await waitFor(() => expect(screen.getByText("Chat")).toBeInTheDocument()); + expect(await screen.findByText("Chat")).toBeInTheDocument(); }); it("keeps the AI Gateway selector at the root even when there is nothing to switch to (discovery)", () => { diff --git a/ui/litellm-dashboard/src/components/Navbar/BlogDropdown/BlogDropdown.test.tsx b/ui/litellm-dashboard/src/components/Navbar/BlogDropdown/BlogDropdown.test.tsx index bad740fa361..0581a381a9a 100644 --- a/ui/litellm-dashboard/src/components/Navbar/BlogDropdown/BlogDropdown.test.tsx +++ b/ui/litellm-dashboard/src/components/Navbar/BlogDropdown/BlogDropdown.test.tsx @@ -197,12 +197,10 @@ describe("BlogDropdown", () => { await openDropdown(); - await waitFor(() => { - const link = screen.getByRole("link", { name: /post one/i }); - expect(link).toHaveAttribute("href", "https://example.com/1"); - expect(link).toHaveAttribute("target", "_blank"); - expect(link).toHaveAttribute("rel", "noopener noreferrer"); - }); + const link = await screen.findByRole("link", { name: /post one/i }); + expect(link).toHaveAttribute("href", "https://example.com/1"); + expect(link).toHaveAttribute("rel", "noopener noreferrer"); + expect(link).toHaveAttribute("target", "_blank"); }); it("should render formatted post dates", async () => { @@ -226,12 +224,10 @@ describe("BlogDropdown", () => { await openDropdown(); - await waitFor(() => { - const viewAllLink = screen.getByRole("link", { name: /view all posts/i }); - expect(viewAllLink).toHaveAttribute("href", "https://docs.litellm.ai/blog"); - expect(viewAllLink).toHaveAttribute("target", "_blank"); - expect(viewAllLink).toHaveAttribute("rel", "noopener noreferrer"); - }); + const viewAllLink = await screen.findByRole("link", { name: /view all posts/i }); + expect(viewAllLink).toHaveAttribute("href", "https://docs.litellm.ai/blog"); + expect(viewAllLink).toHaveAttribute("rel", "noopener noreferrer"); + expect(viewAllLink).toHaveAttribute("target", "_blank"); }); }); diff --git a/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.test.tsx b/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.test.tsx index d9a28b2ab8c..449ef2eddc6 100644 --- a/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.test.tsx +++ b/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.test.tsx @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { act, fireEvent, render, screen } from "@testing-library/react"; import ViewSwitcher from "./ViewSwitcher"; const { mockUsePluginMode, mockUseUISettings, mockUsePathname, state } = vi.hoisted(() => { @@ -58,7 +58,7 @@ describe("ViewSwitcher", () => { act(() => { fireEvent.click(button); }); - await waitFor(() => expect(screen.getByText("Chat")).toBeInTheDocument()); + expect(await screen.findByText("Chat")).toBeInTheDocument(); expect(screen.getByText(/Admins can enable in Settings/i)).toBeInTheDocument(); act(() => { @@ -81,7 +81,7 @@ describe("ViewSwitcher", () => { act(() => { fireEvent.click(screen.getByRole("button")); }); - await waitFor(() => expect(screen.getByText("AI Gateway")).toBeInTheDocument()); + expect(await screen.findByText("AI Gateway")).toBeInTheDocument(); expect(screen.getByText("Observability")).toBeInTheDocument(); }); @@ -93,7 +93,7 @@ describe("ViewSwitcher", () => { act(() => { fireEvent.click(screen.getByRole("button")); }); - await waitFor(() => expect(screen.getByText("Chat UI")).toBeInTheDocument()); + expect(await screen.findByText("Chat UI")).toBeInTheDocument(); act(() => { fireEvent.click(screen.getByText("Chat UI")); }); @@ -108,7 +108,7 @@ describe("ViewSwitcher", () => { act(() => { fireEvent.click(screen.getByRole("button")); }); - await waitFor(() => expect(screen.getByText("Chat")).toBeInTheDocument()); + expect(await screen.findByText("Chat")).toBeInTheDocument(); expect(screen.queryByText(/Admins can enable in Settings/i)).not.toBeInTheDocument(); act(() => { @@ -127,7 +127,7 @@ describe("ViewSwitcher", () => { act(() => { fireEvent.click(screen.getByRole("button")); }); - await waitFor(() => expect(screen.getByText("AI Gateway")).toBeInTheDocument()); + expect(await screen.findByText("AI Gateway")).toBeInTheDocument(); act(() => { fireEvent.click(screen.getByText("AI Gateway")); }); @@ -143,7 +143,7 @@ describe("ViewSwitcher", () => { act(() => { fireEvent.click(screen.getByRole("button")); }); - await waitFor(() => expect(screen.getByText("Observability")).toBeInTheDocument()); + expect(await screen.findByText("Observability")).toBeInTheDocument(); expect(screen.getByText("Chat")).toBeInTheDocument(); expect(screen.getByText(/Admins can enable in Settings/i)).toBeInTheDocument(); diff --git a/ui/litellm-dashboard/src/components/SSOModals.test.tsx b/ui/litellm-dashboard/src/components/SSOModals.test.tsx index 0fcfe60ffe8..783fd58392a 100644 --- a/ui/litellm-dashboard/src/components/SSOModals.test.tsx +++ b/ui/litellm-dashboard/src/components/SSOModals.test.tsx @@ -67,10 +67,8 @@ describe("SSOModals", () => { const ssoProviderSelect = screen.getByLabelText("SSO Provider"); fireEvent.mouseDown(ssoProviderSelect); // Wait for dropdown and select Google - await waitFor(() => { - const googleOption = screen.getByText("Google SSO"); - fireEvent.click(googleOption); - }); + const googleOption = await screen.findByText("Google SSO"); + fireEvent.click(googleOption); // Fill in the email field const emailInput = screen.getByLabelText("Proxy Admin Email"); @@ -119,10 +117,8 @@ describe("SSOModals", () => { const ssoProviderSelect = screen.getByLabelText("SSO Provider"); fireEvent.mouseDown(ssoProviderSelect); // Wait for dropdown and select Google - await waitFor(() => { - const googleOption = screen.getByText("Google SSO"); - fireEvent.click(googleOption); - }); + const googleOption = await screen.findByText("Google SSO"); + fireEvent.click(googleOption); // Fill in the email field const emailInput = screen.getByLabelText("Proxy Admin Email"); @@ -216,10 +212,8 @@ describe("SSOModals", () => { const ssoProviderSelect = screen.getByLabelText("SSO Provider"); fireEvent.mouseDown(ssoProviderSelect); // Wait for dropdown and select Google - await waitFor(() => { - const googleOption = screen.getByText("Google SSO"); - fireEvent.click(googleOption); - }); + const googleOption = await screen.findByText("Google SSO"); + fireEvent.click(googleOption); // Fill in the email field const emailInput = screen.getByLabelText("Proxy Admin Email"); diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterSettings.test.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterSettings.test.tsx index 39542945c45..af2cc0beb7e 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterSettings.test.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterSettings.test.tsx @@ -143,7 +143,7 @@ describe("MCPSemanticFilterSettings", () => { await user.click(screen.getByRole("switch")); - expect(screen.getByRole("button", { name: /save settings/i })).not.toBeDisabled(); + expect(screen.getByRole("button", { name: /save settings/i })).toBeEnabled(); }); it("should show an error alert when the mutation fails", async () => { diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterTestPanel.test.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterTestPanel.test.tsx index c35caffc4be..2362f5ead60 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterTestPanel.test.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterTestPanel.test.tsx @@ -69,7 +69,7 @@ describe("MCPSemanticFilterTestPanel", () => { it("should enable the Test Filter button when testQuery is set and filter is enabled", () => { render(); - expect(screen.getByRole("button", { name: /test filter/i })).not.toBeDisabled(); + expect(screen.getByRole("button", { name: /test filter/i })).toBeEnabled(); }); it("should call onTest when the Test Filter button is clicked", async () => { diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx index 2f9c49dfa56..8043e41a2de 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx @@ -39,10 +39,8 @@ describe("BaseSSOSettingsForm", () => { fireEvent.mouseDown(providerSelect); }); - await waitFor(() => { - const googleOption = screen.getByText(/google sso/i); - fireEvent.click(googleOption); - }); + const googleOption = await screen.findByText(/google sso/i); + fireEvent.click(googleOption); await waitFor(() => { expect(screen.getByText("Google Client ID")).toBeInTheDocument(); @@ -65,10 +63,8 @@ describe("BaseSSOSettingsForm", () => { fireEvent.mouseDown(providerSelect); }); - await waitFor(() => { - const oktaOption = screen.getByText(/okta/i); - fireEvent.click(oktaOption); - }); + const oktaOption = await screen.findByText(/okta/i); + fireEvent.click(oktaOption); await waitFor(() => { expect(screen.getByText("Use Role Mappings")).toBeInTheDocument(); @@ -132,10 +128,8 @@ describe("BaseSSOSettingsForm", () => { fireEvent.mouseDown(providerSelect); }); - await waitFor(() => { - const genericOption = screen.getByText(/generic sso/i); - fireEvent.click(genericOption); - }); + const genericOption = await screen.findByText(/generic sso/i); + fireEvent.click(genericOption); await waitFor(() => { expect(screen.getByText("Use Role Mappings")).toBeInTheDocument(); @@ -167,10 +161,8 @@ describe("BaseSSOSettingsForm", () => { fireEvent.mouseDown(providerSelect); }); - await waitFor(() => { - const oktaOption = screen.getByText(/okta/i); - fireEvent.click(oktaOption); - }); + const oktaOption = await screen.findByText(/okta/i); + fireEvent.click(oktaOption); await waitFor(() => { expect(screen.getByText("Use Team Mappings")).toBeInTheDocument(); @@ -192,10 +184,8 @@ describe("BaseSSOSettingsForm", () => { fireEvent.mouseDown(providerSelect); }); - await waitFor(() => { - const genericOption = screen.getByText(/generic sso/i); - fireEvent.click(genericOption); - }); + const genericOption = await screen.findByText(/generic sso/i); + fireEvent.click(genericOption); await waitFor(() => { expect(screen.getByText("Use Team Mappings")).toBeInTheDocument(); @@ -217,10 +207,8 @@ describe("BaseSSOSettingsForm", () => { fireEvent.mouseDown(providerSelect); }); - await waitFor(() => { - const oktaOption = screen.getByText(/okta/i); - fireEvent.click(oktaOption); - }); + const oktaOption = await screen.findByText(/okta/i); + fireEvent.click(oktaOption); await waitFor(() => { expect(screen.getByText("Use Team Mappings")).toBeInTheDocument(); @@ -251,10 +239,8 @@ describe("BaseSSOSettingsForm", () => { fireEvent.mouseDown(providerSelect); }); - await waitFor(() => { - const googleOption = screen.getByText(/google sso/i); - fireEvent.click(googleOption); - }); + const googleOption = await screen.findByText(/google sso/i); + fireEvent.click(googleOption); await waitFor(() => { expect(screen.getByText("Google Client ID")).toBeInTheDocument(); diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/RoleMappings.test.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/RoleMappings.test.tsx index f4b7b9aadd4..8a1b9bc99ed 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/RoleMappings.test.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/RoleMappings.test.tsx @@ -26,7 +26,7 @@ describe("RoleMappings", () => { it("should return null when roleMappings is undefined", () => { const { container } = renderWithProviders(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should display Group Claim and Default Role with correct values and display names", () => { diff --git a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/AddFallbacks.test.tsx b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/AddFallbacks.test.tsx index 1253532f269..1cdb57335ce 100644 --- a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/AddFallbacks.test.tsx +++ b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/AddFallbacks.test.tsx @@ -189,7 +189,7 @@ describe("AddFallbacks", () => { }); const saveButton = screen.getByRole("button", { name: /save all configurations/i }); - expect(saveButton).not.toBeDisabled(); + expect(saveButton).toBeEnabled(); await user.click(saveButton); await waitFor(() => { diff --git a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/Fallbacks.test.tsx b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/Fallbacks.test.tsx index a397b061801..cdcb1eefadb 100644 --- a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/Fallbacks.test.tsx +++ b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/Fallbacks.test.tsx @@ -125,7 +125,7 @@ describe("Fallbacks", () => { it("should not render when accessToken is null", () => { const { container } = renderWithQueryClient(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should fetch router settings on mount", async () => { diff --git a/ui/litellm-dashboard/src/components/TeamSSOSettings.test.tsx b/ui/litellm-dashboard/src/components/TeamSSOSettings.test.tsx index 8fd03d7340f..9b4e0527190 100644 --- a/ui/litellm-dashboard/src/components/TeamSSOSettings.test.tsx +++ b/ui/litellm-dashboard/src/components/TeamSSOSettings.test.tsx @@ -368,7 +368,7 @@ describe("TeamSSOSettings", () => { await userEvent.click(screen.getByRole("button", { name: /Edit Settings/i })); const permissionComboboxes = screen.getAllByRole("combobox"); const permissionCombobox = permissionComboboxes[permissionComboboxes.length - 1]; - expect(permissionCombobox).toBeDefined(); + expect(permissionCombobox).toBeInTheDocument(); await userEvent.click(permissionCombobox!); const deletePermissionOptions = await screen.findAllByText("/key/delete"); await userEvent.click(deletePermissionOptions[deletePermissionOptions.length - 1]); diff --git a/ui/litellm-dashboard/src/components/TeamsPage/TeamsTable.test.tsx b/ui/litellm-dashboard/src/components/TeamsPage/TeamsTable.test.tsx index 71c6aa66cf3..9375673ba10 100644 --- a/ui/litellm-dashboard/src/components/TeamsPage/TeamsTable.test.tsx +++ b/ui/litellm-dashboard/src/components/TeamsPage/TeamsTable.test.tsx @@ -151,7 +151,7 @@ describe("sort contract – only backend-sortable columns are sortable", () => { it("does not make Spend / Budget sortable (the backend rejects sort_by=spend)", () => { renderTable(); - expect(screen.getByText("Spend / Budget").closest("button")).toBeNull(); + expect(screen.queryByText("Spend / Budget").closest("button")).toBeNull(); // Team and Created are the only sortable headers. expect(screen.getByText("Team").closest("button")).not.toBeNull(); expect(screen.getByText("Created").closest("button")).not.toBeNull(); diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.test.tsx index be13ea1a7d3..dda1b805582 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/TopKeyView.test.tsx @@ -152,7 +152,7 @@ describe("TopKeyView", () => { const bars = container.querySelectorAll("path.recharts-rectangle"); expect(bars).toHaveLength(1); - expect(bars[0].getAttribute("fill")).toBe("var(--color-cyan-500, #06b6d4)"); + expect(bars[0]).toHaveAttribute("fill", "var(--color-cyan-500, #06b6d4)"); expect(screen.getAllByText("A Very Lon...").length).toBeGreaterThan(0); fireEvent.click(bars[0]); diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.test.tsx index 024226c3c03..d513d7c3a80 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/KeyModelUsageView.test.tsx @@ -31,7 +31,7 @@ describe("KeyModelUsageView", () => { it("should return null when topModels is empty", () => { const { container } = render(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should display Model Usage title", () => { diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx index 0910925a940..9ef849dbeb2 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx @@ -339,13 +339,9 @@ it("emphasizes the active field in the Spend / Budget header so the sorted colum await user.click(await screen.findByText("Budget descending")); await waitFor(() => { - expect(screen.getByText("Budget", { selector: "[data-sort-field='max_budget']" }).className).toContain( - "font-semibold", - ); + expect(screen.getByText("Budget", { selector: "[data-sort-field='max_budget']" })).toHaveClass("font-semibold"); }); - expect(screen.getByText("Spend", { selector: "[data-sort-field='spend']" }).className).toContain( - "text-muted-foreground", - ); + expect(screen.getByText("Spend", { selector: "[data-sort-field='spend']" })).toHaveClass("text-muted-foreground"); }); it("sorts by spend ascending when 'Spend ascending' is chosen from the Spend / Budget menu", async () => { @@ -537,7 +533,7 @@ describe("refresh button", () => { const refresh = screen.getByTestId("datatable-refresh"); expect(refresh).toBeInTheDocument(); - expect(refresh).not.toBeDisabled(); + expect(refresh).toBeEnabled(); }); it("disables the refresh control while a fetch is in flight but keeps data visible", () => { diff --git a/ui/litellm-dashboard/src/components/add_model/ClassifierPromptEditor.integration.test.tsx b/ui/litellm-dashboard/src/components/add_model/ClassifierPromptEditor.integration.test.tsx index f80753bc9a8..c35457630a5 100644 --- a/ui/litellm-dashboard/src/components/add_model/ClassifierPromptEditor.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ClassifierPromptEditor.integration.test.tsx @@ -1,4 +1,4 @@ -import { renderWithProviders, screen, waitFor } from "../../../tests/test-utils"; +import { renderWithProviders, screen } from "../../../tests/test-utils"; import userEvent from "@testing-library/user-event"; import { vi } from "vitest"; import ClassifierPromptEditor from "./ClassifierPromptEditor"; @@ -45,7 +45,7 @@ const openEditor = async ({ />, ); await userEvent.click(screen.getByRole("button", { name: /prompt/i })); - await waitFor(() => expect(screen.getByLabelText("Classifier system prompt")).toBeInTheDocument()); + expect(await screen.findByLabelText("Classifier system prompt")).toBeInTheDocument(); return onChange; }; diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx index f94f3306291..27ede9159a8 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx @@ -455,7 +455,7 @@ describe("AddAutoRouterTab", () => { const anthropicOption = optionByLabel("Anthropic Family")!; expect(isOptionDisabled(anthropicOption)).toBe(true); - expect(anthropicOption.textContent).toContain("Checking model availability"); + expect(anthropicOption).toHaveTextContent(/Checking model availability/); // The dropdown is already open from above; polling re-reads its options in place rather than // reopening (openTemplateDropdown toggles, so a second call here would close it instead). @@ -474,7 +474,7 @@ describe("AddAutoRouterTab", () => { openTemplateDropdown(); const anthropicOption = optionByLabel("Anthropic Family")!; expect(isOptionDisabled(anthropicOption)).toBe(true); - expect(anthropicOption.textContent).toContain("Cannot verify these models are available"); + expect(anthropicOption).toHaveTextContent(/Cannot verify these models are available/); }); it("keeps group-name presets selectable when only the deployment fetch fails", async () => { @@ -496,7 +496,7 @@ describe("AddAutoRouterTab", () => { openTemplateDropdown(); await waitFor(() => { - expect(optionByLabel("Anthropic Family")!.textContent).toContain(`Missing: ${ANTHROPIC_ONLY_MODEL}`); + expect(optionByLabel("Anthropic Family")!).toHaveTextContent(new RegExp(`Missing: ${ANTHROPIC_ONLY_MODEL}`)); }); expect(isOptionDisabled(optionByLabel("Anthropic Family")!)).toBe(true); }); @@ -695,7 +695,7 @@ describe("AddAutoRouterTab", () => { await waitFor(() => { expect(isOptionDisabled(optionByLabel("Anthropic Family")!)).toBe(false); }); - expect(optionByLabel("Anthropic Family")!.textContent).toContain("Matches your deployments"); + expect(optionByLabel("Anthropic Family")!).toHaveTextContent(/Matches your deployments/); }); it("keeps detailed configuration open and prefills the admin's group names on apply", async () => { @@ -755,7 +755,7 @@ describe("AddAutoRouterTab", () => { openTemplateDropdown(); await waitFor(() => { - expect(optionByLabel("OpenAI Family")!.textContent).toContain("Missing:"); + expect(optionByLabel("OpenAI Family")!).toHaveTextContent(/Missing:/); }); expect(isOptionDisabled(optionByLabel("OpenAI Family")!)).toBe(true); }); @@ -784,7 +784,7 @@ describe("AddAutoRouterTab", () => { await waitFor(() => { expect(isOptionDisabled(optionByLabel("Anthropic Family")!)).toBe(false); }); - expect(optionByLabel("Anthropic Family")!.textContent).toContain("Matches your deployments"); + expect(optionByLabel("Anthropic Family")!).toHaveTextContent(/Matches your deployments/); }); it("prefills the expanded group names and submits them", async () => { diff --git a/ui/litellm-dashboard/src/components/add_model/auto_router_connection_test.test.tsx b/ui/litellm-dashboard/src/components/add_model/auto_router_connection_test.test.tsx index b07270b5ced..41fb773dc96 100644 --- a/ui/litellm-dashboard/src/components/add_model/auto_router_connection_test.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/auto_router_connection_test.test.tsx @@ -44,7 +44,7 @@ describe("AutoRouterConnectionTest", () => { renderWithProviders(); await waitFor(() => expect(screen.getAllByTestId("test-status-success")).toHaveLength(3)); - expect(screen.queryByTestId("test-status-error")).toBeNull(); + expect(screen.queryByTestId("test-status-error")).not.toBeInTheDocument(); expect(screen.getByText("MEDIUM, COMPLEX")).toBeInTheDocument(); }); @@ -60,7 +60,7 @@ describe("AutoRouterConnectionTest", () => { renderWithProviders(); - await waitFor(() => expect(screen.getByTestId("test-error-message")).toBeInTheDocument()); + expect(await screen.findByTestId("test-error-message")).toBeInTheDocument(); expect(screen.getByTestId("test-error-message")).toHaveTextContent("invalid api key"); expect(screen.getByTestId("test-error-message")).not.toHaveTextContent("litellm.AuthenticationError"); expect(screen.getAllByTestId("test-status-success")).toHaveLength(2); diff --git a/ui/litellm-dashboard/src/components/add_model/litellm_model_name.test.tsx b/ui/litellm-dashboard/src/components/add_model/litellm_model_name.test.tsx index f1fd3fecb37..37eecec1f3f 100644 --- a/ui/litellm-dashboard/src/components/add_model/litellm_model_name.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/litellm_model_name.test.tsx @@ -25,6 +25,6 @@ describe("LitellmModelNameField", () => { , ); expect(getByPlaceholderText("my-deployment")).toBeInTheDocument(); - expect(queryByPlaceholderText("gpt-3.5-turbo")).toBeNull(); + expect(queryByPlaceholderText("gpt-3.5-turbo")).not.toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/add_model/provider_specific_fields.test.tsx b/ui/litellm-dashboard/src/components/add_model/provider_specific_fields.test.tsx index c2b730bf46a..bf62b4873b7 100644 --- a/ui/litellm-dashboard/src/components/add_model/provider_specific_fields.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/provider_specific_fields.test.tsx @@ -150,17 +150,15 @@ describe("ProviderSpecificFields", () => { , ); - await waitFor(() => { - const apiKeyLabel = screen.getByLabelText("OpenAI API Key"); - expect(apiKeyLabel).toBeInTheDocument(); + const apiKeyLabel = await screen.findByLabelText("OpenAI API Key"); + expect(apiKeyLabel).toBeInTheDocument(); - const apiBaseInput = screen.getByPlaceholderText("https://api.openai.com/v1"); - expect(apiBaseInput).toBeInTheDocument(); - expect(apiBaseInput).toHaveAttribute("type", "text"); + const apiBaseInput = screen.getByPlaceholderText("https://api.openai.com/v1"); + expect(apiBaseInput).toBeInTheDocument(); + expect(apiBaseInput).toHaveAttribute("type", "text"); - const orgInput = screen.getByPlaceholderText("[OPTIONAL] my-unique-org"); - expect(orgInput).toBeInTheDocument(); - }); + const orgInput = screen.getByPlaceholderText("[OPTIONAL] my-unique-org"); + expect(orgInput).toBeInTheDocument(); }); it("should render the provider specific fields for vLLM", async () => { @@ -173,14 +171,12 @@ describe("ProviderSpecificFields", () => { , ); - await waitFor(() => { - const apiKeyLabel = screen.getByLabelText("vLLM API Key"); - expect(apiKeyLabel).toBeInTheDocument(); + const apiKeyLabel = await screen.findByLabelText("vLLM API Key"); + expect(apiKeyLabel).toBeInTheDocument(); - const apiBaseInput = screen.getByPlaceholderText("https://..."); - expect(apiBaseInput).toBeInTheDocument(); - expect(apiBaseInput).toHaveAttribute("type", "text"); - }); + const apiBaseInput = screen.getByPlaceholderText("https://..."); + expect(apiBaseInput).toBeInTheDocument(); + expect(apiBaseInput).toHaveAttribute("type", "text"); }); it("should render the provider specific fields for Azure", async () => { @@ -193,27 +189,25 @@ describe("ProviderSpecificFields", () => { , ); - await waitFor(() => { - const apiKeyInput = screen.getByLabelText("Azure API Key"); - expect(apiKeyInput).toBeInTheDocument(); - expect(apiKeyInput).toHaveAttribute("type", "password"); - expect(apiKeyInput).toHaveAttribute("placeholder", "Enter your Azure API Key"); + const apiKeyInput = await screen.findByLabelText("Azure API Key"); + expect(apiKeyInput).toBeInTheDocument(); + expect(apiKeyInput).toHaveAttribute("type", "password"); + expect(apiKeyInput).toHaveAttribute("placeholder", "Enter your Azure API Key"); - const azureAdTokenInput = screen.getByLabelText("Azure AD Token"); - expect(azureAdTokenInput).toBeInTheDocument(); - expect(azureAdTokenInput).toHaveAttribute("type", "password"); - expect(azureAdTokenInput).toHaveAttribute("placeholder", "Enter your Azure AD Token"); + const azureAdTokenInput = screen.getByLabelText("Azure AD Token"); + expect(azureAdTokenInput).toBeInTheDocument(); + expect(azureAdTokenInput).toHaveAttribute("type", "password"); + expect(azureAdTokenInput).toHaveAttribute("placeholder", "Enter your Azure AD Token"); - const apiBaseInput = screen.getByPlaceholderText("https://..."); - expect(apiBaseInput).toBeInTheDocument(); - expect(apiBaseInput).toHaveAttribute("type", "text"); + const apiBaseInput = screen.getByPlaceholderText("https://..."); + expect(apiBaseInput).toBeInTheDocument(); + expect(apiBaseInput).toHaveAttribute("type", "text"); - const apiVersionInput = screen.getByPlaceholderText("2023-07-01-preview"); - expect(apiVersionInput).toBeInTheDocument(); + const apiVersionInput = screen.getByPlaceholderText("2023-07-01-preview"); + expect(apiVersionInput).toBeInTheDocument(); - const baseModelInput = screen.getByPlaceholderText("azure/gpt-3.5-turbo"); - expect(baseModelInput).toBeInTheDocument(); - }); + const baseModelInput = screen.getByPlaceholderText("azure/gpt-3.5-turbo"); + expect(baseModelInput).toBeInTheDocument(); }); it("sets Azure API version from the API base query parameter", async () => { diff --git a/ui/litellm-dashboard/src/components/chat/ConnectFlowBanner.test.tsx b/ui/litellm-dashboard/src/components/chat/ConnectFlowBanner.test.tsx index 4833b4ad8a4..b3cd6e229af 100644 --- a/ui/litellm-dashboard/src/components/chat/ConnectFlowBanner.test.tsx +++ b/ui/litellm-dashboard/src/components/chat/ConnectFlowBanner.test.tsx @@ -16,8 +16,8 @@ describe("ConnectFlowBanner", () => { const { container } = render(); const form = container.querySelector("form")!; - expect(form.getAttribute("method")).toBe("POST"); - expect(form.getAttribute("action")).toBe("https://gateway.example.com/authorize/complete"); + expect(form).toHaveAttribute("method", "POST"); + expect(form).toHaveAttribute("action", "https://gateway.example.com/authorize/complete"); const hidden = form.querySelector('input[name="flow"]') as HTMLInputElement; expect(hidden.value).toBe("flow-handle-123"); diff --git a/ui/litellm-dashboard/src/components/chat/MCPAppsPanel.test.tsx b/ui/litellm-dashboard/src/components/chat/MCPAppsPanel.test.tsx index 5fc3e75195b..e8795c36bc6 100644 --- a/ui/litellm-dashboard/src/components/chat/MCPAppsPanel.test.tsx +++ b/ui/litellm-dashboard/src/components/chat/MCPAppsPanel.test.tsx @@ -58,8 +58,8 @@ describe("MCPAppsPanel logos", () => { renderPanel(); expect(await screen.findByText("external_logo")).toBeInTheDocument(); - expect(screen.getByAltText("external_logo logo").getAttribute("src")).toBe("https://cdn.example.com/ext.png"); - expect(screen.getByAltText("local_logo logo").getAttribute("src")).toBe("/litellm/ui/assets/logos/github.svg"); + expect(screen.getByAltText("external_logo logo")).toHaveAttribute("src", "https://cdn.example.com/ext.png"); + expect(screen.getByAltText("local_logo logo")).toHaveAttribute("src", "/litellm/ui/assets/logos/github.svg"); }); it("renders a colored letter avatar for servers without logo_url", async () => { @@ -83,7 +83,7 @@ describe("MCPAppsPanel logos", () => { fireEvent.click(await screen.findByText("local_logo")); expect(await screen.findByRole("heading", { name: "local_logo" })).toBeInTheDocument(); - expect(screen.getByAltText("local_logo logo").getAttribute("src")).toBe("/litellm/ui/assets/logos/github.svg"); + expect(screen.getByAltText("local_logo logo")).toHaveAttribute("src", "/litellm/ui/assets/logos/github.svg"); }); }); @@ -239,7 +239,7 @@ describe("MCPAppsPanel connected-app reachability (LIT-4861)", () => { expect(onChange).not.toHaveBeenCalled(); expect(screen.queryByText("revoked_srv")).not.toBeInTheDocument(); - expect(screen.getByText("Connected", { exact: false }).textContent).toBe("Connected"); + expect(screen.getByText("Connected", { exact: false })).toHaveTextContent("Connected"); }); it("does not select a server when Connect resolves in the same tick the refetch drops it", async () => { diff --git a/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.test.tsx b/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.test.tsx index f2912d460f0..4093688fcb9 100644 --- a/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.test.tsx +++ b/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.test.tsx @@ -40,8 +40,8 @@ describe("MCPConnectPicker logos", () => { render(); expect(await screen.findByText("external_logo")).toBeInTheDocument(); - expect(screen.getByAltText("external_logo logo").getAttribute("src")).toBe("https://cdn.example.com/ext.png"); - expect(screen.getByAltText("local_logo logo").getAttribute("src")).toBe("/litellm/ui/assets/logos/github.svg"); + expect(screen.getByAltText("external_logo logo")).toHaveAttribute("src", "https://cdn.example.com/ext.png"); + expect(screen.getByAltText("local_logo logo")).toHaveAttribute("src", "/litellm/ui/assets/logos/github.svg"); }); it("renders no logo at all for servers without logo_url", async () => { diff --git a/ui/litellm-dashboard/src/components/common_components/DefaultProxyAdminTag.test.tsx b/ui/litellm-dashboard/src/components/common_components/DefaultProxyAdminTag.test.tsx index e330bb2e1b9..9236a56a8b9 100644 --- a/ui/litellm-dashboard/src/components/common_components/DefaultProxyAdminTag.test.tsx +++ b/ui/litellm-dashboard/src/components/common_components/DefaultProxyAdminTag.test.tsx @@ -22,6 +22,6 @@ describe("DefaultProxyAdminTag", () => { it("should render empty text when userId is null", () => { const { container } = render(); expect(screen.queryByText("Default Proxy Admin")).not.toBeInTheDocument(); - expect(container.textContent).toBe(""); + expect(container).toHaveTextContent(""); }); }); diff --git a/ui/litellm-dashboard/src/components/common_components/DeleteResourceModal.test.tsx b/ui/litellm-dashboard/src/components/common_components/DeleteResourceModal.test.tsx index 465f7fcfcf0..ebcb8221b1a 100644 --- a/ui/litellm-dashboard/src/components/common_components/DeleteResourceModal.test.tsx +++ b/ui/litellm-dashboard/src/components/common_components/DeleteResourceModal.test.tsx @@ -118,7 +118,7 @@ describe("DeleteResourceModal", () => { const input = screen.getByPlaceholderText("DELETE"); await user.type(input, "DELETE"); const deleteButton = screen.getByRole("button", { name: /delete/i }); - expect(deleteButton).not.toBeDisabled(); + expect(deleteButton).toBeEnabled(); }); it("should reset requiredConfirmation input when modal opens", async () => { diff --git a/ui/litellm-dashboard/src/components/common_components/KeyLifecycleSettings.test.tsx b/ui/litellm-dashboard/src/components/common_components/KeyLifecycleSettings.test.tsx index a4263e2f321..5dca1f65874 100644 --- a/ui/litellm-dashboard/src/components/common_components/KeyLifecycleSettings.test.tsx +++ b/ui/litellm-dashboard/src/components/common_components/KeyLifecycleSettings.test.tsx @@ -144,7 +144,7 @@ describe("KeyLifecycleSettings", () => { expect(screen.queryByText("Rotation Interval")).not.toBeInTheDocument(); await user.click(screen.getByRole("switch")); - await waitFor(() => expect(screen.getByText("Rotation Interval")).toBeInTheDocument()); + expect(await screen.findByText("Rotation Interval")).toBeInTheDocument(); }); it("propagates a selected predefined interval", async () => { @@ -152,7 +152,7 @@ describe("KeyLifecycleSettings", () => { renderWithProviders(); await user.click(screen.getByRole("switch")); - await waitFor(() => expect(screen.getByText("Rotation Interval")).toBeInTheDocument()); + expect(await screen.findByText("Rotation Interval")).toBeInTheDocument(); await user.click(screen.getByRole("combobox")); await user.click(await screen.findByText("90 days")); @@ -166,7 +166,7 @@ describe("KeyLifecycleSettings", () => { renderWithProviders(); await user.click(screen.getByRole("switch")); - await waitFor(() => expect(screen.getByText("Rotation Interval")).toBeInTheDocument()); + expect(await screen.findByText("Rotation Interval")).toBeInTheDocument(); await user.click(screen.getByRole("combobox")); await user.click(await screen.findByText("Custom interval")); @@ -181,7 +181,7 @@ describe("KeyLifecycleSettings", () => { renderWithProviders(); await user.click(screen.getByRole("switch")); - await waitFor(() => expect(screen.getByText("Rotation Interval")).toBeInTheDocument()); + expect(await screen.findByText("Rotation Interval")).toBeInTheDocument(); await user.click(screen.getByRole("combobox")); await user.click(await screen.findByText("Custom interval")); @@ -198,7 +198,7 @@ describe("KeyLifecycleSettings", () => { renderWithProviders(); await user.click(screen.getByRole("switch")); - await waitFor(() => expect(screen.getByText("Rotation Interval")).toBeInTheDocument()); + expect(await screen.findByText("Rotation Interval")).toBeInTheDocument(); await user.click(screen.getByRole("combobox")); await user.click(await screen.findByText("Custom interval")); diff --git a/ui/litellm-dashboard/src/components/common_components/NewBadge.test.tsx b/ui/litellm-dashboard/src/components/common_components/NewBadge.test.tsx index 2500551d37b..3ae24b16e7b 100644 --- a/ui/litellm-dashboard/src/components/common_components/NewBadge.test.tsx +++ b/ui/litellm-dashboard/src/components/common_components/NewBadge.test.tsx @@ -47,7 +47,7 @@ describe("NewBadge", () => { const { container } = render(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should render badge with dot when dot prop is true", () => { diff --git a/ui/litellm-dashboard/src/components/common_components/chartUtils.test.tsx b/ui/litellm-dashboard/src/components/common_components/chartUtils.test.tsx index 01a4d8373c4..bae11ff5671 100644 --- a/ui/litellm-dashboard/src/components/common_components/chartUtils.test.tsx +++ b/ui/litellm-dashboard/src/components/common_components/chartUtils.test.tsx @@ -46,7 +46,7 @@ describe("CustomTooltip", () => { label: "2024-01-15", }; const { container } = render(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should return null when payload is empty", () => { @@ -56,7 +56,7 @@ describe("CustomTooltip", () => { label: "2024-01-15", }; const { container } = render(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should display formatted category names", () => { diff --git a/ui/litellm-dashboard/src/components/common_components/routerSettingsWiring.test.tsx b/ui/litellm-dashboard/src/components/common_components/routerSettingsWiring.test.tsx index 15c52e71e8f..6bfda813eaa 100644 --- a/ui/litellm-dashboard/src/components/common_components/routerSettingsWiring.test.tsx +++ b/ui/litellm-dashboard/src/components/common_components/routerSettingsWiring.test.tsx @@ -58,8 +58,8 @@ describe("key router settings wiring, /key/info payload through to rendered outp it("should not leak a field the accordion has no control for into the editor", async () => { render(renderAccordion(KEY_INFO_ROUTER_SETTINGS)); - await waitFor(() => expect(screen.getByTestId("loadbalancing")).toBeInTheDocument()); - expect(screen.getByTestId("loadbalancing").textContent).not.toContain("tag_routing_prefix"); + expect(await screen.findByTestId("loadbalancing")).toBeInTheDocument(); + expect(screen.getByTestId("loadbalancing")).not.toHaveTextContent(/tag_routing_prefix/); }); it("should render an empty editor for a key holding only fields it cannot show", async () => { diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.test.tsx b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.test.tsx index f8a6f671de0..cd37e05c13b 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.test.tsx +++ b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.test.tsx @@ -519,6 +519,7 @@ describe("EditAutoRouterModal custom classifier prompt and fallback", () => { await user.click(await screen.findByText("Advanced: Classification Method")); expect(await screen.findByRole("button", { name: "Edit custom prompt" })).toBeInTheDocument(); + // eslint-disable-next-line jest-dom/prefer-checked -- antd sets the checked attribute without the DOM property, so toBeChecked reads false expect(screen.getByRole("radio", { name: /Route to the default model/ })).toHaveAttribute("checked"); await user.click(screen.getByRole("button", { name: /save changes/i })); diff --git a/ui/litellm-dashboard/src/components/email_settings.test.tsx b/ui/litellm-dashboard/src/components/email_settings.test.tsx index 198577e405e..691e0f251f2 100644 --- a/ui/litellm-dashboard/src/components/email_settings.test.tsx +++ b/ui/litellm-dashboard/src/components/email_settings.test.tsx @@ -101,13 +101,13 @@ describe("EmailSettings", () => { renderWithProviders(); expect(inputNamed("EMAIL_LOGO_URL")).toBeDisabled(); - expect(inputNamed("SMTP_HOST")).not.toBeDisabled(); + expect(inputNamed("SMTP_HOST")).toBeEnabled(); }); it("leaves the premium-only fields editable for premium users", () => { renderWithProviders(); - expect(inputNamed("EMAIL_LOGO_URL")).not.toBeDisabled(); + expect(inputNamed("EMAIL_LOGO_URL")).toBeEnabled(); }); it("triggers a live email health check", async () => { diff --git a/ui/litellm-dashboard/src/components/key_team_helpers/BudgetFallbacksEditor.test.tsx b/ui/litellm-dashboard/src/components/key_team_helpers/BudgetFallbacksEditor.test.tsx index 1b5603045e0..6b21421184f 100644 --- a/ui/litellm-dashboard/src/components/key_team_helpers/BudgetFallbacksEditor.test.tsx +++ b/ui/litellm-dashboard/src/components/key_team_helpers/BudgetFallbacksEditor.test.tsx @@ -9,8 +9,8 @@ describe("BudgetFallbacksEditor", () => { it("renders empty state with add button", () => { const onChange = vi.fn(); render(); - expect(screen.getByText("Add Budget Fallback")).toBeTruthy(); - expect(screen.getByText(/reroute to fallback models/)).toBeTruthy(); + expect(screen.getByText("Add Budget Fallback")).toBeInTheDocument(); + expect(screen.getByText(/reroute to fallback models/)).toBeInTheDocument(); }); it("renders existing entries from value prop", () => { @@ -22,9 +22,9 @@ describe("BudgetFallbacksEditor", () => { availableModels={MODELS} />, ); - expect(screen.getByText("IF BUDGET EXCEEDED, TRY")).toBeTruthy(); - expect(screen.getByText("Primary Model")).toBeTruthy(); - expect(screen.getByText("Fallback Models")).toBeTruthy(); + expect(screen.getByText("IF BUDGET EXCEEDED, TRY")).toBeInTheDocument(); + expect(screen.getByText("Primary Model")).toBeInTheDocument(); + expect(screen.getByText("Fallback Models")).toBeInTheDocument(); }); it("adds a new empty entry when clicking add button", async () => { @@ -33,7 +33,7 @@ describe("BudgetFallbacksEditor", () => { render(); await user.click(screen.getByText("Add Budget Fallback")); - expect(screen.getByText("Primary Model")).toBeTruthy(); + expect(screen.getByText("Primary Model")).toBeInTheDocument(); expect(onChange).toHaveBeenCalledWith({}); }); @@ -81,8 +81,8 @@ describe("BudgetFallbacksEditor", () => { expect(screen.getAllByText("Primary Model").length).toBe(1); rerender(); - expect(screen.queryByText("Primary Model")).toBeNull(); - expect(screen.getByText("Add Budget Fallback")).toBeTruthy(); + expect(screen.queryByText("Primary Model")).not.toBeInTheDocument(); + expect(screen.getByText("Add Budget Fallback")).toBeInTheDocument(); }); it("shows ordering hint when multiple fallback models are configured", () => { @@ -94,6 +94,6 @@ describe("BudgetFallbacksEditor", () => { availableModels={MODELS} />, ); - expect(screen.getByText(/first model still within its own budget/)).toBeTruthy(); + expect(screen.getByText(/first model still within its own budget/)).toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/leftnav.test.tsx b/ui/litellm-dashboard/src/components/leftnav.test.tsx index 8eca990261c..61b6bf8e515 100644 --- a/ui/litellm-dashboard/src/components/leftnav.test.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.test.tsx @@ -456,8 +456,8 @@ describe("Sidebar (leftnav)", () => { const costOptimization = container.querySelector('a[href*="cost-optimization"]'); expect(costOptimization).not.toBeNull(); - expect(costOptimization!.textContent).toContain("Cost Optimization"); - expect(costOptimization!.textContent).toContain("Beta"); + expect(costOptimization!).toHaveTextContent(/Cost Optimization/); + expect(costOptimization!).toHaveTextContent(/Beta/); expect(container.querySelector('a[href*="projects"]')).toBeNull(); }); diff --git a/ui/litellm-dashboard/src/components/logging_settings_view.test.tsx b/ui/litellm-dashboard/src/components/logging_settings_view.test.tsx index b0a1b54bac7..b21ba396411 100644 --- a/ui/litellm-dashboard/src/components/logging_settings_view.test.tsx +++ b/ui/litellm-dashboard/src/components/logging_settings_view.test.tsx @@ -34,7 +34,7 @@ describe("LoggingSettingsView logos", () => { it("renders a letter avatar for the custom callback API, which has no bundled logo", () => { render(); - expect(screen.queryByAltText("Custom Callback API logo")).toBeNull(); + expect(screen.queryByAltText("Custom Callback API logo")).not.toBeInTheDocument(); expect(screen.getByText("C")).toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/model_add/CredentialModal.test.tsx b/ui/litellm-dashboard/src/components/model_add/CredentialModal.test.tsx index 6804d0cba92..c8140765479 100644 --- a/ui/litellm-dashboard/src/components/model_add/CredentialModal.test.tsx +++ b/ui/litellm-dashboard/src/components/model_add/CredentialModal.test.tsx @@ -97,7 +97,7 @@ describe("CredentialModal", () => { expect(screen.getByText("Add Credential")).toBeInTheDocument(); const nameInput = screen.getByLabelText("Credential Name:") as HTMLInputElement; expect(nameInput.value).toBe(""); - expect(nameInput.disabled).toBe(false); + expect(nameInput).toBeEnabled(); }); it("shows provider-specific fields for the selected provider", async () => { @@ -124,7 +124,7 @@ describe("CredentialModal", () => { await waitFor(() => { const nameInput = screen.getByLabelText("Credential Name:") as HTMLInputElement; expect(nameInput.value).toBe("test-credential"); - expect(nameInput.disabled).toBe(true); + expect(nameInput).toBeDisabled(); }); }); @@ -134,7 +134,7 @@ describe("CredentialModal", () => { existingCredential: { ...mockCredential, credential_name: "" }, }); - expect((screen.getByLabelText("Credential Name:") as HTMLInputElement).disabled).toBe(true); + expect(screen.getByLabelText("Credential Name:")).toBeDisabled(); }); }); }); diff --git a/ui/litellm-dashboard/src/components/model_dashboard/HealthCheckComponent.test.tsx b/ui/litellm-dashboard/src/components/model_dashboard/HealthCheckComponent.test.tsx index 011d0568afd..1b0194b6478 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard/HealthCheckComponent.test.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard/HealthCheckComponent.test.tsx @@ -179,7 +179,7 @@ describe("HealthCheckComponent", () => { await user.click(screen.getByTestId("datatable-select-row-id-alpha")); await user.click(screen.getByTestId("clear-health-selection")); - expect(screen.getByTestId("datatable-select-row-id-alpha")).toHaveAttribute("aria-checked", "false"); + expect(screen.getByTestId("datatable-select-row-id-alpha")).not.toBeChecked(); expect(screen.getByTestId("run-health-checks")).toHaveTextContent("Run All Checks"); }); @@ -195,7 +195,7 @@ describe("HealthCheckComponent", () => { await user.click(screen.getByTestId("pagination-next")); expect(screen.queryByTestId("clear-health-selection")).not.toBeInTheDocument(); - expect(screen.getByTestId("datatable-select-row-id-alpha")).toHaveAttribute("aria-checked", "false"); + expect(screen.getByTestId("datatable-select-row-id-alpha")).not.toBeChecked(); }); }); diff --git a/ui/litellm-dashboard/src/components/model_info_view.test.tsx b/ui/litellm-dashboard/src/components/model_info_view.test.tsx index 46bd80a0bc5..4c8b9153077 100644 --- a/ui/litellm-dashboard/src/components/model_info_view.test.tsx +++ b/ui/litellm-dashboard/src/components/model_info_view.test.tsx @@ -955,8 +955,8 @@ describe("ModelInfoView", () => { await waitFor(() => { expect(mockTestModelGroupConnection).toHaveBeenCalledWith("test-token", "gpt-4o-mini", "chat"); - expect(mockTestModelGroupConnection).toHaveBeenCalledWith("test-token", "gpt-4o", "chat"); }); + expect(mockTestModelGroupConnection).toHaveBeenCalledWith("test-token", "gpt-4o", "chat"); expect(mockTestConnectionRequest).not.toHaveBeenCalled(); }); @@ -988,8 +988,8 @@ describe("ModelInfoView", () => { await waitFor(() => { expect(mockTestModelGroupConnection).toHaveBeenCalledWith("test-token", "gpt-4o-mini", "chat"); - expect(mockTestModelGroupConnection).toHaveBeenCalledWith("test-token", "gpt-4o", "chat"); }); + expect(mockTestModelGroupConnection).toHaveBeenCalledWith("test-token", "gpt-4o", "chat"); }); it("does not duplicate the default model as a test target when it is already covered by a configured tier", async () => { @@ -1128,7 +1128,7 @@ describe("ModelInfoView", () => { render(, { wrapper }); const logo = await screen.findByAltText("openai logo"); - expect(logo.getAttribute("src")).toContain("openai_small"); + expect(logo).toHaveAttribute("src", expect.stringContaining("openai_small")); }); it("renders a letter avatar instead of an img for an unknown provider slug", async () => { diff --git a/ui/litellm-dashboard/src/components/molecules/cost_optimization_feedback_banner.test.tsx b/ui/litellm-dashboard/src/components/molecules/cost_optimization_feedback_banner.test.tsx index 328c1441fbc..bad92555bd5 100644 --- a/ui/litellm-dashboard/src/components/molecules/cost_optimization_feedback_banner.test.tsx +++ b/ui/litellm-dashboard/src/components/molecules/cost_optimization_feedback_banner.test.tsx @@ -16,8 +16,8 @@ describe("CostOptimizationFeedbackBanner", () => { }); it("hides itself and persists the dismissal when the dismiss button is clicked", () => { - const { queryByText, getByLabelText } = render(); - expect(queryByText("Help shape cost optimization")).toBeInTheDocument(); + const { getByText, queryByText, getByLabelText } = render(); + expect(getByText("Help shape cost optimization")).toBeInTheDocument(); fireEvent.click(getByLabelText("Dismiss banner")); diff --git a/ui/litellm-dashboard/src/components/molecules/logo/Logo.test.tsx b/ui/litellm-dashboard/src/components/molecules/logo/Logo.test.tsx index c0b52e03a30..5e4da208f4e 100644 --- a/ui/litellm-dashboard/src/components/molecules/logo/Logo.test.tsx +++ b/ui/litellm-dashboard/src/components/molecules/logo/Logo.test.tsx @@ -10,8 +10,8 @@ describe("Logo", () => { it("renders the bundled logo untouched by the server root path for a known provider", () => { render(); const img = screen.getByRole("img", { name: "openai logo" }); - expect(img.getAttribute("src")).toBe(providerLogoMap[Providers.OpenAI]); - expect(img.getAttribute("src")).toContain("openai_small"); + expect(img).toHaveAttribute("src", providerLogoMap[Providers.OpenAI]); + expect(img).toHaveAttribute("src", expect.stringContaining("openai_small")); }); it("renders a letter avatar and no img for an unknown provider", () => { @@ -29,12 +29,12 @@ describe("Logo", () => { it("resolves a backend asset path through the server root path in src mode", () => { render(); const img = screen.getByRole("img", { name: "GitHub logo" }); - expect(img.getAttribute("src")).toBe("/litellm/ui/assets/logos/github.svg"); + expect(img).toHaveAttribute("src", "/litellm/ui/assets/logos/github.svg"); }); it("passes an external https URL through untouched in src mode", () => { render(); - expect(screen.getByRole("img").getAttribute("src")).toBe("https://cdn.example.com/logo.png"); + expect(screen.getByRole("img")).toHaveAttribute("src", "https://cdn.example.com/logo.png"); }); it("swaps to the letter avatar and warns with the failing URL on image error", () => { @@ -63,7 +63,7 @@ describe("Logo", () => { rerender(); const img = screen.getByRole("img", { name: "Agent logo" }); - expect(img.getAttribute("src")).toBe("/litellm/ui/assets/logos/github.svg"); + expect(img).toHaveAttribute("src", "/litellm/ui/assets/logos/github.svg"); rerender(); expect(screen.queryByRole("img")).not.toBeInTheDocument(); diff --git a/ui/litellm-dashboard/src/components/navbar.test.tsx b/ui/litellm-dashboard/src/components/navbar.test.tsx index 72c15990f20..e3d0e4f0b38 100644 --- a/ui/litellm-dashboard/src/components/navbar.test.tsx +++ b/ui/litellm-dashboard/src/components/navbar.test.tsx @@ -96,7 +96,7 @@ vi.mock("./Navbar/CommunityEngagementButtons/CommunityEngagementButtons", () => let mockUseThemeImpl = () => ({ logoUrl: null as string | null }); let mockUseHealthReadinessDetailsImpl = () => ({ data: null as any }); let mockGetLocalStorageItemImpl = (key: string) => null as string | null; -let mockUseAuthorizedImpl = () => ({ +const mockUseAuthorizedImpl = () => ({ userId: "test-user", userEmail: "test@example.com", userRole: "Admin", diff --git a/ui/litellm-dashboard/src/components/organisms/create_key_button.test.tsx b/ui/litellm-dashboard/src/components/organisms/create_key_button.test.tsx index 8609bb1f82a..4f3cd4455a1 100644 --- a/ui/litellm-dashboard/src/components/organisms/create_key_button.test.tsx +++ b/ui/litellm-dashboard/src/components/organisms/create_key_button.test.tsx @@ -611,7 +611,7 @@ describe("CreateKey", () => { }); await waitFor(() => { - expect(screen.getByTestId("org-dropdown")).not.toBeDisabled(); + expect(screen.getByTestId("org-dropdown")).toBeEnabled(); }); }); diff --git a/ui/litellm-dashboard/src/components/organization/organization_view.test.tsx b/ui/litellm-dashboard/src/components/organization/organization_view.test.tsx index ec7baecad2d..4d96fc84ca9 100644 --- a/ui/litellm-dashboard/src/components/organization/organization_view.test.tsx +++ b/ui/litellm-dashboard/src/components/organization/organization_view.test.tsx @@ -124,9 +124,8 @@ test("renders organization view after loading data", async () => { />, ); - await waitFor(() => { - expect(findAllByText("Acme Corp")).toBeTruthy(); - }); + const [orgName] = await findAllByText("Acme Corp"); + expect(orgName).toBeInTheDocument(); }); test("should display empty state when organization has no members", async () => { diff --git a/ui/litellm-dashboard/src/components/per_user_usage.test.tsx b/ui/litellm-dashboard/src/components/per_user_usage.test.tsx index 443ae66af8f..9cd199d786c 100644 --- a/ui/litellm-dashboard/src/components/per_user_usage.test.tsx +++ b/ui/litellm-dashboard/src/components/per_user_usage.test.tsx @@ -117,8 +117,8 @@ describe("PerUserUsage", () => { const xPositions = new Set(rectangles.map((rect) => rect.getAttribute("d")?.match(/^M\s*([\d.]+)/)?.[1])); expect(xPositions.size).toBe(3); - expect(chart.textContent).toContain("curl/8.0"); - expect(chart.textContent).toContain("Unknown"); + expect(chart).toHaveTextContent("curl/8.0"); + expect(chart).toHaveTextContent("Unknown"); for (const bucket of [ "1-9 requests", "10-99 requests", @@ -127,7 +127,7 @@ describe("PerUserUsage", () => { "10K-99.9K requests", "100K+ requests", ]) { - expect(chart.textContent).toContain(bucket); + expect(chart).toHaveTextContent(bucket); } const tickTexts = Array.from(chart.querySelectorAll(".recharts-cartesian-axis-tick-value")).map( diff --git a/ui/litellm-dashboard/src/components/settings.test.tsx b/ui/litellm-dashboard/src/components/settings.test.tsx index 62efa1dc372..dd45521ab4f 100644 --- a/ui/litellm-dashboard/src/components/settings.test.tsx +++ b/ui/litellm-dashboard/src/components/settings.test.tsx @@ -345,7 +345,7 @@ describe("CallbackSelector logos", () => { expect(await screen.findByAltText("Langfuse logo")).toHaveAttribute("src", "/ui/assets/logos/langfuse.png"); expect(screen.getByAltText("Hosted logo")).toHaveAttribute("src", "https://logos.example.com/hosted.png"); - expect(screen.queryByAltText("NoLogo logo")).toBeNull(); + expect(screen.queryByAltText("NoLogo logo")).not.toBeInTheDocument(); expect(screen.getByText("N")).toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx index 3afdd2849ae..58a0cd94997 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTable.test.tsx @@ -343,7 +343,7 @@ describe("DataTable loading", () => { it("renders skeleton rows while loading and real rows once loaded", () => { const { rerender } = render(); expect(screen.getAllByTestId("skeleton-row").length).toBeGreaterThan(0); - expect(screen.queryByTestId("name-cell")).toBeNull(); + expect(screen.queryByTestId("name-cell")).not.toBeInTheDocument(); rerender(); expect(screen.queryAllByTestId("skeleton-row")).toHaveLength(0); @@ -462,7 +462,7 @@ describe("DataTable column visibility", () => { await user.click(screen.getByTestId("view-options-trigger")); expect(await screen.findByTestId("view-option-email")).toBeInTheDocument(); - expect(screen.queryByTestId("view-option-name")).toBeNull(); + expect(screen.queryByTestId("view-option-name")).not.toBeInTheDocument(); }); }); @@ -615,7 +615,7 @@ describe("DataTable layout", () => { const { container } = render(); expect(container.querySelector("thead")?.className).toContain("sticky"); const scroller = container.querySelector('[data-slot="table-container"]')?.parentElement as HTMLElement; - expect(scroller.style.maxHeight).toBe("240px"); + expect(scroller).toHaveStyle({ maxHeight: "240px" }); }); it("caps fillHeight at the parent's height instead of stretching to it, so a short table stays short", () => { @@ -634,7 +634,7 @@ describe("DataTable layout", () => { expect(frame.className).toContain("flex-col"); expect(scroller.className).toContain("min-h-0"); expect(scroller.className).toContain("overflow-auto"); - expect(scroller.style.maxHeight).toBe(""); + expect(scroller).toHaveStyle({ maxHeight: "" }); // Without this the Table primitive's own overflow container captures the sticky header. expect(scroller.className).toContain("[&_[data-slot=table-container]]:overflow-visible"); @@ -650,7 +650,7 @@ describe("DataTable layout", () => { expect(scroller.className).toContain("overflow-x-auto"); expect(scroller.className).not.toContain("min-h-0"); - expect(scroller.style.maxHeight).toBe(""); + expect(scroller).toHaveStyle({ maxHeight: "" }); expect((scroller.parentElement as HTMLElement).className).not.toContain("flex-col"); expect(container.querySelector("thead")?.className).not.toContain("sticky"); expect(container.querySelector("thead")?.className).not.toContain("bg-background"); diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableFilterDrawer.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableFilterDrawer.test.tsx index 0770c17cba6..c6a8049de55 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableFilterDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableFilterDrawer.test.tsx @@ -68,7 +68,7 @@ describe("DataTableFilterDrawer", () => { await user.type(await screen.findByTestId("draft-name"), "Bob"); expect(names()).toEqual(["Alice", "Bob", "Carol"]); - expect(screen.queryByTestId("filter-chip-name")).toBeNull(); + expect(screen.queryByTestId("filter-chip-name")).not.toBeInTheDocument(); await user.click(screen.getByTestId("filter-drawer-apply")); expect(names()).toEqual(["Bob"]); @@ -92,7 +92,7 @@ describe("DataTableFilterDrawer", () => { await user.click(await screen.findByTestId("filter-drawer-reset")); expect(names()).toEqual(["Alice", "Bob", "Carol"]); - expect(screen.queryByTestId("filter-chip-name")).toBeNull(); + expect(screen.queryByTestId("filter-chip-name")).not.toBeInTheDocument(); expect(screen.getByTestId("draft-name")).toHaveValue(""); }); }); diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.test.tsx index a6164307a78..70403ba2efe 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableSortHeader.test.tsx @@ -64,7 +64,7 @@ function SortHeaderHarness({ variant, canSort = true, onSortingChange }: Harness describe("DataTableSortHeader", () => { it("renders a plain label and no button when the column cannot sort", () => { render(); - expect(screen.queryByTestId("sort-header-name")).toBeNull(); + expect(screen.queryByTestId("sort-header-name")).not.toBeInTheDocument(); expect(screen.getByText("Name")).toBeInTheDocument(); }); diff --git a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.test.tsx b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.test.tsx index 8571b13e4d8..c6181cb90dc 100644 --- a/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/DataTable/DataTableToolbar.test.tsx @@ -66,7 +66,7 @@ describe("DataTableToolbar", () => { const user = userEvent.setup(); render(); await user.click(screen.getByTestId("filter-chip-remove-name")); - expect(screen.queryByTestId("filter-chip-name")).toBeNull(); + expect(screen.queryByTestId("filter-chip-name")).not.toBeInTheDocument(); expect(names()).toEqual(["Alice", "Bob"]); }); @@ -74,7 +74,7 @@ describe("DataTableToolbar", () => { const user = userEvent.setup(); render(); await user.click(screen.getByTestId("datatable-clear-filters")); - expect(screen.queryByTestId("filter-chip-name")).toBeNull(); + expect(screen.queryByTestId("filter-chip-name")).not.toBeInTheDocument(); expect(names()).toEqual(["Alice", "Bob"]); }); diff --git a/ui/litellm-dashboard/src/components/shared/charts/area_chart.test.tsx b/ui/litellm-dashboard/src/components/shared/charts/area_chart.test.tsx index f3ef72392b0..6e4ab14be33 100644 --- a/ui/litellm-dashboard/src/components/shared/charts/area_chart.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/charts/area_chart.test.tsx @@ -23,7 +23,7 @@ describe("AreaChart", () => { it("renders the No data placeholder instead of a chart when data is empty", () => { const { container, getByText } = render(); - expect(getByText("No data")).toBeTruthy(); + expect(getByText("No data")).toBeInTheDocument(); expect(container.querySelector('[data-slot="chart"]')).toBeNull(); }); @@ -37,7 +37,7 @@ describe("AreaChart", () => { const areas = Array.from(container.querySelectorAll("path.recharts-area-area")); expect(areas).toHaveLength(2); for (const area of areas) { - expect(area.getAttribute("fill")).toMatch(/^url\(#fill-/); + expect(area).toHaveAttribute("fill", expect.stringMatching(/^url\(#fill-/)); } }); diff --git a/ui/litellm-dashboard/src/components/shared/charts/bar_chart.test.tsx b/ui/litellm-dashboard/src/components/shared/charts/bar_chart.test.tsx index cb0d5c603a4..a322eeb3ad0 100644 --- a/ui/litellm-dashboard/src/components/shared/charts/bar_chart.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/charts/bar_chart.test.tsx @@ -23,7 +23,7 @@ describe("BarChart", () => { it("renders the No data placeholder instead of a chart when data is empty", () => { const { container, getByText } = render(); - expect(getByText("No data")).toBeTruthy(); + expect(getByText("No data")).toBeInTheDocument(); expect(container.querySelector('[data-slot="chart"]')).toBeNull(); }); diff --git a/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.test.tsx b/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.test.tsx index 7afc7532760..bb0fa985c42 100644 --- a/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/charts/chart_tooltip.test.tsx @@ -29,10 +29,10 @@ describe("CustomTooltip", () => { const inactive = render( , ); - expect(inactive.container.firstChild).toBeNull(); + expect(inactive.container).toBeEmptyDOMElement(); const empty = render(); - expect(empty.container.firstChild).toBeNull(); + expect(empty.container).toBeEmptyDOMElement(); }); it("renders the label and title-cased category names without the metrics prefix", () => { @@ -82,7 +82,7 @@ describe("ValueTooltip", () => { it("returns null when not active", () => { const { container } = render(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("renders label, series name, and locale-formatted value by default", () => { diff --git a/ui/litellm-dashboard/src/components/shared/form/FormField.test.tsx b/ui/litellm-dashboard/src/components/shared/form/FormField.test.tsx index af9122e2bd5..3b37638b0bc 100644 --- a/ui/litellm-dashboard/src/components/shared/form/FormField.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/form/FormField.test.tsx @@ -101,7 +101,7 @@ describe("FormField", () => { const control = await screen.findByLabelText("Team Name"); await waitFor(() => expect(control).toHaveAttribute("aria-invalid", "true")); - expect(control.getAttribute("aria-describedby")).toBe(screen.getByRole("alert").id); + expect(control).toHaveAttribute("aria-describedby", screen.getByRole("alert").id); }); it("leaves a valid control free of aria-invalid", () => { diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.test.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.test.tsx index 1a87f17d50b..e02cbdf941c 100644 --- a/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/table_cells/id_cell.test.tsx @@ -26,22 +26,22 @@ describe("IdCell", () => { render(); const el = screen.getByText("sk-1234567890abcdef"); expect(el.tagName).toBe("SPAN"); - expect(el.className).toContain("bg-blue-50"); - expect(el.className).toContain("font-mono"); - expect(el.className).toContain("max-w-[15ch]"); - expect(el.className).toContain("truncate"); + expect(el).toHaveClass("bg-blue-50"); + expect(el).toHaveClass("font-mono"); + expect(el).toHaveClass("max-w-[15ch]"); + expect(el).toHaveClass("truncate"); }); it("renders plain mono text without pill styling for the plain variant", () => { render(); const el = screen.getByText("req-123"); - expect(el.className).toContain("font-mono"); - expect(el.className).not.toContain("bg-blue-50"); + expect(el).toHaveClass("font-mono"); + expect(el).not.toHaveClass("bg-blue-50"); }); it("does not truncate when truncate is false", () => { render(); - expect(screen.getByText("audit-object-id").className).not.toContain("truncate"); + expect(screen.getByText("audit-object-id")).not.toHaveClass("truncate"); }); it("becomes a button that fires onClick with the id value", async () => { diff --git a/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.test.tsx b/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.test.tsx index 9a91e163baf..1122eede522 100644 --- a/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/table_cells/identity_cell.test.tsx @@ -36,8 +36,8 @@ describe("IdentityCell", () => { const button = screen.getByRole("button"); expect(button.querySelector(".lucide-chevron-right")).not.toBeNull(); // The clickable area must read as clickable: a hover background and a pointer cursor. - expect(button.className).toContain("hover:bg-muted"); - expect(button.className).toContain("cursor-pointer"); + expect(button).toHaveClass("hover:bg-muted"); + expect(button).toHaveClass("cursor-pointer"); await user.click(button); expect(onClick).toHaveBeenCalledTimes(1); }); @@ -48,7 +48,7 @@ describe("IdentityCell", () => { const link = screen.getByRole("link", { name: /routing-qa-key-alpha/ }); expect(link).toHaveAttribute("href", "/api-keys?key=abc123"); expect(link.querySelector(".lucide-chevron-right")).not.toBeNull(); - expect(link.className).toContain("hover:bg-muted"); + expect(link).toHaveClass("hover:bg-muted"); await user.click(link); expect(routerPush).toHaveBeenCalledWith("/api-keys?key=abc123"); }); diff --git a/ui/litellm-dashboard/src/components/team/LoggingSettings.test.tsx b/ui/litellm-dashboard/src/components/team/LoggingSettings.test.tsx index f998d7d3ecf..83c541afd15 100644 --- a/ui/litellm-dashboard/src/components/team/LoggingSettings.test.tsx +++ b/ui/litellm-dashboard/src/components/team/LoggingSettings.test.tsx @@ -135,7 +135,7 @@ describe("LoggingSettings", () => { renderWithProviders(); expect(screen.getByText("Custom Callback API Configuration")).toBeInTheDocument(); - expect(screen.queryByAltText("Custom Callback API logo")).toBeNull(); + expect(screen.queryByAltText("Custom Callback API logo")).not.toBeInTheDocument(); expect(screen.getByText("C")).toBeInTheDocument(); }); diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx index 22481eaa4b2..1d6c46dc0c9 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.test.tsx @@ -816,7 +816,7 @@ describe("TeamInfoView", () => { const secretField = await screen.findByPlaceholderText( '{"namespace": "admin", "mount": "secret", "path_prefix": "litellm"}', ); - expect(secretField).not.toBeDisabled(); + expect(secretField).toBeEnabled(); }); it("should add team member when form is submitted", async () => { diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx index f71b2a05f3f..2cefe33eb8d 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.test.tsx @@ -212,7 +212,7 @@ describe("TeamVirtualKeysTable", () => { renderWithProviders(); - await waitFor(() => expect(screen.getByTestId("sort-header-created_at")).toBeInTheDocument()); + expect(await screen.findByTestId("sort-header-created_at")).toBeInTheDocument(); await user.click(screen.getByTestId("sort-header-created_at")); await waitFor(() => diff --git a/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.test.tsx b/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.test.tsx index b81495e68e8..cc876758f74 100644 --- a/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.test.tsx +++ b/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.test.tsx @@ -161,7 +161,7 @@ describe("KeyInfoHeader", () => { it("should not disable Regenerate button by default", () => { render(); - expect(screen.getByRole("button", { name: /regenerate key/i })).not.toBeDisabled(); + expect(screen.getByRole("button", { name: /regenerate key/i })).toBeEnabled(); }); }); diff --git a/ui/litellm-dashboard/src/components/user_agent_activity.test.tsx b/ui/litellm-dashboard/src/components/user_agent_activity.test.tsx index 07a03f4addf..79bb63da31a 100644 --- a/ui/litellm-dashboard/src/components/user_agent_activity.test.tsx +++ b/ui/litellm-dashboard/src/components/user_agent_activity.test.tsx @@ -206,9 +206,9 @@ describe("UserAgentActivity", () => { const xPositions = new Set(rectangles.map((rect) => rect.getAttribute("d")?.match(/^M\s*([\d.]+)/)?.[1])); expect(xPositions.size).toBe(1); - expect(chart.textContent).toContain("Chrome/1.0"); - expect(chart.textContent).toContain("Firefox/2.0"); - expect(chart.textContent).toContain(firstBucketLabel); + expect(chart).toHaveTextContent(/Chrome\/1\.0/); + expect(chart).toHaveTextContent(/Firefox\/2\.0/); + expect(chart).toHaveTextContent(new RegExp(firstBucketLabel)); const tickTexts = Array.from(chart.querySelectorAll(".recharts-cartesian-axis-tick-value")).map( (tick) => tick.textContent ?? "", diff --git a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreSelector.test.tsx b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreSelector.test.tsx index 07468abc582..52c51a8ecc4 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreSelector.test.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreSelector.test.tsx @@ -307,21 +307,21 @@ describe("VectorStoreSelector", () => { renderComponent({ value: ["store-1", "store-2"] }); await waitForDataFetch(); - expect(getSelectElement().getAttribute("data-value")).toBe(JSON.stringify(["store-1", "store-2"])); + expect(getSelectElement()).toHaveAttribute("data-value", JSON.stringify(["store-1", "store-2"])); }); it("should handle empty value array", async () => { renderComponent({ value: [] }); await waitForDataFetch(); - expect(getSelectElement().getAttribute("data-value")).toBe(JSON.stringify([])); + expect(getSelectElement()).toHaveAttribute("data-value", JSON.stringify([])); }); it("should handle undefined value", async () => { renderComponent({ value: undefined }); await waitForDataFetch(); - expect(getSelectElement().getAttribute("data-value")).toBeNull(); + expect(getSelectElement()).not.toHaveAttribute("data-value"); }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/AuditLogsTable.test.tsx b/ui/litellm-dashboard/src/components/view_logs/AuditLogsTable.test.tsx index dbb0a39e2ee..8bc771ee298 100644 --- a/ui/litellm-dashboard/src/components/view_logs/AuditLogsTable.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/AuditLogsTable.test.tsx @@ -95,7 +95,7 @@ describe("AuditLogsTable", () => { renderTable({ isLoading: true, data: [] }); expect(screen.getAllByTestId("skeleton-row").length).toBeGreaterThan(0); - expect(screen.queryByText("No audit logs yet")).toBeNull(); + expect(screen.queryByText("No audit logs yet")).not.toBeInTheDocument(); }); it("uses a distinct empty state for unfiltered vs filtered-empty results", () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.test.tsx b/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.test.tsx index ad9b724ba80..2e93b10e3ae 100644 --- a/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.test.tsx @@ -10,7 +10,7 @@ describe("ConfigInfoMessage", () => { it("should render nothing when show is false", () => { const { container } = render(); - expect(container.innerHTML).toBe(""); + expect(container).toBeEmptyDOMElement(); }); it("should display the YAML config snippet", () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/CollapsibleMessage.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/CollapsibleMessage.test.tsx index 3aa8d21c31c..377615d3cb8 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/CollapsibleMessage.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/CollapsibleMessage.test.tsx @@ -7,12 +7,12 @@ import { CollapsibleMessage } from "./CollapsibleMessage"; describe("CollapsibleMessage", () => { it("should return null when content is empty", () => { const { container } = render(); - expect(container.innerHTML).toBe(""); + expect(container).toBeEmptyDOMElement(); }); it("should return null when content is undefined", () => { const { container } = render(); - expect(container.innerHTML).toBe(""); + expect(container).toBeEmptyDOMElement(); }); it("should render the label and char count", () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/HistoryTree.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/HistoryTree.test.tsx index 0726a9f4b82..07796de6927 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/HistoryTree.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/HistoryTree.test.tsx @@ -8,7 +8,7 @@ import { ParsedMessage } from "./prettyMessagesTypes"; describe("HistoryTree", () => { it("should return null when messages array is empty", () => { const { container } = render(); - expect(container.innerHTML).toBe(""); + expect(container).toBeEmptyDOMElement(); }); it('should render message count with plural "messages" for multiple messages', () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/InputCard.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/InputCard.test.tsx index 5bf97b16233..df9d00a05d6 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/InputCard.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/InputCard.test.tsx @@ -42,7 +42,7 @@ describe("InputCard", () => { it("should return null when messages array is empty", () => { const { container } = render(); - expect(container.firstChild).toBeNull(); + expect(container).toBeEmptyDOMElement(); }); it("should display system message when present", () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx index b913f7c8d21..b96e5279722 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx @@ -153,7 +153,7 @@ describe("LogDetailsDrawer session sidebar auto-router icon", () => { it("marks the auto-routed entry with the router icon and leaves a direct call on the default icon", async () => { renderRoutedSession(); - await waitFor(() => expect(screen.queryByText("claude-opus-4-8")).not.toBeNull()); + expect(await screen.findByText("claude-opus-4-8")).toBeInTheDocument(); const routedRow = rowFor("claude-opus-4-8"); const directRow = rowFor("claude-haiku-4-5"); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/SimpleMessageBlock.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/SimpleMessageBlock.test.tsx index c4be05f0e9c..ecd2e0fdf2a 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/SimpleMessageBlock.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/SimpleMessageBlock.test.tsx @@ -12,12 +12,12 @@ describe("SimpleMessageBlock", () => { it("should return null when content is empty and no tool calls", () => { const { container } = render(); - expect(container.innerHTML).toBe(""); + expect(container).toBeEmptyDOMElement(); }); it('should return null when content is "null" string and no tool calls', () => { const { container } = render(); - expect(container.innerHTML).toBe(""); + expect(container).toBeEmptyDOMElement(); }); it("should render tool calls when present", () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.test.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.test.tsx index 852f146c786..9e2abec4716 100644 --- a/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/RequestLogsPanel.test.tsx @@ -210,15 +210,16 @@ describe("RequestLogsPanel", () => { await user.click(screen.getByRole("button", { name: /Last 24 Hours/i })); await user.click(await screen.findByRole("button", { name: "Last 15 Minutes" })); - await waitFor(() => { + const windowSeconds = () => { const call = lastCall(); if (!call) throw new Error("no call"); - const diff = moment + return moment .utc(call.end_date, "YYYY-MM-DD HH:mm:ss") .diff(moment.utc(call.start_date, "YYYY-MM-DD HH:mm:ss"), "seconds"); - expect(diff).toBeGreaterThanOrEqual(15 * 60); - expect(diff).toBeLessThanOrEqual(16 * 60); - }); + }; + + await waitFor(() => expect(windowSeconds()).toBeGreaterThanOrEqual(15 * 60)); + expect(windowSeconds()).toBeLessThanOrEqual(16 * 60); }); it("restores the default 24 hour window when filters are reset", async () => { @@ -228,7 +229,7 @@ describe("RequestLogsPanel", () => { await waitFor(() => expect(uiSpendLogsCall).toHaveBeenCalled()); await user.click(screen.getByRole("button", { name: /Last 24 Hours/i })); await user.click(await screen.findByRole("button", { name: "Last 15 Minutes" })); - await waitFor(() => expect(screen.getByRole("button", { name: /Last 15 Minutes/i })).toBeInTheDocument()); + expect(await screen.findByRole("button", { name: /Last 15 Minutes/i })).toBeInTheDocument(); await user.click(screen.getByRole("button", { name: "Reset Filters" })); @@ -262,8 +263,8 @@ describe("RequestLogsPanel", () => { expect(historyModes()).toEqual(["push"]); await waitFor(() => { expect(drawer()).toHaveTextContent("open"); - expect(drawer()).toHaveAttribute("data-log-id", "req-1"); }); + expect(drawer()).toHaveAttribute("data-log-id", "req-1"); }); it("opens the drawer on load when ?log_id= matches a log in the loaded page", async () => { @@ -272,8 +273,8 @@ describe("RequestLogsPanel", () => { await waitFor(() => { expect(drawer()).toHaveTextContent("open"); - expect(drawer()).toHaveAttribute("data-log-id", "req-2"); }); + expect(drawer()).toHaveAttribute("data-log-id", "req-2"); }); it("fetches the log by request_id and opens the drawer when it is not in the loaded page", async () => { @@ -286,8 +287,8 @@ describe("RequestLogsPanel", () => { await waitFor(() => { expect(drawer()).toHaveTextContent("open"); - expect(drawer()).toHaveAttribute("data-log-id", "req-old"); }); + expect(drawer()).toHaveAttribute("data-log-id", "req-old"); const byIdCall = vi .mocked(uiSpendLogsCall) @@ -344,8 +345,8 @@ describe("RequestLogsPanel", () => { expect(historyModes()).toEqual(["push"]); await waitFor(() => { expect(drawer()).toHaveTextContent("open"); - expect(drawer()).toHaveAttribute("data-session-id", "sess-solo"); }); + expect(drawer()).toHaveAttribute("data-session-id", "sess-solo"); }); it("clicking a log row clears a lingering ?session_id= so the drawer shows the clicked log", async () => { @@ -366,8 +367,8 @@ describe("RequestLogsPanel", () => { expect(urlParams().get("session_id")).toBeNull(); await waitFor(() => { expect(drawer()).toHaveAttribute("data-log-id", "req-b"); - expect(drawer()).toHaveAttribute("data-session-id", ""); }); + expect(drawer()).toHaveAttribute("data-session-id", ""); }); it("closing a drawer opened via a session id clears both params", async () => { @@ -394,9 +395,9 @@ describe("RequestLogsPanel", () => { await waitFor(() => { expect(drawer()).toHaveTextContent("open"); - expect(drawer()).toHaveAttribute("data-log-id", "req-llm"); - expect(drawer()).toHaveAttribute("data-session-id", "sess-1"); }); + expect(drawer()).toHaveAttribute("data-session-id", "sess-1"); + expect(drawer()).toHaveAttribute("data-log-id", "req-llm"); }); it("clicking a multi-call session's row writes ?session_id= alongside ?log_id=", async () => { @@ -442,7 +443,7 @@ describe("RequestLogsPanel", () => { await user.click(screen.getByRole("button", { name: "Stop" })); - expect(screen.queryByText("Auto-refreshing every 15 seconds")).toBeNull(); + expect(screen.queryByText("Auto-refreshing every 15 seconds")).not.toBeInTheDocument(); }); }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx index 2fdc8455ca1..3d0ea03c5d5 100644 --- a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx @@ -103,7 +103,7 @@ describe("sortable headers", () => { expect(screen.getByTestId(`sort-trigger-${field}`)).toBeInTheDocument(); } for (const field of ["request_id", "session_id", "status", "type", "end_user"]) { - expect(screen.queryByTestId(`sort-trigger-${field}`)).toBeNull(); + expect(screen.queryByTestId(`sort-trigger-${field}`)).not.toBeInTheDocument(); } }); }); diff --git a/ui/litellm-dashboard/src/contexts/PluginModeContext.test.tsx b/ui/litellm-dashboard/src/contexts/PluginModeContext.test.tsx index 96576269fbe..f0af7313a2c 100644 --- a/ui/litellm-dashboard/src/contexts/PluginModeContext.test.tsx +++ b/ui/litellm-dashboard/src/contexts/PluginModeContext.test.tsx @@ -39,15 +39,15 @@ describe("PluginModeProvider effectiveMode fallback", () => { renderWithPlugins([]); await waitFor(() => expect(getMock).toHaveBeenCalled()); - await waitFor(() => expect(screen.getByTestId("mode").textContent).toBe("ai-gateway")); - expect(screen.getByTestId("active").textContent).toBe("none"); + await waitFor(() => expect(screen.getByTestId("mode")).toHaveTextContent("ai-gateway")); + expect(screen.getByTestId("active")).toHaveTextContent("none"); }); it("keeps the stored mode when it is still registered", async () => { renderWithPlugins([{ name: "my-plugin", display_name: "My Plugin", url: "https://p.example.com" }]); - await waitFor(() => expect(screen.getByTestId("active").textContent).toBe("my-plugin")); - expect(screen.getByTestId("mode").textContent).toBe("my-plugin"); + await waitFor(() => expect(screen.getByTestId("active")).toHaveTextContent("my-plugin")); + expect(screen.getByTestId("mode")).toHaveTextContent("my-plugin"); }); it("falls back to ai-gateway when the plugins fetch fails, never stranding the user", async () => { @@ -59,6 +59,6 @@ describe("PluginModeProvider effectiveMode fallback", () => { ); await waitFor(() => expect(getMock).toHaveBeenCalled()); - await waitFor(() => expect(screen.getByTestId("mode").textContent).toBe("ai-gateway")); + await waitFor(() => expect(screen.getByTestId("mode")).toHaveTextContent("ai-gateway")); }); }); diff --git a/ui/litellm-dashboard/src/utils/dataUtils.test.ts b/ui/litellm-dashboard/src/utils/dataUtils.test.ts index 14eba8a7edd..f9e2be81163 100644 --- a/ui/litellm-dashboard/src/utils/dataUtils.test.ts +++ b/ui/litellm-dashboard/src/utils/dataUtils.test.ts @@ -225,9 +225,11 @@ describe("dataUtils", () => { await copyToClipboard("test text"); expect(mockTextArea.value).toBe("test text"); + /* eslint-disable jest-dom/prefer-to-have-style -- the subject is a plain mock object, not a DOM node, so toHaveStyle cannot read it */ expect(mockTextArea.style.position).toBe("fixed"); expect(mockTextArea.style.left).toBe("-999999px"); expect(mockTextArea.style.top).toBe("-999999px"); + /* eslint-enable jest-dom/prefer-to-have-style */ expect(mockTextArea.setAttribute).toHaveBeenCalledWith("readonly", ""); expect(mockTextArea.focus).toHaveBeenCalled(); expect(mockTextArea.select).toHaveBeenCalled();