import { describe, it, expect, vi, beforeEach } from 'vitest'; import { renderHook, waitFor } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useFileManagement } from '../../src/hooks/useFileManagement'; // 完整的mock响应对象 const createMockResponse = (status: number, data?: any) => ({ status, ok: status >= 200 && status < 300, body: null, bodyUsed: false, statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error', headers: new Headers(), url: '', redirected: false, type: 'basic' as ResponseType, json: async () => data || {}, text: async () => '', blob: async () => new Blob(), arrayBuffer: async () => new ArrayBuffer(0), formData: async () => new FormData(), clone: function() { return this; } }); // Mock API客户端 vi.mock('../../src/api/fileClient', () => { const mockFileClient = { index: { $get: vi.fn(() => Promise.resolve({ status: 200, body: null })), }, ':id': { $get: vi.fn(() => Promise.resolve({ status: 200, body: null })), $put: vi.fn(() => Promise.resolve({ status: 200, body: null })), $delete: vi.fn(() => Promise.resolve({ status: 204, body: null })), }, }; const mockFileClientManager = { get: vi.fn(() => mockFileClient), }; return { fileClientManager: mockFileClientManager, fileClient: mockFileClient, }; }); // Mock toast vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn(), warning: vi.fn(), }, })); describe('useFileManagement', () => { let queryClient: QueryClient; beforeEach(() => { queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); vi.clearAllMocks(); }); const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); const mockFiles = [ { id: 1, name: 'test-file-1.jpg', type: 'image/jpeg', size: 1024, fullUrl: 'http://example.com/test-file-1.jpg', uploadTime: '2024-01-01T00:00:00Z', }, { id: 2, name: 'test-file-2.pdf', type: 'application/pdf', size: 2048, fullUrl: 'http://example.com/test-file-2.pdf', uploadTime: '2024-01-01T00:00:00Z', }, ]; it('应该初始化文件管理钩子', () => { const { result } = renderHook(() => useFileManagement(), { wrapper }); expect(result.current.files).toEqual([]); expect(result.current.isLoading).toBe(true); expect(result.current.searchText).toBe(''); }); it('应该获取文件列表', async () => { const { fileClient } = await import('../../src/api/fileClient'); (fileClient.index.$get as any).mockResolvedValue(createMockResponse(200, { data: mockFiles, pagination: { current: 1, pageSize: 10, total: 2 } })); const { result } = renderHook(() => useFileManagement(), { wrapper }); await waitFor(() => { expect(result.current.isLoading).toBe(false); }); expect(result.current.files).toEqual(mockFiles); expect(result.current.pagination.total).toBe(2); }); it('应该处理搜索', async () => { const { fileClient } = await import('../../src/api/fileClient'); (fileClient.index.$get as any).mockResolvedValue(createMockResponse(200, { data: [mockFiles[0]], pagination: { current: 1, pageSize: 10, total: 1 } })); const { result } = renderHook(() => useFileManagement(), { wrapper }); result.current.handleSearch('test'); await waitFor(() => { expect(result.current.searchText).toBe('test'); }); expect(fileClient.index.$get).toHaveBeenCalledWith({ query: { page: 1, pageSize: 10, keyword: 'test' } }); }); it('应该处理分页', async () => { const { fileClient } = await import('../../src/api/fileClient'); (fileClient.index.$get as any).mockResolvedValue(createMockResponse(200, { data: mockFiles, pagination: { current: 2, pageSize: 5, total: 2 } })); const { result } = renderHook(() => useFileManagement(), { wrapper }); result.current.handlePageChange(2, 5); await waitFor(() => { expect(result.current.pagination.current).toBe(2); expect(result.current.pagination.pageSize).toBe(5); }); }); it('应该更新文件信息', async () => { const { fileClient } = await import('../../src/api/fileClient'); const { toast } = await import('sonner'); (fileClient[':id'].$put as any).mockResolvedValue(createMockResponse(200, { id: 1, name: 'updated-file.jpg', description: 'Updated description' })); const { result } = renderHook(() => useFileManagement(), { wrapper }); await result.current.updateFile({ id: 1, data: { name: 'updated-file.jpg', description: 'Updated description' } }); expect(fileClient[':id'].$put).toHaveBeenCalledWith({ param: { id: 1 }, json: { name: 'updated-file.jpg', description: 'Updated description' } }); expect(toast.success).toHaveBeenCalledWith('文件信息更新成功'); }); it('应该删除文件', async () => { const { fileClient } = await import('../../src/api/fileClient'); const { toast } = await import('sonner'); (fileClient[':id'].$delete as any).mockResolvedValue(createMockResponse(204)); const { result } = renderHook(() => useFileManagement(), { wrapper }); await result.current.deleteFile(1); expect(fileClient[':id'].$delete).toHaveBeenCalledWith({ param: { id: 1 } }); expect(toast.success).toHaveBeenCalledWith('文件删除成功'); }); it('应该检查文件是否可预览', () => { const { result } = renderHook(() => useFileManagement(), { wrapper }); expect(result.current.isPreviewable('image/jpeg')).toBe(true); expect(result.current.isPreviewable('video/mp4')).toBe(true); expect(result.current.isPreviewable('application/pdf')).toBe(false); expect(result.current.isPreviewable(null)).toBe(false); }); it('应该处理文件预览', () => { const { result } = renderHook(() => useFileManagement(), { wrapper }); const windowOpenSpy = vi.spyOn(window, 'open').mockImplementation(() => null); const file = { id: 1, name: 'test.jpg', type: 'image/jpeg', fullUrl: 'http://example.com/test.jpg' } as any; result.current.handlePreview(file); expect(windowOpenSpy).toHaveBeenCalledWith('http://example.com/test.jpg', '_blank'); windowOpenSpy.mockRestore(); }); it('应该处理文件下载', () => { const { result } = renderHook(() => useFileManagement(), { wrapper }); const createElementSpy = vi.spyOn(document, 'createElement'); const appendChildSpy = vi.spyOn(document.body, 'appendChild'); const removeChildSpy = vi.spyOn(document.body, 'removeChild'); const file = { id: 1, name: 'test.jpg', fullUrl: 'http://example.com/test.jpg' } as any; result.current.handleDownload(file); expect(createElementSpy).toHaveBeenCalledWith('a'); expect(appendChildSpy).toHaveBeenCalled(); expect(removeChildSpy).toHaveBeenCalled(); createElementSpy.mockRestore(); appendChildSpy.mockRestore(); removeChildSpy.mockRestore(); }); });