// @vitest-environment jsdom import { act, renderHook } from "@testing-library/react"; import type { ReactNode } from "react"; import { beforeAll, describe, expect, it, vi } from "vitest"; import { createTestAdapters } from "../../__tests__/adapters/in-memory-adapters.js"; import { AllProviders } from "../../__tests__/test-providers.js"; import { useBulkImport } from "../use-bulk-import.js"; beforeAll(() => { Object.defineProperty(globalThis, "matchMedia", { writable: true, value: vi.fn().mockImplementation((query: string) => ({ matches: false, media: query, onchange: null, addListener: vi.fn(), removeListener: vi.fn(), addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn(), })), }); }); const adapters = createTestAdapters(); adapters.bestiaryIndex = { ...adapters.bestiaryIndex, getAllSourceCodes: () => ["MM", "VGM", "XGE"], getDefaultFetchUrl: (code: string, baseUrl?: string) => `${baseUrl}${code}.json`, }; function wrapper({ children }: { children: ReactNode }) { return {children}; } /** Flush microtasks so the internal async IIFE inside startImport settles. */ function flushMicrotasks(): Promise { return new Promise((resolve) => { setTimeout(resolve, 0); }); } describe("useBulkImport", () => { it("starts in idle state with all counters at 0", () => { const { result } = renderHook(() => useBulkImport(), { wrapper }); expect(result.current.state).toEqual({ status: "idle", total: 0, completed: 0, failed: 0, }); }); it("reset returns to idle state", async () => { const { result } = renderHook(() => useBulkImport(), { wrapper }); const isSourceCached = vi.fn().mockResolvedValue(true); const fetchAndCacheSource = vi.fn(); const refreshCache = vi.fn(); await act(async () => { result.current.startImport( "https://example.com/", fetchAndCacheSource, isSourceCached, refreshCache, ); await flushMicrotasks(); }); act(() => result.current.reset()); expect(result.current.state.status).toBe("idle"); }); it("goes straight to complete when all sources are cached", async () => { const { result } = renderHook(() => useBulkImport(), { wrapper }); const isSourceCached = vi.fn().mockResolvedValue(true); const fetchAndCacheSource = vi.fn(); const refreshCache = vi.fn(); await act(async () => { result.current.startImport( "https://example.com/", fetchAndCacheSource, isSourceCached, refreshCache, ); await flushMicrotasks(); }); expect(result.current.state.status).toBe("complete"); expect(result.current.state.completed).toBe(3); expect(fetchAndCacheSource).not.toHaveBeenCalled(); }); it("fetches uncached sources and completes", async () => { const { result } = renderHook(() => useBulkImport(), { wrapper }); const isSourceCached = vi.fn().mockResolvedValue(false); const fetchAndCacheSource = vi.fn().mockResolvedValue(undefined); const refreshCache = vi.fn().mockResolvedValue(undefined); await act(async () => { result.current.startImport( "https://example.com/", fetchAndCacheSource, isSourceCached, refreshCache, ); await flushMicrotasks(); }); expect(result.current.state.status).toBe("complete"); expect(result.current.state.completed).toBe(3); expect(result.current.state.failed).toBe(0); expect(fetchAndCacheSource).toHaveBeenCalledTimes(3); expect(refreshCache).toHaveBeenCalled(); }); it("reports partial-failure when some sources fail", async () => { const { result } = renderHook(() => useBulkImport(), { wrapper }); const isSourceCached = vi.fn().mockResolvedValue(false); const fetchAndCacheSource = vi .fn() .mockResolvedValueOnce(undefined) .mockRejectedValueOnce(new Error("fail")) .mockResolvedValueOnce(undefined); const refreshCache = vi.fn().mockResolvedValue(undefined); await act(async () => { result.current.startImport( "https://example.com/", fetchAndCacheSource, isSourceCached, refreshCache, ); await flushMicrotasks(); }); expect(result.current.state.status).toBe("partial-failure"); expect(result.current.state.completed).toBe(2); expect(result.current.state.failed).toBe(1); expect(refreshCache).toHaveBeenCalled(); }); it("calls refreshCache after all batches complete", async () => { const { result } = renderHook(() => useBulkImport(), { wrapper }); const isSourceCached = vi.fn().mockResolvedValue(false); const fetchAndCacheSource = vi.fn().mockResolvedValue(undefined); const refreshCache = vi.fn().mockResolvedValue(undefined); await act(async () => { result.current.startImport( "https://example.com/", fetchAndCacheSource, isSourceCached, refreshCache, ); await flushMicrotasks(); }); expect(refreshCache).toHaveBeenCalledOnce(); }); });