import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor, fireEvent } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { BrowserRouter } from 'react-router'; import { AreaManagement } from '../../src/components/AreaManagement'; 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 TestWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, }, }, }); return ( {children} ); }; describe('AreaManagement Integration Tests', () => { beforeEach(() => { vi.clearAllMocks(); }); it('should render area management component with title', async () => { // Mock successful API response for province data (areaClient.index.$get as any).mockResolvedValueOnce(createMockResponse(200, { data: [ { id: 1, name: '北京市', code: '110000', level: 1, parentId: null, isDisabled: 0 } ] })); render( ); // Check if title is rendered expect(screen.getByText('省市区树形管理')).toBeInTheDocument(); expect(screen.getByText('异步加载树形结构,高效管理省市区数据')).toBeInTheDocument(); // Wait for loading to complete await waitFor(() => { expect(screen.getByText('北京市')).toBeInTheDocument(); }); }); it('should show loading state when fetching data', async () => { // Mock delayed API response (areaClient.index.$get as any).mockImplementationOnce(() => new Promise(resolve => setTimeout(() => resolve(createMockResponse(200, { data: [] })), 100)) ); render( ); // Check if loading state is shown expect(screen.getByText('加载中...')).toBeInTheDocument(); // Wait for loading to complete await waitFor(() => { expect(screen.queryByText('加载中...')).not.toBeInTheDocument(); }); }); it('should show empty state when no data', async () => { // Mock empty API response (areaClient.index.$get as any).mockResolvedValueOnce(createMockResponse(200, { data: [] })); render( ); // Wait for empty state to appear await waitFor(() => { expect(screen.getByText('暂无数据')).toBeInTheDocument(); }); }); it('should open create dialog when clicking add button', async () => { // Mock successful API response (areaClient.index.$get as any).mockResolvedValueOnce(createMockResponse(200, { data: [ { id: 1, name: '北京市', code: '110000', level: 1, parentId: null, isDisabled: 0 } ] })); render( ); // Wait for data to load await waitFor(() => { expect(screen.getByText('北京市')).toBeInTheDocument(); }); // Click add button const addButton = screen.getByText('新增省'); fireEvent.click(addButton); // Check if dialog opens await waitFor(() => { expect(screen.getByRole('heading', { name: '新增省' })).toBeInTheDocument(); expect(screen.getByText('填写省信息')).toBeInTheDocument(); }); }); it('should handle API errors gracefully', async () => { // Mock API error (areaClient.index.$get as any).mockRejectedValueOnce(new Error('API Error')); render( ); // Wait for error state await waitFor(() => { // Component should handle errors gracefully expect(screen.getByText('省市区树形管理')).toBeInTheDocument(); }); }); it('should complete create and delete workflow', async () => { const { toast } = await import('sonner'); // Mock initial areas data const mockAreas = { data: [ { id: 1, name: '北京市', code: '110000', level: 1, parentId: null, isDisabled: 0 } ] }; // Mock initial data fetch (areaClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockAreas)); render( ); // Wait for initial data to load await waitFor(() => { expect(screen.getByText('北京市')).toBeInTheDocument(); }); // Test create area const addButton = screen.getByText('新增省'); fireEvent.click(addButton); // Wait for create dialog await waitFor(() => { expect(screen.getByRole('heading', { name: '新增省' })).toBeInTheDocument(); }); // Fill create form const nameInput = screen.getByPlaceholderText('输入区域名称'); const codeInput = screen.getByPlaceholderText('输入行政区划代码'); fireEvent.change(nameInput, { target: { value: '上海市' } }); fireEvent.change(codeInput, { target: { value: '310000' } }); // Mock successful creation (areaClient.index.$post as any).mockResolvedValue(createMockResponse(201, { id: 2, name: '上海市' })); const submitButton = screen.getByText('创建'); fireEvent.click(submitButton); await waitFor(() => { expect(areaClient.index.$post).toHaveBeenCalledWith({ json: { name: '上海市', code: '310000', level: 1, parentId: null, isDisabled: 0 } }); expect(toast.success).toHaveBeenCalledWith('省市区创建成功'); }); // 跳过编辑操作测试,专注于创建和删除操作 // Test delete area const deleteButtons = screen.getAllByRole('button', { name: '删除' }); fireEvent.click(deleteButtons[0]); // Confirm deletion expect(screen.getByRole('heading', { name: '确认删除' })).toBeInTheDocument(); // Mock successful deletion (areaClient[':id']['$delete'] as any).mockResolvedValue({ status: 204, }); // 查找删除确认按钮 const confirmDeleteButton = screen.getByRole('button', { name: '确认删除' }); fireEvent.click(confirmDeleteButton); await waitFor(() => { expect(areaClient[':id']['$delete']).toHaveBeenCalledWith({ param: { id: 1 }, }); expect(toast.success).toHaveBeenCalledWith('省市区删除成功'); }); }); it('should handle API errors in CRUD operations', async () => { const { areaClient } = await import('../../src/api/areaClient'); const { toast } = await import('sonner'); // Mock initial data const mockAreas = { data: [ { id: 1, name: '北京市', code: '110000', level: 1, parentId: null, isDisabled: 0 } ] }; (areaClient.index.$get as any).mockResolvedValue(createMockResponse(200, mockAreas)); render( ); // Wait for data to load await waitFor(() => { expect(screen.getByText('北京市')).toBeInTheDocument(); }); // Test create area error const addButton = screen.getByText('新增省'); fireEvent.click(addButton); await waitFor(() => { expect(screen.getByRole('heading', { name: '新增省' })).toBeInTheDocument(); }); const nameInput = screen.getByPlaceholderText('输入区域名称'); const codeInput = screen.getByPlaceholderText('输入行政区划代码'); fireEvent.change(nameInput, { target: { value: '上海市' } }); fireEvent.change(codeInput, { target: { value: '310000' } }); // Mock creation error (areaClient.index.$post as any).mockRejectedValue(new Error('Creation failed')); const submitButton = screen.getByText('创建'); fireEvent.click(submitButton); await waitFor(() => { expect(toast.error).toHaveBeenCalledWith('创建失败,请重试'); }); }); });