Spaces:
Running
Running
| import { describe, it, expect, vi, beforeEach } from 'vitest'; | |
| import { renderHook, act, waitFor } from '@testing-library/react'; | |
| import { useChapterDownload } from './useChapterDownload'; | |
| import * as lib from './offlineLibrary'; | |
| import { useToastStore } from '../../../store/toastStore'; | |
| const META = { mediaId: 'm1', mediaTitle: 'T', chapterNumber: 1, chapterTitle: 'C1' }; | |
| const PAGES = [{ number: 0, image_url: 'u0' }]; | |
| describe('useChapterDownload', () => { | |
| beforeEach(() => vi.restoreAllMocks()); | |
| it('starts idle when not downloaded', async () => { | |
| vi.spyOn(lib, 'isChapterDownloaded').mockResolvedValue(false); | |
| const { result } = renderHook(() => useChapterDownload(META, PAGES)); | |
| await waitFor(() => expect(result.current.status).toBe('idle')); | |
| }); | |
| it('starts downloaded when already cached', async () => { | |
| vi.spyOn(lib, 'isChapterDownloaded').mockResolvedValue(true); | |
| const { result } = renderHook(() => useChapterDownload(META, PAGES)); | |
| await waitFor(() => expect(result.current.status).toBe('downloaded')); | |
| }); | |
| it('transitions idle -> downloading -> downloaded and tracks progress', async () => { | |
| vi.spyOn(lib, 'isChapterDownloaded').mockResolvedValue(false); | |
| vi.spyOn(lib, 'downloadChapter').mockImplementation(async (_m, _p, onProgress) => { | |
| onProgress?.(0.5); | |
| onProgress?.(1); | |
| return {} as lib.DownloadedChapter; | |
| }); | |
| const { result } = renderHook(() => useChapterDownload(META, PAGES)); | |
| await waitFor(() => expect(result.current.status).toBe('idle')); | |
| await act(async () => { | |
| await result.current.download(); | |
| }); | |
| expect(result.current.status).toBe('downloaded'); | |
| expect(result.current.progress).toBe(1); | |
| }); | |
| it('sets error status with message on failure', async () => { | |
| vi.spyOn(lib, 'isChapterDownloaded').mockResolvedValue(false); | |
| vi.spyOn(lib, 'downloadChapter').mockRejectedValue(new lib.OfflineQuotaError()); | |
| const { result } = renderHook(() => useChapterDownload(META, PAGES)); | |
| await waitFor(() => expect(result.current.status).toBe('idle')); | |
| await act(async () => { | |
| await result.current.download(); | |
| }); | |
| expect(result.current.status).toBe('error'); | |
| expect(result.current.error).toContain('stockage'); | |
| }); | |
| it('toasts an error on download failure', async () => { | |
| vi.spyOn(lib, 'isChapterDownloaded').mockResolvedValue(false); | |
| vi.spyOn(lib, 'downloadChapter').mockRejectedValue(new lib.OfflineQuotaError()); | |
| const addToast = vi | |
| .spyOn(useToastStore.getState(), 'addToast') | |
| .mockImplementation(() => {}); | |
| const { result } = renderHook(() => useChapterDownload(META, PAGES)); | |
| await waitFor(() => expect(result.current.status).toBe('idle')); | |
| await act(async () => { | |
| await result.current.download(); | |
| }); | |
| expect(addToast).toHaveBeenCalledWith(expect.stringContaining('stockage'), 'error'); | |
| }); | |
| it('remove() deletes and returns to idle', async () => { | |
| vi.spyOn(lib, 'isChapterDownloaded').mockResolvedValue(true); | |
| const del = vi.spyOn(lib, 'deleteChapter').mockResolvedValue(); | |
| const { result } = renderHook(() => useChapterDownload(META, PAGES)); | |
| await waitFor(() => expect(result.current.status).toBe('downloaded')); | |
| await act(async () => { | |
| await result.current.remove(); | |
| }); | |
| expect(del).toHaveBeenCalledWith('m1', 1); | |
| expect(result.current.status).toBe('idle'); | |
| expect(result.current.progress).toBe(0); | |
| expect(result.current.error).toBeNull(); | |
| }); | |
| }); | |