/** * @vitest-environment happy-dom */ import { act } from "react"; import { createRoot } from "react-dom/client"; import { describe, expect, it, vi } from "vitest"; import type { ProviderDisplay, ProviderType } from "@/types/provider"; // Mock next-intl vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, })); // Mock UI components vi.mock("@/components/ui/button", () => ({ Button: ({ children, ...props }: any) => , })); vi.mock("@/components/ui/checkbox", () => ({ Checkbox: ({ checked, onCheckedChange, ...props }: any) => ( onCheckedChange?.(e.target.checked)} {...props} /> ), })); vi.mock("@/components/ui/dropdown-menu", () => ({ DropdownMenu: ({ children }: any) =>
{children}
, DropdownMenuTrigger: ({ children }: any) => (
{children}
), DropdownMenuContent: ({ children }: any) => (
{children}
), DropdownMenuItem: ({ children, onClick, ...props }: any) => (
{children}
), })); // Mock lucide-react vi.mock("lucide-react", () => ({ ChevronDown: () => , Pencil: () => , X: () => , })); function createProvider( id: number, providerType: ProviderType, groupTag: string | null = null ): ProviderDisplay { return { id, providerType, groupTag } as ProviderDisplay; } function render(node: React.ReactNode) { const container = document.createElement("div"); document.body.appendChild(container); const root = createRoot(container); act(() => { root.render(node); }); return { container, unmount: () => { act(() => root.unmount()); container.remove(); }, }; } // Import after mocks import { ProviderBatchToolbar } from "@/app/[locale]/settings/providers/_components/batch-edit/provider-batch-toolbar"; const defaultProps = { isMultiSelectMode: false, allSelected: false, selectedCount: 0, totalCount: 5, onEnterMode: vi.fn(), onExitMode: vi.fn(), onSelectAll: vi.fn(), onInvertSelection: vi.fn(), onOpenBatchEdit: vi.fn(), providers: [] as ProviderDisplay[], onSelectByType: vi.fn(), onSelectByGroup: vi.fn(), }; describe("ProviderBatchToolbar - Selection enhancements", () => { it("does NOT render type/group dropdowns when NOT in multi-select mode", () => { const providers = [createProvider(1, "claude"), createProvider(2, "openai-compatible")]; const { container, unmount } = render( ); const dropdowns = container.querySelectorAll('[data-testid="dropdown-menu"]'); expect(dropdowns.length).toBe(0); unmount(); }); it("renders Select by Type dropdown in multi-select mode when providers have multiple types", () => { const providers = [ createProvider(1, "claude"), createProvider(2, "claude"), createProvider(3, "openai-compatible"), ]; const { container, unmount } = render( ); const buttons = container.querySelectorAll("button"); const typeButton = Array.from(buttons).find((b) => b.textContent?.includes("selectByType")); expect(typeButton).toBeTruthy(); const items = container.querySelectorAll('[data-testid="dropdown-menu-item"]'); const typeItems = Array.from(items).filter( (item) => item.getAttribute("data-value") === "claude" || item.getAttribute("data-value") === "openai-compatible" ); expect(typeItems.length).toBe(2); unmount(); }); it("calls onSelectByType with correct type when clicking a type option", () => { const onSelectByType = vi.fn(); const providers = [createProvider(1, "claude"), createProvider(2, "openai-compatible")]; const { container, unmount } = render( ); const claudeItem = container.querySelector('[data-value="claude"]'); expect(claudeItem).toBeTruthy(); act(() => { claudeItem!.dispatchEvent(new MouseEvent("click", { bubbles: true })); }); expect(onSelectByType).toHaveBeenCalledWith("claude"); unmount(); }); it("renders Select by Group dropdown when providers have groups", () => { const providers = [ createProvider(1, "claude", "production"), createProvider(2, "claude", "staging"), createProvider(3, "claude", "production"), ]; const { container, unmount } = render( ); const buttons = container.querySelectorAll("button"); const groupButton = Array.from(buttons).find((b) => b.textContent?.includes("selectByGroup")); expect(groupButton).toBeTruthy(); const items = container.querySelectorAll('[data-testid="dropdown-menu-item"]'); const groupItems = Array.from(items).filter( (item) => item.getAttribute("data-value") === "production" || item.getAttribute("data-value") === "staging" ); expect(groupItems.length).toBe(2); unmount(); }); it("calls onSelectByGroup with correct group when clicking a group option", () => { const onSelectByGroup = vi.fn(); const providers = [ createProvider(1, "claude", "production"), createProvider(2, "claude", "staging"), ]; const { container, unmount } = render( ); const productionItem = container.querySelector('[data-value="production"]'); expect(productionItem).toBeTruthy(); act(() => { productionItem!.dispatchEvent(new MouseEvent("click", { bubbles: true })); }); expect(onSelectByGroup).toHaveBeenCalledWith("production"); unmount(); }); it("does NOT render type dropdown when all filtered providers have same type", () => { const providers = [createProvider(1, "claude"), createProvider(2, "claude")]; const { container, unmount } = render( ); const buttons = container.querySelectorAll("button"); const typeButton = Array.from(buttons).find((b) => b.textContent?.includes("selectByType")); expect(typeButton).toBeFalsy(); unmount(); }); it("does NOT render group dropdown when no groups exist", () => { const providers = [ createProvider(1, "claude", null), createProvider(2, "openai-compatible", null), ]; const { container, unmount } = render( ); const buttons = container.querySelectorAll("button"); const groupButton = Array.from(buttons).find((b) => b.textContent?.includes("selectByGroup")); expect(groupButton).toBeFalsy(); unmount(); }); });