diff --git a/frontend/src/routes/admin/system/page.svelte.test.ts b/frontend/src/routes/admin/system/page.svelte.test.ts index 641470f5..9a028cee 100644 --- a/frontend/src/routes/admin/system/page.svelte.test.ts +++ b/frontend/src/routes/admin/system/page.svelte.test.ts @@ -238,4 +238,114 @@ describe('admin/system page', () => { const banner = document.querySelector('[data-testid="thumbnails-status-done"]'); expect(banner).not.toBeNull(); }); + + it('renders the FAILED state for import-status with retry button', async () => { + fetchSpy.mockImplementation(async (url: RequestInfo | URL) => { + const u = url.toString(); + if (u.includes('import-status')) { + return new Response( + JSON.stringify({ + state: 'FAILED', + message: 'database error', + processed: 0, + startedAt: null + }), + { status: 200, headers: { 'Content-Type': 'application/json' } } + ); + } + return new Response(JSON.stringify({ state: 'IDLE' }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + }); + + render(AdminSystemPage, { props: {} }); + + await new Promise((r) => setTimeout(r, 100)); + expect(document.body.textContent).toContain('database error'); + }); + + it('renders the running thumbnail status with progress count', async () => { + fetchSpy.mockImplementation(async (url: RequestInfo | URL) => { + const u = url.toString(); + if (u.includes('thumbnail-status')) { + return new Response( + JSON.stringify({ + state: 'RUNNING', + message: '', + total: 100, + processed: 30, + skipped: 5, + failed: 1, + startedAt: null + }), + { status: 200, headers: { 'Content-Type': 'application/json' } } + ); + } + return new Response(JSON.stringify({ state: 'IDLE' }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + }); + + render(AdminSystemPage, { props: {} }); + + await new Promise((r) => setTimeout(r, 100)); + // Total 100, processed+skipped+failed = 36 + expect(document.body.textContent).toMatch(/36|100/); + }); + + it('triggers thumbnail backfill when its button is clicked', async () => { + render(AdminSystemPage, { props: {} }); + + await new Promise((r) => setTimeout(r, 50)); + const btns = Array.from(document.querySelectorAll('[data-thumbnails-trigger]')); + (btns[0] as HTMLButtonElement)?.click(); + + await new Promise((r) => setTimeout(r, 50)); + const calls = fetchSpy.mock.calls.map((c) => c[0].toString()); + expect(calls.some((c) => c.includes('generate-thumbnails'))).toBe(true); + }); + + it('triggers import when import button is clicked from idle state', async () => { + render(AdminSystemPage, { props: {} }); + + await new Promise((r) => setTimeout(r, 50)); + const btns = Array.from(document.querySelectorAll('[data-import-trigger]')); + (btns[0] as HTMLButtonElement)?.click(); + + await new Promise((r) => setTimeout(r, 50)); + const calls = fetchSpy.mock.calls.map((c) => c[0].toString()); + expect(calls.some((c) => c.includes('trigger-import'))).toBe(true); + }); + + it('renders the running thumbnail status without progress when total is 0', async () => { + fetchSpy.mockImplementation(async (url: RequestInfo | URL) => { + const u = url.toString(); + if (u.includes('thumbnail-status')) { + return new Response( + JSON.stringify({ + state: 'RUNNING', + message: '', + total: 0, + processed: 0, + skipped: 0, + failed: 0, + startedAt: null + }), + { status: 200, headers: { 'Content-Type': 'application/json' } } + ); + } + return new Response(JSON.stringify({ state: 'IDLE' }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + }); + + render(AdminSystemPage, { props: {} }); + + await new Promise((r) => setTimeout(r, 100)); + // running state shown but no progress count when total=0 + expect(document.body.textContent).toMatch(/läuft|wird|generier/i); + }); });