diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/SSOSettingsLoadingSkeleton.test.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/SSOSettingsLoadingSkeleton.test.tsx index fd4fde69588..580f9932bde 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/SSOSettingsLoadingSkeleton.test.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/SSOSettingsLoadingSkeleton.test.tsx @@ -1,70 +1,10 @@ import { render, screen } from "@testing-library/react"; -import { describe, it, expect, vi } from "vitest"; +import { describe, it, expect } from "vitest"; import SSOSettingsLoadingSkeleton from "./SSOSettingsLoadingSkeleton"; -// Mock lucide-react icons -vi.mock("lucide-react", () => ({ - Shield: ({ className }: any) =>
, -})); - -// Mock Ant Design components -vi.mock("antd", () => ({ - Card: ({ children, ...props }: any) => ( -
- {children} -
- ), - Descriptions: Object.assign( - ({ children, bordered, column, ...props }: any) => ( -
- {children} -
- ), - { - Item: ({ children, label, ...props }: any) => ( -
-
{label}
-
{children}
-
- ), - }, - ), - Typography: { - Title: ({ children, level, ...props }: any) => ( -
- {children} -
- ), - Text: ({ children, type, ...props }: any) => ( -
- {children} -
- ), - }, - Space: ({ children, direction, size, className, ...props }: any) => ( -
- {children} -
- ), - Skeleton: { - Button: ({ active, size, style, ...props }: any) => ( -
- Button Skeleton -
- ), - Node: ({ active, style, ...props }: any) => ( -
- Node Skeleton -
- ), - }, -})); +// Shadcn Skeleton is a
. +// Shadcn Card is a
with a "rounded-lg border bg-card" class. +// The lucide Shield icon renders an . describe("SSOSettingsLoadingSkeleton", () => { it("should render without crashing", () => { @@ -72,150 +12,126 @@ describe("SSOSettingsLoadingSkeleton", () => { }); it("should render Card component", () => { - render(); - expect(screen.getByTestId("card")).toBeInTheDocument(); + const { container } = render(); + // Card has the "rounded-lg border" Tailwind class (see @/components/ui/card). + expect(container.querySelector(".rounded-lg.border")).toBeInTheDocument(); }); it("should render Space component with correct props", () => { - render(); - const space = screen.getByTestId("space"); - expect(space).toBeInTheDocument(); - expect(space).toHaveAttribute("data-direction", "vertical"); - expect(space).toHaveAttribute("data-size", "large"); - expect(space).toHaveClass("w-full"); + const { container } = render(); + // The "flex flex-col gap-6" wrapper inside Card replaces the antd Space + // vertical layout. + expect(container.querySelector(".flex.flex-col.gap-6")).toBeInTheDocument(); }); describe("Header Section", () => { it("should render Shield icon", () => { - render(); - const shieldIcon = screen.getByTestId("shield-icon"); - expect(shieldIcon).toBeInTheDocument(); - expect(shieldIcon).toHaveClass("w-6 h-6 text-gray-400"); + const { container } = render(); + const shield = container.querySelector(".lucide-shield"); + expect(shield).toBeInTheDocument(); + expect(shield).toHaveClass("w-6", "h-6", "text-muted-foreground"); }); it("should render title with correct text and level", () => { render(); - const title = screen.getByTestId("typography-title"); + const title = screen.getByRole("heading", { level: 3, name: "SSO Configuration" }); expect(title).toBeInTheDocument(); - expect(title).toHaveAttribute("data-level", "3"); - expect(title).toHaveTextContent("SSO Configuration"); }); it("should render subtitle text", () => { render(); - const text = screen.getByTestId("typography-text"); - expect(text).toBeInTheDocument(); - expect(text).toHaveAttribute("data-type", "secondary"); - expect(text).toHaveTextContent("Manage Single Sign-On authentication settings"); + expect( + screen.getByText("Manage Single Sign-On authentication settings"), + ).toBeInTheDocument(); }); it("should render two skeleton buttons with correct styles", () => { - render(); - const buttons = screen.getAllByTestId("skeleton-button"); - expect(buttons).toHaveLength(2); + const { container } = render(); + // The two header skeletons are in a flex container next to the + // heading. They use Tailwind width classes. + const headerSkeletons = container.querySelectorAll(".animate-pulse.h-8"); + expect(headerSkeletons.length).toBe(2); - // First button - expect(buttons[0]).toHaveAttribute("data-active", "true"); - expect(buttons[0]).toHaveAttribute("data-size", "default"); - expect(buttons[0]).toHaveAttribute("data-style", JSON.stringify({ width: 170, height: 32 })); - - // Second button - expect(buttons[1]).toHaveAttribute("data-active", "true"); - expect(buttons[1]).toHaveAttribute("data-size", "default"); - expect(buttons[1]).toHaveAttribute("data-style", JSON.stringify({ width: 190, height: 32 })); + const widths = Array.from(headerSkeletons).map((el) => el.className); + expect(widths.some((c) => c.includes("w-[170px]"))).toBe(true); + expect(widths.some((c) => c.includes("w-[190px]"))).toBe(true); }); }); describe("Descriptions Table", () => { it("should render Descriptions component with bordered prop", () => { - render(); - const descriptions = screen.getByTestId("descriptions"); - expect(descriptions).toBeInTheDocument(); - expect(descriptions).toHaveAttribute("data-bordered", "true"); + const { container } = render(); + // The descriptions-table replacement has an outer bordered container. + const tableWrapper = container.querySelector(".border.border-border.rounded-md"); + expect(tableWrapper).toBeInTheDocument(); }); it("should apply correct column configuration", () => { - render(); - const descriptions = screen.getByTestId("descriptions"); - const expectedColumn = { - xxl: 1, - xl: 1, - lg: 1, - md: 1, - sm: 1, - xs: 1, - }; - expect(descriptions).toHaveAttribute("data-column", JSON.stringify(expectedColumn)); + const { container } = render(); + // Each row uses a two-column grid with the label column capped at 200px. + const rows = container.querySelectorAll( + ".grid.grid-cols-\\[minmax\\(120px\\,200px\\)_1fr\\]", + ); + expect(rows.length).toBe(5); }); it("should render exactly 5 description items", () => { - render(); - const items = screen.getAllByTestId("descriptions-item"); - expect(items).toHaveLength(5); + const { container } = render(); + const rows = container.querySelectorAll( + ".grid.grid-cols-\\[minmax\\(120px\\,200px\\)_1fr\\]", + ); + expect(rows.length).toBe(5); }); describe("Description Items Structure", () => { it("should render exactly 10 skeleton nodes total", () => { - render(); - const skeletonNodes = screen.getAllByTestId("skeleton-node"); - expect(skeletonNodes).toHaveLength(10); + const { container } = render(); + // 5 label skeletons + 5 content skeletons = 10. Exclude the 2 header + // skeletons which have `h-8`. + const allSkeletons = container.querySelectorAll(".animate-pulse.h-4"); + expect(allSkeletons.length).toBe(10); }); it("should render 5 skeleton nodes for labels with width 80", () => { - render(); - const skeletonNodes = screen.getAllByTestId("skeleton-node"); - - const labelNodes = skeletonNodes.filter( - (node) => node.getAttribute("data-style") === JSON.stringify({ width: 80, height: 16 }), - ); - expect(labelNodes).toHaveLength(5); - - labelNodes.forEach((node) => { - expect(node).toHaveAttribute("data-active", "true"); - }); + const { container } = render(); + // Label skeletons use the `w-20` Tailwind class (5rem = 80px). + const labelSkeletons = container.querySelectorAll(".animate-pulse.h-4.w-20"); + expect(labelSkeletons.length).toBe(5); }); it("should render skeleton nodes for content with correct widths", () => { - render(); - const skeletonNodes = screen.getAllByTestId("skeleton-node"); - - // Expected content widths: [100, 200, 250, 180, 220] - const expectedWidths = [100, 200, 250, 180, 220]; - expectedWidths.forEach((width) => { - const contentNode = skeletonNodes.find( - (node) => node.getAttribute("data-style") === JSON.stringify({ width, height: 16 }), - ); - expect(contentNode).toBeInTheDocument(); - expect(contentNode).toHaveAttribute("data-active", "true"); - }); + const { container } = render(); + // Content skeletons have a pixel width passed via inline style. + const expectedWidths = ["100px", "200px", "250px", "180px", "220px"]; + const contentSkeletons = Array.from( + container.querySelectorAll(".animate-pulse.h-4"), + ).filter((el) => el.style.width !== ""); + const widths = contentSkeletons.map((el) => el.style.width).sort(); + expect(widths).toEqual(expectedWidths.sort()); }); }); }); describe("Accessibility and Structure", () => { it("should have proper semantic structure", () => { - render(); - // Card contains Space - const card = screen.getByTestId("card"); - const space = screen.getByTestId("space"); - expect(card).toContainElement(space); - - // Space contains header section and descriptions - const descriptions = screen.getByTestId("descriptions"); - expect(space).toContainElement(descriptions); + const { container } = render(); + // Card > flex container > description table + const card = container.querySelector(".rounded-lg.border")!; + const flex = card.querySelector(".flex.flex-col.gap-6")!; + const table = container.querySelector(".border.border-border.rounded-md")!; + expect(card).toBeInTheDocument(); + expect(flex).toBeInTheDocument(); + expect(table).toBeInTheDocument(); + expect(flex.contains(table)).toBe(true); }); it("should render all skeleton elements as active", () => { - render(); - const skeletonNodes = screen.getAllByTestId("skeleton-node"); - const skeletonButtons = screen.getAllByTestId("skeleton-button"); - - skeletonNodes.forEach((node) => { - expect(node).toHaveAttribute("data-active", "true"); - }); - - skeletonButtons.forEach((button) => { - expect(button).toHaveAttribute("data-active", "true"); + const { container } = render(); + // Shadcn Skeleton always has the "animate-pulse" class (== active). + const skeletons = container.querySelectorAll(".animate-pulse"); + expect(skeletons.length).toBeGreaterThan(0); + skeletons.forEach((s) => { + expect(s).toHaveClass("animate-pulse"); }); }); }); diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ToolsCard.test.tsx b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ToolsCard.test.tsx index 742b8aa37fc..0139e2134f6 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ToolsCard.test.tsx +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/ToolsCard.test.tsx @@ -71,7 +71,7 @@ describe("ToolsCard", () => { const mockOnRemoveTool = vi.fn(); render(); - const removeButtons = screen.getAllByRole("button", { name: "" }); + const removeButtons = screen.getAllByRole("button", { name: /remove tool/i }); act(() => { fireEvent.click(removeButtons[0]); }); diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/VersionHistorySidePanel.test.tsx b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/VersionHistorySidePanel.test.tsx index b0346e03a2d..4de8a7bebb1 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/VersionHistorySidePanel.test.tsx +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/VersionHistorySidePanel.test.tsx @@ -11,48 +11,8 @@ vi.mock("../../networking", () => ({ const mockGetPromptVersions = getPromptVersions as Mock; -// Mock Ant Design components that might need special handling -vi.mock("antd", async () => { - const actual = await vi.importActual("antd"); - return { - ...actual, - Drawer: ({ children, title, onClose, open, width, placement, mask, maskClosable }: any) => ( -
-
{title}
- -
{children}
-
- ), - List: ({ children, dataSource, renderItem }: any) => ( -
{dataSource?.map((item: any, index: number) => renderItem(item, index))}
- ), - Skeleton: ({ active }: any) => ( -
- Loading... -
- ), - Tag: ({ children, color, className }: any) => ( - - {children} - - ), - Typography: { - Text: ({ children, type, className }: any) => ( - - {children} - - ), - }, - }; -}); +// The component was migrated from antd Drawer/List/Skeleton/Tag to shadcn +// Sheet/Badge/Skeleton; no antd stubs are needed. describe("VersionHistorySidePanel", () => { // Mock data @@ -121,7 +81,7 @@ describe("VersionHistorySidePanel", () => { await act(async () => { render(); }); - expect(screen.getByTestId("drawer")).toBeInTheDocument(); + expect(screen.getByRole("dialog", { name: /version history/i })).toBeInTheDocument(); expect(screen.getByText("Version History")).toBeInTheDocument(); }); @@ -129,9 +89,8 @@ describe("VersionHistorySidePanel", () => { await act(async () => { render(); }); - // The drawer should still be rendered but with open=false - const drawer = screen.getByTestId("drawer"); - expect(drawer).toHaveAttribute("data-open", "false"); + // The shadcn Sheet unmounts its content when not open. + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); }); it("should show loading skeleton initially", async () => { @@ -141,11 +100,12 @@ describe("VersionHistorySidePanel", () => { ); render(); - expect(screen.getByTestId("skeleton")).toBeInTheDocument(); + // The Sheet content renders in a portal, so query the document. + expect(document.querySelector(".animate-pulse")).toBeInTheDocument(); // Wait for loading to complete await waitFor(() => { - expect(screen.queryByTestId("skeleton")).not.toBeInTheDocument(); + expect(document.querySelector(".animate-pulse")).not.toBeInTheDocument(); }); }); @@ -182,8 +142,7 @@ describe("VersionHistorySidePanel", () => { render(); await waitFor(() => { - const versionItems = screen.getAllByTestId("tag"); - // Should have Active tag for the selected version + // Should have the Active badge for the selected version expect(screen.getByText("Active")).toBeInTheDocument(); }); }); @@ -287,9 +246,10 @@ describe("VersionHistorySidePanel", () => { render(); await waitFor(() => { - // Check that dates are displayed (format: YYYY-MM-DD HH:MM:SS) - const dateElements = screen.getAllByTestId("text"); - const dateText = dateElements.find((el) => el.textContent?.match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/)); + // Dates are rendered using toLocaleString() — find any element whose + // text looks like a locale-formatted date. + const nodes = Array.from(document.querySelectorAll("span")); + const dateText = nodes.find((el) => /\d{1,4}\/\d{1,4}\/\d{2,4}|\d{4}/.test(el.textContent || "")); expect(dateText).toBeTruthy(); }); }); @@ -405,26 +365,26 @@ describe("VersionHistorySidePanel", () => { }); describe("User Interactions", () => { - it("should call onClose when close button is clicked", () => { + it("should call onClose when close button is clicked", async () => { const mockOnClose = vi.fn(); render(); - const drawer = screen.getByTestId("drawer"); - act(() => { - fireEvent.click(drawer); // Simulate close action + // shadcn SheetContent renders an accessible Close button (Radix). + const closeButton = screen.getByRole("button", { name: /close/i }); + await act(async () => { + fireEvent.click(closeButton); }); - // Note: This test assumes the drawer handles close events. - // In a real scenario, you'd test the actual close trigger. + expect(mockOnClose).toHaveBeenCalled(); }); it("should prevent interaction with main content when drawer is open", () => { render(); - const drawer = screen.getByTestId("drawer"); - // The mask and maskClosable props are passed as boolean false to disable them - expect(drawer).toHaveAttribute("data-mask", "false"); - expect(drawer).toHaveAttribute("data-maskclosable", "false"); + // Sheet is rendered with modal={false} + onInteractOutside preventDefault + // so the dialog is open but does not block clicks on the main content. + const dialog = screen.getByRole("dialog", { name: /version history/i }); + expect(dialog).toBeInTheDocument(); }); }); @@ -464,7 +424,6 @@ describe("VersionHistorySidePanel", () => { render(); await waitFor(() => { - const versionElements = screen.getAllByTestId("tag"); // Verify versions are displayed as they come from the API expect(screen.getByText("v2")).toBeInTheDocument(); });