import { describe, it, expect, vi, afterEach } from 'vitest'; import { render, screen, cleanup } from '@testing-library/svelte'; import { tick } from 'svelte'; import AddToCollectionModal from './AddToCollectionModal.svelte'; // Stub the API modules at the import-graph level so the modal exercises // its dispatch (manga vs page) without hitting fetch. const collectionsApi = vi.hoisted(() => ({ listMyCollections: vi.fn(), getMyCollectionsContaining: vi.fn(), addMangaToCollection: vi.fn(), removeMangaFromCollection: vi.fn(), createCollection: vi.fn() })); const pageCollectionsApi = vi.hoisted(() => ({ getMyCollectionsContainingPage: vi.fn(), addPageToCollection: vi.fn(), removePageFromCollection: vi.fn() })); vi.mock('$lib/api/collections', () => collectionsApi); vi.mock('$lib/api/page_collections', () => pageCollectionsApi); afterEach(() => { cleanup(); vi.clearAllMocks(); }); function summary(id: string, name: string, containing: string[] = []) { return { id, user_id: 'u1', name, description: null, created_at: '2026-01-01T00:00:00Z', updated_at: '2026-01-01T00:00:00Z', manga_count: 0, sample_covers: [], _containing: containing }; } describe('AddToCollectionModal', () => { it('manga target loads via getMyCollectionsContaining', async () => { collectionsApi.listMyCollections.mockResolvedValueOnce({ items: [summary('c1', 'Favorites')], page: { limit: 200, offset: 0, total: 1 } }); collectionsApi.getMyCollectionsContaining.mockResolvedValueOnce(['c1']); render(AddToCollectionModal, { props: { open: true, target: { kind: 'manga', id: 'm1' }, onClose: () => {} } }); // Wait for the $effect → load() → await chain to settle. await tick(); await Promise.resolve(); await tick(); expect(collectionsApi.getMyCollectionsContaining).toHaveBeenCalledWith('m1'); expect(pageCollectionsApi.getMyCollectionsContainingPage).not.toHaveBeenCalled(); }); it('page target loads via getMyCollectionsContainingPage', async () => { collectionsApi.listMyCollections.mockResolvedValueOnce({ items: [summary('c1', 'Favorite panels')], page: { limit: 200, offset: 0, total: 1 } }); pageCollectionsApi.getMyCollectionsContainingPage.mockResolvedValueOnce([]); render(AddToCollectionModal, { props: { open: true, target: { kind: 'page', id: 'p1' }, onClose: () => {} } }); await tick(); await Promise.resolve(); await tick(); expect(pageCollectionsApi.getMyCollectionsContainingPage).toHaveBeenCalledWith('p1'); expect(collectionsApi.getMyCollectionsContaining).not.toHaveBeenCalled(); expect(screen.getByText('Favorite panels')).toBeTruthy(); }); it('does not load when closed', () => { render(AddToCollectionModal, { props: { open: false, target: { kind: 'manga', id: 'm1' }, onClose: () => {} } }); expect(collectionsApi.listMyCollections).not.toHaveBeenCalled(); }); });