diff --git a/frontend/src/routes/documents/[id]/page.svelte.test.ts b/frontend/src/routes/documents/[id]/page.svelte.test.ts index 66b30b81..1f823757 100644 --- a/frontend/src/routes/documents/[id]/page.svelte.test.ts +++ b/frontend/src/routes/documents/[id]/page.svelte.test.ts @@ -446,4 +446,73 @@ describe('documents/[id] page', () => { }) ).not.toThrow(); }); + + it('handles ocr-status DONE state without restarting polling', async () => { + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(async (url) => { + const u = url.toString(); + if (u.includes('ocr-status')) { + return new Response(JSON.stringify({ status: 'DONE', jobId: null }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + } + return new Response('[]', { status: 200, headers: { 'Content-Type': 'application/json' } }); + }); + try { + mockPage.url = new URL('http://localhost/documents/d-ocr-done?task=transcribe'); + render(DocumentDetailPage, { + props: { data: baseData({ document: { ...baseDoc, id: 'd-ocr-done' } }) } + }); + await new Promise((r) => setTimeout(r, 100)); + // No throw — the ocrRunning state stays false because status was DONE + expect(document.body.firstElementChild).not.toBeNull(); + } finally { + fetchSpy.mockRestore(); + } + }); + + it('handles ocr-status without jobId (no polling started)', async () => { + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(async (url) => { + const u = url.toString(); + if (u.includes('ocr-status')) { + return new Response(JSON.stringify({ status: 'PENDING', jobId: null }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + } + return new Response('[]', { status: 200, headers: { 'Content-Type': 'application/json' } }); + }); + try { + mockPage.url = new URL('http://localhost/documents/d-ocr-no-job?task=transcribe'); + expect(() => + render(DocumentDetailPage, { + props: { data: baseData({ document: { ...baseDoc, id: 'd-ocr-no-job' } }) } + }) + ).not.toThrow(); + await new Promise((r) => setTimeout(r, 100)); + } finally { + fetchSpy.mockRestore(); + } + }); + + it('handles ocr-status fetch network error gracefully', async () => { + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(async (url) => { + const u = url.toString(); + if (u.includes('ocr-status')) { + throw new Error('network down'); + } + return new Response('[]', { status: 200, headers: { 'Content-Type': 'application/json' } }); + }); + try { + mockPage.url = new URL('http://localhost/documents/d-ocr-throw?task=transcribe'); + expect(() => + render(DocumentDetailPage, { + props: { data: baseData({ document: { ...baseDoc, id: 'd-ocr-throw' } }) } + }) + ).not.toThrow(); + await new Promise((r) => setTimeout(r, 100)); + } finally { + fetchSpy.mockRestore(); + } + }); });