import { render, screen, fireEvent } from "@testing-library/react"; import { describe, it, expect, vi, beforeEach } from "vitest"; import type { Provider } from "@/types"; import { ProviderList } from "@/components/providers/ProviderList"; const useDragSortMock = vi.fn(); const useSortableMock = vi.fn(); const providerCardRenderSpy = vi.fn(); vi.mock("@/hooks/useDragSort", () => ({ useDragSort: (...args: unknown[]) => useDragSortMock(...args), })); vi.mock("@/components/providers/ProviderCard", () => ({ ProviderCard: (props: any) => { providerCardRenderSpy(props); const { provider, onSwitch, onEdit, onDelete, onDuplicate, onConfigureUsage, } = props; return (
{props.isCurrent ? "current" : "inactive"} {props.isEditMode ? "edit-mode" : "view-mode"} {props.dragHandleProps?.attributes?.["data-dnd-id"] ?? "none"}
); }, })); vi.mock("@/components/UsageFooter", () => ({ default: () =>
, })); vi.mock("@dnd-kit/sortable", async () => { const actual = await vi.importActual("@dnd-kit/sortable"); return { ...actual, useSortable: (...args: unknown[]) => useSortableMock(...args), }; }); function createProvider(overrides: Partial = {}): Provider { return { id: overrides.id ?? "provider-1", name: overrides.name ?? "Test Provider", settingsConfig: overrides.settingsConfig ?? {}, category: overrides.category, createdAt: overrides.createdAt, sortIndex: overrides.sortIndex, meta: overrides.meta, websiteUrl: overrides.websiteUrl, }; } beforeEach(() => { useDragSortMock.mockReset(); useSortableMock.mockReset(); providerCardRenderSpy.mockClear(); useSortableMock.mockImplementation(({ id }: { id: string }) => ({ setNodeRef: vi.fn(), attributes: { "data-dnd-id": id }, listeners: { onPointerDown: vi.fn() }, transform: null, transition: null, isDragging: false, })); useDragSortMock.mockReturnValue({ sortedProviders: [], sensors: [], handleDragEnd: vi.fn(), }); }); describe("ProviderList Component", () => { it("should render skeleton placeholders when loading", () => { const { container } = render( , ); const placeholders = container.querySelectorAll( ".border-dashed.border-muted-foreground\\/40", ); expect(placeholders).toHaveLength(3); }); it("should show empty state and trigger create callback when no providers exist", () => { const handleCreate = vi.fn(); useDragSortMock.mockReturnValueOnce({ sortedProviders: [], sensors: [], handleDragEnd: vi.fn(), }); render( , ); const addButton = screen.getByRole("button", { name: "provider.addProvider", }); fireEvent.click(addButton); expect(handleCreate).toHaveBeenCalledTimes(1); }); it("should render in order returned by useDragSort and pass through action callbacks", () => { const providerA = createProvider({ id: "a", name: "A" }); const providerB = createProvider({ id: "b", name: "B" }); const handleSwitch = vi.fn(); const handleEdit = vi.fn(); const handleDelete = vi.fn(); const handleDuplicate = vi.fn(); const handleUsage = vi.fn(); const handleOpenWebsite = vi.fn(); useDragSortMock.mockReturnValue({ sortedProviders: [providerB, providerA], sensors: [], handleDragEnd: vi.fn(), }); render( , ); // Verify sort order expect(providerCardRenderSpy).toHaveBeenCalledTimes(2); expect(providerCardRenderSpy.mock.calls[0][0].provider.id).toBe("b"); expect(providerCardRenderSpy.mock.calls[1][0].provider.id).toBe("a"); // Verify current provider marker and edit mode pass-through expect( providerCardRenderSpy.mock.calls[0][0].isCurrent, ).toBe(true); expect(providerCardRenderSpy.mock.calls[0][0].isEditMode).toBe(true); // Drag attributes from useSortable expect( providerCardRenderSpy.mock.calls[0][0].dragHandleProps?.attributes[ "data-dnd-id" ], ).toBe("b"); expect( providerCardRenderSpy.mock.calls[1][0].dragHandleProps?.attributes[ "data-dnd-id" ], ).toBe("a"); // Trigger action buttons fireEvent.click(screen.getByTestId("switch-b")); fireEvent.click(screen.getByTestId("edit-b")); fireEvent.click(screen.getByTestId("duplicate-b")); fireEvent.click(screen.getByTestId("usage-b")); fireEvent.click(screen.getByTestId("delete-a")); expect(handleSwitch).toHaveBeenCalledWith(providerB); expect(handleEdit).toHaveBeenCalledWith(providerB); expect(handleDuplicate).toHaveBeenCalledWith(providerB); expect(handleUsage).toHaveBeenCalledWith(providerB); expect(handleDelete).toHaveBeenCalledWith(providerA); // Verify useDragSort call parameters expect(useDragSortMock).toHaveBeenCalledWith( { a: providerA, b: providerB }, "claude", ); }); });