import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import ChannelManagement from '../../src/components/ChannelManagement'; import { channelClientManager } from '../../src/api/channelClient'; // 完整的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 client vi.mock('../../src/api/channelClient', () => { const mockChannelClient = { getAllChannels: { $get: vi.fn(() => Promise.resolve(createMockResponse(200, { data: [ { id: 1, channelName: '微信小程序', channelType: '小程序', contactPerson: '张三', contactPhone: '13800138000', description: '微信小程序渠道', status: 1, createTime: '2024-01-01T00:00:00Z', updateTime: '2024-01-01T00:00:00Z' }, { id: 2, channelName: '支付宝生活号', channelType: '生活号', contactPerson: '李四', contactPhone: '13900139000', description: '支付宝生活号渠道', status: 1, createTime: '2024-01-02T00:00:00Z', updateTime: '2024-01-02T00:00:00Z' } ], total: 2 }))), }, createChannel: { $post: vi.fn(() => Promise.resolve(createMockResponse(200, { id: 3, channelName: '抖音小程序', channelType: '小程序', contactPerson: '王五', contactPhone: '13700137000', description: '抖音小程序渠道', status: 1 }))), }, updateChannel: { $post: vi.fn(() => Promise.resolve(createMockResponse(200, { id: 1, channelName: '更新后的微信小程序', channelType: '小程序', contactPerson: '张三', contactPhone: '13800138000', description: '更新后的微信小程序渠道', status: 1 }))), }, deleteChannel: { $post: vi.fn(() => Promise.resolve(createMockResponse(200, { success: true }))), }, searchChannels: { $get: vi.fn(() => Promise.resolve(createMockResponse(200, { data: [ { id: 1, channelName: '微信小程序', channelType: '小程序', contactPerson: '张三', contactPhone: '13800138000', description: '微信小程序渠道', status: 1, createTime: '2024-01-01T00:00:00Z', updateTime: '2024-01-01T00:00:00Z' } ], total: 1 }))), }, getChannel: { ':id': { $get: vi.fn(() => Promise.resolve(createMockResponse(200, { id: 1, channelName: '微信小程序', channelType: '小程序', contactPerson: '张三', contactPhone: '13800138000', description: '微信小程序渠道', status: 1, createTime: '2024-01-01T00:00:00Z', updateTime: '2024-01-01T00:00:00Z' }))), } }, // 注意:渠道模块使用自定义路由,没有batchDeleteChannel路由 }; const mockChannelClientManager = { get: vi.fn(() => mockChannelClient), }; return { channelClientManager: mockChannelClientManager, channelClient: mockChannelClient, }; }); // Mock toast vi.mock('sonner', () => ({ toast: { success: vi.fn(() => {}), error: vi.fn(() => {}), }, })); describe('ChannelManagement 集成测试', () => { let queryClient: QueryClient; beforeEach(() => { queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, }, }, }); vi.clearAllMocks(); }); const renderComponent = () => { return render( ); }; it('应该正确渲染渠道列表', async () => { renderComponent(); // 等待数据加载 await waitFor(() => { expect(screen.getByText('微信小程序')).toBeInTheDocument(); }); // 验证表格内容 expect(screen.getByText('支付宝生活号')).toBeInTheDocument(); expect(screen.getByText('张三')).toBeInTheDocument(); expect(screen.getByText('13800138000')).toBeInTheDocument(); expect(screen.getByText('小程序')).toBeInTheDocument(); expect(screen.getByText('生活号')).toBeInTheDocument(); }); it('应该打开创建渠道模态框', async () => { renderComponent(); // 等待数据加载 await waitFor(() => { expect(screen.getByText('微信小程序')).toBeInTheDocument(); }); // 点击创建按钮 const createButton = screen.getByTestId('create-channel-button'); fireEvent.click(createButton); // 验证模态框打开 expect(screen.getByTestId('create-channel-modal-title')).toBeInTheDocument(); }); it('应该成功创建渠道', async () => { renderComponent(); // 等待数据加载 await waitFor(() => { expect(screen.getByText('微信小程序')).toBeInTheDocument(); }); // 打开创建模态框 const createButton = screen.getByTestId('create-channel-button'); fireEvent.click(createButton); // 填写表单 const channelNameInput = screen.getByLabelText('渠道名称 *'); const channelTypeInput = screen.getByLabelText('渠道类型'); const contactPersonInput = screen.getByLabelText('联系人'); const contactPhoneInput = screen.getByLabelText('联系电话'); const descriptionInput = screen.getByLabelText('描述'); fireEvent.change(channelNameInput, { target: { value: '抖音小程序' } }); fireEvent.change(channelTypeInput, { target: { value: '小程序' } }); fireEvent.change(contactPersonInput, { target: { value: '王五' } }); fireEvent.change(contactPhoneInput, { target: { value: '13700137000' } }); fireEvent.change(descriptionInput, { target: { value: '抖音小程序渠道' } }); // 提交表单 const submitButton = screen.getByText('创建'); fireEvent.click(submitButton); // 验证API调用 await waitFor(() => { expect(channelClientManager.get().createChannel.$post).toHaveBeenCalledWith({ json: { channelName: '抖音小程序', channelType: '小程序', contactPerson: '王五', contactPhone: '13700137000', description: '抖音小程序渠道' } }); }); }); it('应该打开编辑渠道模态框', async () => { renderComponent(); // 等待数据加载 await waitFor(() => { expect(screen.getByText('微信小程序')).toBeInTheDocument(); }); // 点击编辑按钮 const editButton = screen.getByTestId('edit-channel-1'); fireEvent.click(editButton); // 验证模态框打开并显示正确的数据 expect(screen.getByTestId('create-channel-modal-title')).toBeInTheDocument(); expect(screen.getByDisplayValue('微信小程序')).toBeInTheDocument(); }); it('应该成功更新渠道', async () => { renderComponent(); // 等待数据加载 await waitFor(() => { expect(screen.getByText('微信小程序')).toBeInTheDocument(); }); // 打开编辑模态框 const editButton = screen.getByTestId('edit-channel-1'); fireEvent.click(editButton); // 修改表单数据 const channelNameInput = screen.getByLabelText('渠道名称 *'); fireEvent.change(channelNameInput, { target: { value: '更新后的微信小程序' } }); // 提交表单 const submitButton = screen.getByText('更新'); fireEvent.click(submitButton); // 验证API调用 await waitFor(() => { expect(channelClientManager.get().updateChannel.$post).toHaveBeenCalledWith({ json: { id: 1, channelName: '更新后的微信小程序', channelType: '小程序', contactPerson: '张三', contactPhone: '13800138000', description: '微信小程序渠道' } }); }); }); it('应该打开删除确认对话框', async () => { renderComponent(); // 等待数据加载 await waitFor(() => { expect(screen.getByText('微信小程序')).toBeInTheDocument(); }); // 点击删除按钮 const deleteButton = screen.getByTestId('delete-channel-1'); fireEvent.click(deleteButton); // 验证删除确认对话框打开 expect(screen.getByTestId('delete-confirm-dialog-title')).toBeInTheDocument(); }); it('应该成功搜索渠道', async () => { renderComponent(); // 等待数据加载 await waitFor(() => { expect(screen.getByText('微信小程序')).toBeInTheDocument(); }); // 输入搜索关键词 const searchInput = screen.getByTestId('search-input'); fireEvent.change(searchInput, { target: { value: '微信' } }); // 点击搜索按钮 const searchButton = screen.getByTestId('search-button'); fireEvent.click(searchButton); // 验证API调用 await waitFor(() => { expect(channelClientManager.get().searchChannels.$get).toHaveBeenCalledWith({ query: { name: '微信', skip: 0, take: 10 } }); }); }); it('应该处理API错误', async () => { // Mock API错误 - 使用vi.spyOn来mock const mockErrorResponse = createMockResponse(500, { message: '服务器错误' }); vi.spyOn(channelClientManager.get().getAllChannels, '$get').mockResolvedValue(mockErrorResponse as any); renderComponent(); // 验证错误处理 - 等待错误状态 await waitFor(() => { // 检查是否没有显示数据 expect(screen.queryByText('微信小程序')).not.toBeInTheDocument(); expect(screen.queryByText('支付宝生活号')).not.toBeInTheDocument(); }); // 恢复原始mock vi.mocked(channelClientManager.get().getAllChannels.$get).mockRestore(); }); });