/** * Task F4 – LibraryProviderExtension Tests. * * Testet die drei Bibliotheksquellen: * - builtin-catalog (statische JSON-Startersets event/architecture/landscape) * - user-library (zeichnungsscoped Blöcke via /api/drawings/:id/blocks) * - global-library (globale Blöcke via /api/global-blocks) */ import { describe, it, expect, vi, afterEach } from 'vitest'; import { render, screen, waitFor, fireEvent } from '@testing-library/react'; import { builtinCatalogProvider } from '../src/plugins/builtin/library/providers'; import { makeUserLibraryProvider, makeGlobalLibraryProvider } from '../src/plugins/builtin/library/providers'; import { builtinLibraryPlugin } from '../src/plugins/builtin/library'; import { pluginRegistry } from '../src/plugins/PluginRegistry'; import LibraryProviderPanel from '../src/components/LibraryProviderPanel'; describe('F4: builtin catalog provider', () => { it('listet genau die 3 Katalog-Ordner', async () => { const folders = await builtinCatalogProvider.listFolders(); expect(folders.map(f => f.id).sort()).toEqual(['cat-arch', 'cat-event', 'cat-land']); expect(folders.every(f => f.parentId === null)).toBe(true); }); it('Startersets: je 15-30 Blöcke mit gültiger Payload und SVG-Thumbnail', async () => { const folders = await builtinCatalogProvider.listFolders(); for (const folder of folders) { const blocks = await builtinCatalogProvider.listBlocks(folder.id); expect(blocks.length).toBeGreaterThanOrEqual(15); expect(blocks.length).toBeLessThanOrEqual(30); expect(blocks.every(b => b.folderId === folder.id)).toBe(true); for (const block of blocks) { expect(block.thumbnail).toContain(' { const eventBlocks = await builtinCatalogProvider.listBlocks('cat-event'); expect(eventBlocks.length).toBeGreaterThanOrEqual(15); const first = eventBlocks[0]; const fetched = await builtinCatalogProvider.getBlock(first.id); expect(fetched.id).toBe(first.id); expect(fetched.payload).toBe(first.payload); }); }); describe('F4: Registry-Anbindung', () => { afterEach(() => { pluginRegistry.unregisterV2(builtinLibraryPlugin.manifest.id); }); it('Plugin registriert Provider, enableV2 macht ihn sichtbar, disableV2 entfernt ihn', () => { pluginRegistry.registerV2(builtinLibraryPlugin); pluginRegistry.enableV2(builtinLibraryPlugin.manifest.id); const providers = pluginRegistry.getLibraryProviders(); expect(providers.some(p => p.id === 'builtin-catalog')).toBe(true); pluginRegistry.disableV2(builtinLibraryPlugin.manifest.id); expect(pluginRegistry.getLibraryProviders().some(p => p.id === 'builtin-catalog')).toBe(false); }); }); describe('F4: user-library Provider', () => { it('mappt zeichnungsscoped Blöcke auf LibBlock mit Thumbnail+Payload', async () => { const provider = makeUserLibraryProvider([ { id: 'b1', drawing_id: 'd1', name: 'Bühne A', description: null, category: 'stage', elements_json: JSON.stringify([ { id: 'e1', type: 'rect', layerId: 'layer-0', x: 0, y: 0, width: 100, height: 50, properties: { fill: '#3a3a4a' } }, ]), thumbnail: null, }, ]); const folders = await provider.listFolders(); expect(folders.length).toBe(1); const blocks = await provider.listBlocks(folders[0].id); expect(blocks).toHaveLength(1); expect(blocks[0].name).toBe('Bühne A'); expect(blocks[0].thumbnail).toContain(' { afterEach(() => { vi.unstubAllGlobals(); }); it('lädt Ordner und Blöcke der globalen Bibliothek über die API', async () => { const mockFetch = vi.fn() .mockResolvedValueOnce({ ok: true, json: async () => [ { id: 'gf1', name: 'Event', parent_id: null, created_at: '2026-01-01' }, ] }) .mockResolvedValueOnce({ ok: true, json: async () => [ { id: 'gb1', folder_id: 'gf1', name: 'Stuhl-Reihe', block_data: '[]', svg_data: null, is_favorite: false, created_at: '2026-01-01', updated_at: '2026-01-01' }, ] }); vi.stubGlobal('fetch', mockFetch); const provider = makeGlobalLibraryProvider('tok'); const folders = await provider.listFolders(); expect(folders).toHaveLength(1); expect(folders[0].label).toBe('Event'); const blocks = await provider.listBlocks('gf1'); expect(blocks).toHaveLength(1); expect(blocks[0].name).toBe('Stuhl-Reihe'); }); }); describe('F4: LibraryProviderPanel UI', () => { it('rendert Provider-Sektion mit Katalog-Ordnern und Blöcken', async () => { render(); await waitFor(() => { expect(screen.getAllByText(/Event-Katalog/).length).toBeGreaterThan(0); }); // Jede Sektion listet Blöcke expect(screen.getAllByRole('button', { name: /Stuhl/ }).length).toBeGreaterThan(0); }); it('DragStart legt die Element-Array-Payload in den Datentransfer', async () => { const setData = vi.fn(); render(); const blockBtn = await screen.findByRole('button', { name: /Stuhl/ }); fireEvent.dragStart(blockBtn, { dataTransfer: { setData } }); expect(setData).toHaveBeenCalledWith('text/global-block-data', expect.any(String)); const payload = JSON.parse(setData.mock.calls[0][1] as string); expect(Array.isArray(payload)).toBe(true); expect(payload[0].id).toBeTypeOf('string'); }); });