fix: bug
This commit is contained in:
338
hooks/use-download-media.test.ts
Normal file
338
hooks/use-download-media.test.ts
Normal file
@@ -0,0 +1,338 @@
|
||||
import { renderHook, act } from '@testing-library/react-native'
|
||||
import { useDownloadMedia } from './use-download-media'
|
||||
import * as FileSystem from 'expo-file-system'
|
||||
import * as MediaLibrary from 'expo-media-library'
|
||||
|
||||
jest.mock('expo-file-system', () => ({
|
||||
documentDirectory: 'file:///mock/document/',
|
||||
createDownloadResumable: jest.fn(),
|
||||
deleteAsync: jest.fn(),
|
||||
}))
|
||||
|
||||
jest.mock('expo-media-library', () => ({
|
||||
requestPermissionsAsync: jest.fn(),
|
||||
saveToLibraryAsync: jest.fn(),
|
||||
}))
|
||||
|
||||
describe('useDownloadMedia', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('initial state', () => {
|
||||
it('should return initial state', () => {
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
expect(result.current.loading).toBe(false)
|
||||
expect(result.current.error).toBeNull()
|
||||
expect(result.current.progress).toBe(0)
|
||||
expect(typeof result.current.download).toBe('function')
|
||||
})
|
||||
})
|
||||
|
||||
describe('permission handling', () => {
|
||||
it('should return error when permission is denied', async () => {
|
||||
;(MediaLibrary.requestPermissionsAsync as jest.Mock).mockResolvedValue({
|
||||
status: 'denied',
|
||||
})
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
let response
|
||||
await act(async () => {
|
||||
response = await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(response).toEqual({ success: false, error: 'Permission denied' })
|
||||
expect(result.current.error).toBe('Permission denied')
|
||||
expect(result.current.loading).toBe(false)
|
||||
})
|
||||
|
||||
it('should proceed when permission is granted', async () => {
|
||||
;(MediaLibrary.requestPermissionsAsync as jest.Mock).mockResolvedValue({
|
||||
status: 'granted',
|
||||
})
|
||||
|
||||
const mockDownloadAsync = jest.fn().mockResolvedValue({ uri: 'file:///mock/document/download_123.jpg' })
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
;(MediaLibrary.saveToLibraryAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
;(FileSystem.deleteAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
let response
|
||||
await act(async () => {
|
||||
response = await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(response).toEqual({ success: true })
|
||||
expect(MediaLibrary.requestPermissionsAsync).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('download functionality', () => {
|
||||
beforeEach(() => {
|
||||
;(MediaLibrary.requestPermissionsAsync as jest.Mock).mockResolvedValue({
|
||||
status: 'granted',
|
||||
})
|
||||
})
|
||||
|
||||
it('should download image successfully', async () => {
|
||||
const mockDownloadAsync = jest.fn().mockResolvedValue({ uri: 'file:///mock/document/download_123.jpg' })
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
;(MediaLibrary.saveToLibraryAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
;(FileSystem.deleteAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
let response
|
||||
await act(async () => {
|
||||
response = await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(response).toEqual({ success: true })
|
||||
expect(FileSystem.createDownloadResumable).toHaveBeenCalledWith(
|
||||
'https://example.com/image.jpg',
|
||||
expect.stringMatching(/^file:\/\/\/mock\/document\/download_\d+\.jpg$/),
|
||||
{},
|
||||
expect.any(Function)
|
||||
)
|
||||
expect(MediaLibrary.saveToLibraryAsync).toHaveBeenCalled()
|
||||
expect(FileSystem.deleteAsync).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/^file:\/\/\/mock\/document\/download_\d+\.jpg$/),
|
||||
{ idempotent: true }
|
||||
)
|
||||
})
|
||||
|
||||
it('should download video successfully', async () => {
|
||||
const mockDownloadAsync = jest.fn().mockResolvedValue({ uri: 'file:///mock/document/download_123.mp4' })
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
;(MediaLibrary.saveToLibraryAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
;(FileSystem.deleteAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
let response
|
||||
await act(async () => {
|
||||
response = await result.current.download('https://example.com/video.mp4', 'video')
|
||||
})
|
||||
|
||||
expect(response).toEqual({ success: true })
|
||||
expect(FileSystem.createDownloadResumable).toHaveBeenCalledWith(
|
||||
'https://example.com/video.mp4',
|
||||
expect.stringMatching(/^file:\/\/\/mock\/document\/download_\d+\.mp4$/),
|
||||
{},
|
||||
expect.any(Function)
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle download failure', async () => {
|
||||
const mockDownloadAsync = jest.fn().mockRejectedValue(new Error('Network error'))
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
let response
|
||||
await act(async () => {
|
||||
response = await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(response).toEqual({ success: false, error: 'Network error' })
|
||||
expect(result.current.error).toBe('Network error')
|
||||
})
|
||||
|
||||
it('should handle save to library failure', async () => {
|
||||
const mockDownloadAsync = jest.fn().mockResolvedValue({ uri: 'file:///mock/document/download_123.jpg' })
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
;(MediaLibrary.saveToLibraryAsync as jest.Mock).mockRejectedValue(new Error('Save failed'))
|
||||
;(FileSystem.deleteAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
let response
|
||||
await act(async () => {
|
||||
response = await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(response).toEqual({ success: false, error: 'Save failed' })
|
||||
expect(result.current.error).toBe('Save failed')
|
||||
// Should still attempt to clean up temp file
|
||||
expect(FileSystem.deleteAsync).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('loading state', () => {
|
||||
beforeEach(() => {
|
||||
;(MediaLibrary.requestPermissionsAsync as jest.Mock).mockResolvedValue({
|
||||
status: 'granted',
|
||||
})
|
||||
})
|
||||
|
||||
it('should set loading to true during download', async () => {
|
||||
let resolveDownload: (value: any) => void
|
||||
const downloadPromise = new Promise((resolve) => {
|
||||
resolveDownload = resolve
|
||||
})
|
||||
|
||||
const mockDownloadAsync = jest.fn().mockReturnValue(downloadPromise)
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
;(MediaLibrary.saveToLibraryAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
;(FileSystem.deleteAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
act(() => {
|
||||
result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(result.current.loading).toBe(true)
|
||||
|
||||
await act(async () => {
|
||||
resolveDownload!({ uri: 'file:///mock/document/download_123.jpg' })
|
||||
await downloadPromise
|
||||
})
|
||||
|
||||
expect(result.current.loading).toBe(false)
|
||||
})
|
||||
|
||||
it('should set loading to false after error', async () => {
|
||||
const mockDownloadAsync = jest.fn().mockRejectedValue(new Error('Error'))
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
await act(async () => {
|
||||
await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(result.current.loading).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('progress tracking', () => {
|
||||
beforeEach(() => {
|
||||
;(MediaLibrary.requestPermissionsAsync as jest.Mock).mockResolvedValue({
|
||||
status: 'granted',
|
||||
})
|
||||
})
|
||||
|
||||
it('should update progress during download', async () => {
|
||||
let progressCallback: (progress: { totalBytesWritten: number; totalBytesExpectedToWrite: number }) => void
|
||||
|
||||
const mockDownloadAsync = jest.fn().mockImplementation(async () => {
|
||||
// Simulate progress updates
|
||||
progressCallback({ totalBytesWritten: 50, totalBytesExpectedToWrite: 100 })
|
||||
return { uri: 'file:///mock/document/download_123.jpg' }
|
||||
})
|
||||
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockImplementation(
|
||||
(url, fileUri, options, callback) => {
|
||||
progressCallback = callback
|
||||
return { downloadAsync: mockDownloadAsync }
|
||||
}
|
||||
)
|
||||
;(MediaLibrary.saveToLibraryAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
;(FileSystem.deleteAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
await act(async () => {
|
||||
await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
// Progress should be reset after completion
|
||||
expect(result.current.progress).toBe(0)
|
||||
})
|
||||
|
||||
it('should reset progress on new download', async () => {
|
||||
const mockDownloadAsync = jest.fn().mockResolvedValue({ uri: 'file:///mock/document/download_123.jpg' })
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
;(MediaLibrary.saveToLibraryAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
;(FileSystem.deleteAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
await act(async () => {
|
||||
await result.current.download('https://example.com/image1.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(result.current.progress).toBe(0)
|
||||
|
||||
await act(async () => {
|
||||
await result.current.download('https://example.com/image2.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(result.current.progress).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('error handling', () => {
|
||||
it('should clear error on new download attempt', async () => {
|
||||
;(MediaLibrary.requestPermissionsAsync as jest.Mock)
|
||||
.mockResolvedValueOnce({ status: 'denied' })
|
||||
.mockResolvedValueOnce({ status: 'granted' })
|
||||
|
||||
const mockDownloadAsync = jest.fn().mockResolvedValue({ uri: 'file:///mock/document/download_123.jpg' })
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
;(MediaLibrary.saveToLibraryAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
;(FileSystem.deleteAsync as jest.Mock).mockResolvedValue(undefined)
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
await act(async () => {
|
||||
await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(result.current.error).toBe('Permission denied')
|
||||
|
||||
await act(async () => {
|
||||
await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(result.current.error).toBeNull()
|
||||
})
|
||||
|
||||
it('should handle unknown error types', async () => {
|
||||
;(MediaLibrary.requestPermissionsAsync as jest.Mock).mockResolvedValue({
|
||||
status: 'granted',
|
||||
})
|
||||
|
||||
const mockDownloadAsync = jest.fn().mockRejectedValue('Unknown error')
|
||||
;(FileSystem.createDownloadResumable as jest.Mock).mockReturnValue({
|
||||
downloadAsync: mockDownloadAsync,
|
||||
})
|
||||
|
||||
const { result } = renderHook(() => useDownloadMedia())
|
||||
|
||||
let response
|
||||
await act(async () => {
|
||||
response = await result.current.download('https://example.com/image.jpg', 'image')
|
||||
})
|
||||
|
||||
expect(response).toEqual({ success: false, error: 'Download failed' })
|
||||
expect(result.current.error).toBe('Download failed')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user