From 58085116583d2e6b0467ca1a64a2ff7fedc9160e Mon Sep 17 00:00:00 2001 From: 4gray Date: Thu, 30 Jul 2026 22:12:49 +0200 Subject: [PATCH] test(downloads): cover missing-file and series journeys --- ...ownload-library-navigation.service.spec.ts | 9 +++ .../src/lib/downloads.component.spec.ts | 60 ++++++++++++++++++- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/libs/portal/downloads/feature/src/lib/download-library-navigation.service.spec.ts b/libs/portal/downloads/feature/src/lib/download-library-navigation.service.spec.ts index 1a2908d59..fbbdb15c5 100644 --- a/libs/portal/downloads/feature/src/lib/download-library-navigation.service.spec.ts +++ b/libs/portal/downloads/feature/src/lib/download-library-navigation.service.spec.ts @@ -42,6 +42,7 @@ function download(overrides: Partial = {}): DownloadItem { url: 'https://media.example.test/video', posterUrl: 'https://media.example.test/poster.jpg', status: 'completed', + fileAvailability: 'available', ...overrides, }; } @@ -343,6 +344,10 @@ describe('DownloadLibraryNavigationService', () => { }, } ); + expect(router.navigate).not.toHaveBeenCalledWith( + expect.arrayContaining(['recent']), + expect.anything() + ); } ); @@ -504,5 +509,9 @@ describe('DownloadLibraryNavigationService', () => { }, } ); + expect(router.navigate).not.toHaveBeenCalledWith( + expect.arrayContaining(['recent']), + expect.anything() + ); }); }); diff --git a/libs/portal/downloads/feature/src/lib/downloads.component.spec.ts b/libs/portal/downloads/feature/src/lib/downloads.component.spec.ts index 37599870a..b313352d6 100644 --- a/libs/portal/downloads/feature/src/lib/downloads.component.spec.ts +++ b/libs/portal/downloads/feature/src/lib/downloads.component.spec.ts @@ -72,6 +72,7 @@ function download( title: `Download ${id}`, url: `https://example.com/${id}.mp4`, status: 'completed', + fileAvailability: 'available', bytesDownloaded: id * 10, filePath: `/downloads/${id}.mp4`, createdAt: `2026-07-${String(id).padStart(2, '0')}T12:00:00Z`, @@ -288,6 +289,13 @@ describe('DownloadsComponent', () => { ACTION_FAILED: 'Action failed', FILE_NOT_FOUND: 'File not found', FILE_ACTION_ERROR: 'File action failed', + DOWNLOAD_AGAIN: 'Download again', + STATUS: { + FILE_MISSING: 'File missing', + }, + ARIA: { + DOWNLOAD_AGAIN: 'Download {{title}} again', + }, SOURCE_PLAYLIST_MISSING: 'Source playlist missing', URL_COPIED: 'URL copied', URL_COPY_FAILED: 'URL copy failed', @@ -559,12 +567,58 @@ describe('DownloadsComponent', () => { ); }); - it('dispatches missing-file recovery by managed id', async () => { - const item = download(16, { fileAvailability: 'missing' }); + it('moves a recovered missing file from attention into the active queue', async () => { + const recovery = deferred<{ success: boolean }>(); + const item = download(16, { + fileAvailability: 'missing', + title: 'Missing movie', + }); + downloadsService.redownloadMissing.mockReturnValueOnce( + recovery.promise + ); + downloads.set([item]); + fixture.detectChanges(); - await component.runAction({ type: 'redownload', item }); + const attention = + fixture.nativeElement.querySelector( + '[data-test-id="downloads-attention-section"]' + ); + const recover = attention?.querySelector( + '[data-test-action="redownload"]' + ); + expect(attention?.textContent).toContain('File missing'); + expect(recover).not.toBeNull(); + recover?.click(); + fixture.detectChanges(); expect(downloadsService.redownloadMissing).toHaveBeenCalledWith(16); + expect( + downloadsService.redownloadMissing.mock.calls[0] + ).toHaveLength(1); + expect(component.pendingIds().has(16)).toBe(true); + + recovery.resolve({ success: true }); + await fixture.whenStable(); + downloads.set([ + { + ...item, + fileAvailability: 'not-applicable', + status: 'queued', + }, + ]); + fixture.detectChanges(); + + expect( + fixture.nativeElement.querySelector( + '[data-test-id="downloads-attention-section"]' + ) + ).toBeNull(); + expect( + fixture.nativeElement.querySelector( + '[data-test-id="downloads-active-section"]' + )?.textContent + ).toContain('Missing movie'); + expect(component.pendingIds().has(16)).toBe(false); }); it('refreshes a newly missing file before clearing pending state', async () => {