diff --git a/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.test.tsx b/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.test.tsx
new file mode 100644
index 0000000000..17b8b39360
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.test.tsx
@@ -0,0 +1,134 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi, beforeEach } from "vitest";
+
+// Mock the useWorker hook
+const mockUseWorker = vi.fn();
+vi.mock("@/hooks/useWorker", () => ({
+ useWorker: () => mockUseWorker(),
+}));
+
+// Mock antd Select
+vi.mock("antd", () => ({
+ Select: ({ value, options, onChange, style, disabled, ...props }: any) => (
+
+ ),
+}));
+
+// Mock icon
+vi.mock("@ant-design/icons", () => ({
+ CloudServerOutlined: () => ,
+}));
+
+import WorkerDropdown from "./WorkerDropdown";
+
+describe("WorkerDropdown", () => {
+ const mockOnWorkerSwitch = vi.fn();
+ const workers = [
+ { worker_id: "w1", name: "Worker 1" },
+ { worker_id: "w2", name: "Worker 2" },
+ { worker_id: "w3", name: "Worker 3" },
+ ];
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it("renders null when isControlPlane is false", () => {
+ mockUseWorker.mockReturnValue({
+ isControlPlane: false,
+ selectedWorker: workers[0],
+ workers,
+ });
+
+ const { container } = render();
+ expect(container).toBeEmptyDOMElement();
+ });
+
+ it("renders null when selectedWorker is null", () => {
+ mockUseWorker.mockReturnValue({
+ isControlPlane: true,
+ selectedWorker: null,
+ workers,
+ });
+
+ const { container } = render();
+ expect(container).toBeEmptyDOMElement();
+ });
+
+ it("renders the select when isControlPlane and selectedWorker exist", () => {
+ mockUseWorker.mockReturnValue({
+ isControlPlane: true,
+ selectedWorker: workers[0],
+ workers,
+ });
+
+ render();
+ expect(screen.getByTestId("worker-select")).toBeInTheDocument();
+ });
+
+ it("renders all worker options", () => {
+ mockUseWorker.mockReturnValue({
+ isControlPlane: true,
+ selectedWorker: workers[0],
+ workers,
+ });
+
+ render();
+ expect(screen.getByText("Worker 1")).toBeInTheDocument();
+ expect(screen.getByText("Worker 2")).toBeInTheDocument();
+ expect(screen.getByText("Worker 3")).toBeInTheDocument();
+ });
+
+ it("sets current worker as selected value", () => {
+ mockUseWorker.mockReturnValue({
+ isControlPlane: true,
+ selectedWorker: workers[1],
+ workers,
+ });
+
+ render();
+ const select = screen.getByTestId("worker-select") as HTMLSelectElement;
+ expect(select.value).toBe("w2");
+ });
+
+ it("disables the currently selected worker in options", () => {
+ mockUseWorker.mockReturnValue({
+ isControlPlane: true,
+ selectedWorker: workers[0],
+ workers,
+ });
+
+ render();
+ const options = screen.getAllByRole("option");
+ const selectedOption = options.find((opt) => (opt as HTMLOptionElement).value === "w1");
+ expect(selectedOption).toBeDisabled();
+ });
+
+ it("calls onWorkerSwitch when selection changes", async () => {
+ mockUseWorker.mockReturnValue({
+ isControlPlane: true,
+ selectedWorker: workers[0],
+ workers,
+ });
+
+ render();
+ const select = screen.getByTestId("worker-select");
+
+ const { default: userEvent } = await import("@testing-library/user-event");
+ const user = userEvent.setup();
+ await user.selectOptions(select, "w2");
+
+ expect(mockOnWorkerSwitch).toHaveBeenCalledWith("w2");
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/agent_management/AgentSelector.test.tsx b/ui/litellm-dashboard/src/components/agent_management/AgentSelector.test.tsx
new file mode 100644
index 0000000000..26f903cd31
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/agent_management/AgentSelector.test.tsx
@@ -0,0 +1,144 @@
+import { render, screen, waitFor } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { describe, expect, it, vi, beforeEach } from "vitest";
+
+// Mock networking module
+const mockGetAgentsList = vi.fn();
+vi.mock("../networking", () => ({
+ getAgentsList: (...args: any[]) => mockGetAgentsList(...args),
+}));
+
+// Mock antd Select
+vi.mock("antd", () => {
+ const SelectComponent = ({ children, onChange, value, mode, placeholder, loading, disabled, ...props }: any) => (
+
+
+ {loading && Loading}
+
+ );
+
+ SelectComponent.Option = ({ children, value, ...props }: any) => (
+
+ );
+
+ return { Select: SelectComponent };
+});
+
+import AgentSelector from "./AgentSelector";
+
+describe("AgentSelector", () => {
+ const defaultProps = {
+ onChange: vi.fn(),
+ accessToken: "test-token",
+ };
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ mockGetAgentsList.mockResolvedValue({
+ agents: [
+ { agent_id: "agent-1", agent_name: "Agent One" },
+ { agent_id: "agent-2", agent_name: "Agent Two", agent_access_groups: ["group-a", "group-b"] },
+ ],
+ });
+ });
+
+ it("renders the selector", () => {
+ render();
+ expect(screen.getByTestId("agent-select")).toBeInTheDocument();
+ });
+
+ it("fetches agents on mount with access token", async () => {
+ render();
+ await waitFor(() => {
+ expect(mockGetAgentsList).toHaveBeenCalledWith("test-token");
+ });
+ });
+
+ it("does not fetch when accessToken is empty", () => {
+ render();
+ expect(mockGetAgentsList).not.toHaveBeenCalled();
+ });
+
+ it("shows loading state while fetching", async () => {
+ // Keep the promise pending
+ let resolve: any;
+ mockGetAgentsList.mockReturnValue(new Promise((r) => { resolve = r; }));
+
+ render();
+ expect(screen.getByTestId("agent-select")).toHaveAttribute("data-loading", "true");
+
+ // Resolve to clean up
+ resolve({ agents: [] });
+ await waitFor(() => {
+ expect(screen.getByTestId("agent-select")).toHaveAttribute("data-loading", "false");
+ });
+ });
+
+ it("renders agent options after fetch", async () => {
+ render();
+ await waitFor(() => {
+ expect(screen.getByText("Agent One")).toBeInTheDocument();
+ expect(screen.getByText("Agent Two")).toBeInTheDocument();
+ });
+ });
+
+ it("renders access group options with group prefix", async () => {
+ render();
+ await waitFor(() => {
+ expect(screen.getByText("group-a")).toBeInTheDocument();
+ expect(screen.getByText("group-b")).toBeInTheDocument();
+ });
+ });
+
+ it("respects disabled prop", () => {
+ render();
+ expect(screen.getByTestId("agent-select")).toHaveAttribute("data-disabled", "true");
+ });
+
+ it("handles API error gracefully", async () => {
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
+ mockGetAgentsList.mockRejectedValue(new Error("API error"));
+
+ render();
+ await waitFor(() => {
+ expect(consoleSpy).toHaveBeenCalledWith("Error fetching agents:", expect.any(Error));
+ });
+
+ consoleSpy.mockRestore();
+ });
+
+ it("passes value as flattened selectedValues", async () => {
+ render(
+
+ );
+ await waitFor(() => {
+ const select = screen.getByTestId("select-input");
+ // The value should contain agent-1 and group:group-a
+ expect(select).toBeInTheDocument();
+ });
+ });
+
+ it("handles null response from API", async () => {
+ mockGetAgentsList.mockResolvedValue(null);
+ render();
+ await waitFor(() => {
+ expect(screen.getByTestId("agent-select")).toHaveAttribute("data-loading", "false");
+ });
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/claude_code_plugins/helpers.test.ts b/ui/litellm-dashboard/src/components/claude_code_plugins/helpers.test.ts
new file mode 100644
index 0000000000..6a754f5739
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/claude_code_plugins/helpers.test.ts
@@ -0,0 +1,329 @@
+import { describe, expect, it } from "vitest";
+import {
+ formatInstallCommand,
+ extractCategories,
+ validatePluginName,
+ getSourceDisplayText,
+ getSourceLink,
+ getCategoryBadgeColor,
+ formatDateString,
+ truncateText,
+ filterPluginsBySearch,
+ filterPluginsByCategory,
+ isValidSemanticVersion,
+ isValidEmail,
+ isValidUrl,
+ parseKeywords,
+ formatKeywords,
+} from "./helpers";
+import { MarketplacePluginEntry, PluginSource } from "./types";
+
+describe("formatInstallCommand", () => {
+ it("formats github source with repo", () => {
+ const plugin = { name: "my-plugin", source: { source: "github" as const, repo: "org/repo" } };
+ expect(formatInstallCommand(plugin)).toBe("/plugin marketplace add org/repo");
+ });
+
+ it("formats url source", () => {
+ const plugin = { name: "my-plugin", source: { source: "url" as const, url: "https://example.com/plugin" } };
+ expect(formatInstallCommand(plugin)).toBe("/plugin marketplace add https://example.com/plugin");
+ });
+
+ it("falls back to plugin name when no repo or url", () => {
+ const plugin = { name: "my-plugin", source: { source: "github" as const } };
+ expect(formatInstallCommand(plugin)).toBe("/plugin marketplace add my-plugin");
+ });
+});
+
+describe("extractCategories", () => {
+ it("returns All and Other for empty list", () => {
+ expect(extractCategories([])).toEqual(["All", "Other"]);
+ });
+
+ it("extracts and sorts unique categories", () => {
+ const plugins = [
+ { category: "Development" },
+ { category: "Analytics" },
+ { category: "Development" },
+ ];
+ expect(extractCategories(plugins)).toEqual(["All", "Analytics", "Development", "Other"]);
+ });
+
+ it("ignores empty/whitespace categories", () => {
+ const plugins = [{ category: "" }, { category: " " }, { category: "Tools" }];
+ expect(extractCategories(plugins)).toEqual(["All", "Tools", "Other"]);
+ });
+
+ it("handles undefined category", () => {
+ const plugins = [{ category: undefined }, { category: "Security" }];
+ expect(extractCategories(plugins)).toEqual(["All", "Security", "Other"]);
+ });
+});
+
+describe("validatePluginName", () => {
+ it("accepts valid kebab-case names", () => {
+ expect(validatePluginName("my-plugin")).toBe(true);
+ expect(validatePluginName("plugin123")).toBe(true);
+ expect(validatePluginName("a-b-c")).toBe(true);
+ });
+
+ it("rejects names with uppercase", () => {
+ expect(validatePluginName("MyPlugin")).toBe(false);
+ });
+
+ it("rejects names with spaces", () => {
+ expect(validatePluginName("my plugin")).toBe(false);
+ });
+
+ it("rejects empty/whitespace names", () => {
+ expect(validatePluginName("")).toBe(false);
+ expect(validatePluginName(" ")).toBe(false);
+ });
+
+ it("rejects names with special characters", () => {
+ expect(validatePluginName("my_plugin")).toBe(false);
+ expect(validatePluginName("my.plugin")).toBe(false);
+ });
+});
+
+describe("getSourceDisplayText", () => {
+ it("shows github repo", () => {
+ expect(getSourceDisplayText({ source: "github", repo: "org/repo" })).toBe("GitHub: org/repo");
+ });
+
+ it("shows url", () => {
+ expect(getSourceDisplayText({ source: "url", url: "https://example.com" })).toBe("https://example.com");
+ });
+
+ it("returns unknown for missing data", () => {
+ expect(getSourceDisplayText({ source: "github" })).toBe("Unknown source");
+ });
+});
+
+describe("getSourceLink", () => {
+ it("returns github link for github source", () => {
+ expect(getSourceLink({ source: "github", repo: "org/repo" })).toBe("https://github.com/org/repo");
+ });
+
+ it("returns url for url source", () => {
+ expect(getSourceLink({ source: "url", url: "https://example.com" })).toBe("https://example.com");
+ });
+
+ it("returns null when no repo or url", () => {
+ expect(getSourceLink({ source: "github" })).toBeNull();
+ });
+});
+
+describe("getCategoryBadgeColor", () => {
+ it("returns blue for development categories", () => {
+ expect(getCategoryBadgeColor("Development")).toBe("blue");
+ expect(getCategoryBadgeColor("dev-tools")).toBe("blue");
+ });
+
+ it("returns green for productivity categories", () => {
+ expect(getCategoryBadgeColor("Productivity")).toBe("green");
+ expect(getCategoryBadgeColor("Workflow")).toBe("green");
+ });
+
+ it("returns purple for learning categories", () => {
+ expect(getCategoryBadgeColor("Learning")).toBe("purple");
+ expect(getCategoryBadgeColor("Education")).toBe("purple");
+ });
+
+ it("returns red for security categories", () => {
+ expect(getCategoryBadgeColor("Security")).toBe("red");
+ expect(getCategoryBadgeColor("Safety")).toBe("red");
+ });
+
+ it("returns orange for data categories", () => {
+ expect(getCategoryBadgeColor("Data")).toBe("orange");
+ expect(getCategoryBadgeColor("Analytics")).toBe("orange");
+ });
+
+ it("returns yellow for integration categories", () => {
+ expect(getCategoryBadgeColor("Integration")).toBe("yellow");
+ expect(getCategoryBadgeColor("API")).toBe("yellow");
+ });
+
+ it("returns gray for unknown or undefined categories", () => {
+ expect(getCategoryBadgeColor("Unknown")).toBe("gray");
+ expect(getCategoryBadgeColor(undefined)).toBe("gray");
+ });
+});
+
+describe("formatDateString", () => {
+ it("formats valid date strings", () => {
+ const result = formatDateString("2024-01-15T12:00:00Z");
+ expect(result).toContain("2024");
+ expect(result).toContain("Jan");
+ expect(result).toContain("15");
+ });
+
+ it("returns N/A for undefined", () => {
+ expect(formatDateString(undefined)).toBe("N/A");
+ });
+
+ it("returns N/A for empty string", () => {
+ expect(formatDateString("")).toBe("N/A");
+ });
+});
+
+describe("truncateText", () => {
+ it("returns text unchanged if shorter than max", () => {
+ expect(truncateText("hello", 10)).toBe("hello");
+ });
+
+ it("truncates and adds ellipsis", () => {
+ expect(truncateText("hello world", 5)).toBe("hello...");
+ });
+
+ it("handles exact length", () => {
+ expect(truncateText("hello", 5)).toBe("hello");
+ });
+
+ it("handles empty text", () => {
+ expect(truncateText("", 5)).toBe("");
+ });
+});
+
+describe("filterPluginsBySearch", () => {
+ const plugins: MarketplacePluginEntry[] = [
+ {
+ name: "code-formatter",
+ source: { source: "github", repo: "org/formatter" },
+ description: "Formats code nicely",
+ keywords: ["format", "lint"],
+ },
+ {
+ name: "data-viewer",
+ source: { source: "github", repo: "org/viewer" },
+ description: "View data",
+ keywords: ["analytics"],
+ },
+ ];
+
+ it("returns all plugins for empty search", () => {
+ expect(filterPluginsBySearch(plugins, "")).toEqual(plugins);
+ expect(filterPluginsBySearch(plugins, " ")).toEqual(plugins);
+ });
+
+ it("matches by name", () => {
+ expect(filterPluginsBySearch(plugins, "formatter")).toHaveLength(1);
+ expect(filterPluginsBySearch(plugins, "formatter")[0].name).toBe("code-formatter");
+ });
+
+ it("matches by description", () => {
+ expect(filterPluginsBySearch(plugins, "nicely")).toHaveLength(1);
+ });
+
+ it("matches by keyword", () => {
+ expect(filterPluginsBySearch(plugins, "analytics")).toHaveLength(1);
+ expect(filterPluginsBySearch(plugins, "analytics")[0].name).toBe("data-viewer");
+ });
+
+ it("is case insensitive", () => {
+ expect(filterPluginsBySearch(plugins, "FORMATTER")).toHaveLength(1);
+ });
+});
+
+describe("filterPluginsByCategory", () => {
+ const plugins: MarketplacePluginEntry[] = [
+ { name: "a", source: { source: "github" }, category: "Dev" },
+ { name: "b", source: { source: "github" }, category: "Security" },
+ { name: "c", source: { source: "github" }, category: "" },
+ { name: "d", source: { source: "github" } },
+ ];
+
+ it("returns all plugins for 'All'", () => {
+ expect(filterPluginsByCategory(plugins, "All")).toEqual(plugins);
+ });
+
+ it("returns uncategorized plugins for 'Other'", () => {
+ const result = filterPluginsByCategory(plugins, "Other");
+ expect(result).toHaveLength(2);
+ expect(result.map((p) => p.name)).toEqual(["c", "d"]);
+ });
+
+ it("filters by specific category", () => {
+ const result = filterPluginsByCategory(plugins, "Dev");
+ expect(result).toHaveLength(1);
+ expect(result[0].name).toBe("a");
+ });
+});
+
+describe("isValidSemanticVersion", () => {
+ it("accepts valid semver", () => {
+ expect(isValidSemanticVersion("1.0.0")).toBe(true);
+ expect(isValidSemanticVersion("0.1.0-alpha")).toBe(true);
+ expect(isValidSemanticVersion("2.3.4+build.1")).toBe(true);
+ });
+
+ it("rejects invalid semver", () => {
+ expect(isValidSemanticVersion("1.0")).toBe(false);
+ expect(isValidSemanticVersion("abc")).toBe(false);
+ });
+
+ it("returns true for undefined (optional)", () => {
+ expect(isValidSemanticVersion(undefined)).toBe(true);
+ });
+});
+
+describe("isValidEmail", () => {
+ it("accepts valid emails", () => {
+ expect(isValidEmail("user@example.com")).toBe(true);
+ });
+
+ it("rejects invalid emails", () => {
+ expect(isValidEmail("not-an-email")).toBe(false);
+ expect(isValidEmail("@example.com")).toBe(false);
+ });
+
+ it("returns true for undefined (optional)", () => {
+ expect(isValidEmail(undefined)).toBe(true);
+ });
+});
+
+describe("isValidUrl", () => {
+ it("accepts valid urls", () => {
+ expect(isValidUrl("https://example.com")).toBe(true);
+ expect(isValidUrl("http://localhost:3000")).toBe(true);
+ });
+
+ it("rejects invalid urls", () => {
+ expect(isValidUrl("not a url")).toBe(false);
+ });
+
+ it("returns true for undefined (optional)", () => {
+ expect(isValidUrl(undefined)).toBe(true);
+ });
+});
+
+describe("parseKeywords", () => {
+ it("splits comma-separated keywords", () => {
+ expect(parseKeywords("a, b, c")).toEqual(["a", "b", "c"]);
+ });
+
+ it("trims whitespace", () => {
+ expect(parseKeywords(" foo , bar ")).toEqual(["foo", "bar"]);
+ });
+
+ it("filters empty entries", () => {
+ expect(parseKeywords("a,,b,")).toEqual(["a", "b"]);
+ });
+
+ it("returns empty array for empty string", () => {
+ expect(parseKeywords("")).toEqual([]);
+ expect(parseKeywords(" ")).toEqual([]);
+ });
+});
+
+describe("formatKeywords", () => {
+ it("joins keywords with comma and space", () => {
+ expect(formatKeywords(["a", "b", "c"])).toBe("a, b, c");
+ });
+
+ it("returns empty string for empty/undefined array", () => {
+ expect(formatKeywords([])).toBe("");
+ expect(formatKeywords(undefined)).toBe("");
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/molecules/message_manager.test.ts b/ui/litellm-dashboard/src/components/molecules/message_manager.test.ts
new file mode 100644
index 0000000000..541efcbc4c
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/molecules/message_manager.test.ts
@@ -0,0 +1,107 @@
+import { describe, expect, it, vi, beforeEach } from "vitest";
+
+// Use vi.hoisted so the mock object is available when vi.mock is hoisted
+const mockStaticMessage = vi.hoisted(() => ({
+ success: vi.fn(),
+ error: vi.fn(),
+ warning: vi.fn(),
+ info: vi.fn(),
+ loading: vi.fn(),
+ destroy: vi.fn(),
+}));
+
+vi.mock("antd", () => ({
+ message: mockStaticMessage,
+}));
+
+import MessageManager, { setMessageInstance } from "./message_manager";
+
+describe("MessageManager", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ describe("when no instance is set (falls back to static message)", () => {
+ it("delegates success to static message", () => {
+ MessageManager.success("done!");
+ expect(mockStaticMessage.success).toHaveBeenCalledWith("done!", undefined);
+ });
+
+ it("delegates error to static message", () => {
+ MessageManager.error("failed!", 5);
+ expect(mockStaticMessage.error).toHaveBeenCalledWith("failed!", 5);
+ });
+
+ it("delegates warning to static message", () => {
+ MessageManager.warning("watch out");
+ expect(mockStaticMessage.warning).toHaveBeenCalledWith("watch out", undefined);
+ });
+
+ it("delegates info to static message", () => {
+ MessageManager.info("fyi");
+ expect(mockStaticMessage.info).toHaveBeenCalledWith("fyi", undefined);
+ });
+
+ it("delegates loading to static message", () => {
+ MessageManager.loading("loading...", 3);
+ expect(mockStaticMessage.loading).toHaveBeenCalledWith("loading...", 3);
+ });
+
+ it("delegates destroy to static message", () => {
+ MessageManager.destroy();
+ expect(mockStaticMessage.destroy).toHaveBeenCalled();
+ });
+ });
+
+ describe("when a custom instance is set", () => {
+ const mockInstance = {
+ success: vi.fn(),
+ error: vi.fn(),
+ warning: vi.fn(),
+ info: vi.fn(),
+ loading: vi.fn(),
+ destroy: vi.fn(),
+ open: vi.fn(),
+ };
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ setMessageInstance(mockInstance as any);
+ });
+
+ it("delegates success to custom instance", () => {
+ MessageManager.success("done!");
+ expect(mockInstance.success).toHaveBeenCalledWith("done!", undefined);
+ expect(mockStaticMessage.success).not.toHaveBeenCalled();
+ });
+
+ it("delegates error with duration to custom instance", () => {
+ MessageManager.error("failed!", 5);
+ expect(mockInstance.error).toHaveBeenCalledWith("failed!", 5);
+ expect(mockStaticMessage.error).not.toHaveBeenCalled();
+ });
+
+ it("delegates warning to custom instance", () => {
+ MessageManager.warning("watch out");
+ expect(mockInstance.warning).toHaveBeenCalledWith("watch out", undefined);
+ });
+
+ it("delegates info to custom instance", () => {
+ MessageManager.info("fyi", 2);
+ expect(mockInstance.info).toHaveBeenCalledWith("fyi", 2);
+ });
+
+ it("delegates loading to custom instance and returns result", () => {
+ const mockReturn = { then: vi.fn() };
+ mockInstance.loading.mockReturnValue(mockReturn);
+ const result = MessageManager.loading("loading...", 3);
+ expect(mockInstance.loading).toHaveBeenCalledWith("loading...", 3);
+ expect(result).toBe(mockReturn);
+ });
+
+ it("delegates destroy to custom instance", () => {
+ MessageManager.destroy();
+ expect(mockInstance.destroy).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/ui/AntDLoadingSpinner.test.tsx b/ui/litellm-dashboard/src/components/ui/AntDLoadingSpinner.test.tsx
new file mode 100644
index 0000000000..e86a765985
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/ui/AntDLoadingSpinner.test.tsx
@@ -0,0 +1,61 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+
+// Mock antd Spin component
+vi.mock("antd", () => ({
+ Spin: ({ indicator, size, ...props }: any) => (
+
+ {indicator}
+
+ ),
+}));
+
+// Mock the icon
+vi.mock("@ant-design/icons", () => ({
+ LoadingOutlined: ({ style, spin, ...props }: any) => (
+
+ ),
+}));
+
+import { AntDLoadingSpinner } from "./AntDLoadingSpinner";
+
+describe("AntDLoadingSpinner", () => {
+ it("renders without props", () => {
+ render();
+ expect(screen.getByTestId("spin")).toBeInTheDocument();
+ expect(screen.getByTestId("loading-icon")).toBeInTheDocument();
+ });
+
+ it("passes size prop to Spin", () => {
+ render();
+ expect(screen.getByTestId("spin")).toHaveAttribute("data-size", "large");
+ });
+
+ it("passes small size to Spin", () => {
+ render();
+ expect(screen.getByTestId("spin")).toHaveAttribute("data-size", "small");
+ });
+
+ it("applies custom fontSize to the icon", () => {
+ render();
+ const icon = screen.getByTestId("loading-icon");
+ expect(icon).toHaveStyle({ fontSize: "32px" });
+ });
+
+ it("does not set style when fontSize is not provided", () => {
+ render();
+ const icon = screen.getByTestId("loading-icon");
+ expect(icon.style.fontSize).toBe("");
+ });
+
+ it("sets spin attribute on icon", () => {
+ render();
+ const icon = screen.getByTestId("loading-icon");
+ expect(icon).toHaveAttribute("data-spin", "true");
+ });
+});