import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { renderHook, waitFor } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useAreas, useCreateArea, useUpdateArea, useDeleteArea, useToggleAreaStatus } from '../../src/hooks/useAreas'; import { areaClient } from '../../src/api/areaClient'; // 完整的mock响应对象 - 按照用户UI包规范 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 areaClient - 按照用户UI包规范 vi.mock('../../src/api/areaClient', () => { const mockAreaClient = { index: { $get: vi.fn(() => Promise.resolve({ status: 200, body: null })), $post: vi.fn(() => Promise.resolve({ status: 201, body: null })), }, ':id': { $put: vi.fn(() => Promise.resolve({ status: 200, body: null })), $delete: vi.fn(() => Promise.resolve({ status: 204, body: null })), }, }; const mockAreaClientManager = { get: vi.fn(() => mockAreaClient), }; return { areaClientManager: mockAreaClientManager, areaClient: mockAreaClient, }; }); // Mock sonner toast vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } })); // Test wrapper component const createWrapper = () => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, }, }, }); return ({ children }: { children: React.ReactNode }) => ( {children} ); }; describe('useAreas Hook', () => { beforeEach(() => { vi.clearAllMocks(); }); it('should fetch areas successfully', async () => { const mockAreas = [ { id: 1, name: '北京市', code: '110000', level: 1, parentId: null, isDisabled: 0 } ]; (areaClient.index.$get as any).mockResolvedValueOnce(createMockResponse(200, { data: mockAreas })); const { result } = renderHook(() => useAreas(), { wrapper: createWrapper() }); // Initial loading state expect(result.current.isLoading).toBe(true); // Wait for data to load await waitFor(() => { expect(result.current.isSuccess).toBe(true); }); expect(result.current.data).toEqual(mockAreas); expect(areaClient.index.$get).toHaveBeenCalledWith({ query: { page: 1, pageSize: 100, filters: '', sortBy: 'id', sortOrder: 'ASC' } }); }); it('should handle fetch areas error', async () => { (areaClient.index.$get as any).mockRejectedValueOnce(new Error('API Error')); const { result } = renderHook(() => useAreas(), { wrapper: createWrapper() }); // Wait for error await waitFor(() => { expect(result.current.isError).toBe(true); }); expect(result.current.error).toBeDefined(); }); it('should create area successfully', async () => { const mockAreaData = { name: '北京市', code: '110000', level: 1, parentId: null, isDisabled: 0 }; (areaClient.index.$post as any).mockResolvedValueOnce(createMockResponse(201)); const { result } = renderHook(() => useCreateArea(), { wrapper: createWrapper() }); await result.current.mutateAsync(mockAreaData); expect(areaClient.index.$post).toHaveBeenCalledWith({ json: mockAreaData }); }); it('should update area successfully', async () => { const mockUpdateData = { name: '北京市更新', code: '110000', level: 1, parentId: null, isDisabled: 0 }; (areaClient[':id'].$put as any).mockResolvedValueOnce(createMockResponse(200)); const { result } = renderHook(() => useUpdateArea(), { wrapper: createWrapper() }); await result.current.mutateAsync({ id: 1, data: mockUpdateData }); expect(areaClient[':id'].$put).toHaveBeenCalledWith({ param: { id: 1 }, json: mockUpdateData }); }); it('should delete area successfully', async () => { (areaClient[':id'].$delete as any).mockResolvedValueOnce(createMockResponse(204)); const { result } = renderHook(() => useDeleteArea(), { wrapper: createWrapper() }); await result.current.mutateAsync(1); expect(areaClient[':id'].$delete).toHaveBeenCalledWith({ param: { id: 1 } }); }); it('should toggle area status successfully', async () => { (areaClient[':id'].$put as any).mockResolvedValueOnce(createMockResponse(200)); const { result } = renderHook(() => useToggleAreaStatus(), { wrapper: createWrapper() }); await result.current.mutateAsync({ id: 1, isDisabled: 1 }); expect(areaClient[':id'].$put).toHaveBeenCalledWith({ param: { id: 1 }, json: { isDisabled: 1 } }); }); });