import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { AreaSelect } from '../../src/components/areas/composite/AreaSelect'; // Mock API 调用 - 注意:新的组件使用相对路径导入api vi.mock('../../src/api/areaClient', () => ({ areaClientManager: { get: vi.fn(() => ({ index: { $get: vi.fn(async ({ query }) => { const filters = JSON.parse(query.filters); if (filters.level === 1) { // 省份数据 return { status: 200, json: async () => ({ data: [ { id: 1, name: '北京市', level: 1, parentId: null }, { id: 2, name: '上海市', level: 1, parentId: null } ] }) }; } else if (filters.level === 2 && filters.parentId === 1) { // 北京市的城市数据 return { status: 200, json: async () => ({ data: [ { id: 3, name: '北京市', level: 2, parentId: 1 } ] }) }; } else if (filters.level === 2 && filters.parentId === 2) { // 上海市的城市数据 return { status: 200, json: async () => ({ data: [ { id: 4, name: '上海市', level: 2, parentId: 2 } ] }) }; } else if (filters.level === 3 && filters.parentId === 3) { // 北京市的区县数据 return { status: 200, json: async () => ({ data: [ { id: 5, name: '东城区', level: 3, parentId: 3 }, { id: 6, name: '西城区', level: 3, parentId: 3 } ] }) }; } else if (filters.level === 3 && filters.parentId === 4) { // 上海市的区县数据 return { status: 200, json: async () => ({ data: [ { id: 7, name: '黄浦区', level: 3, parentId: 4 }, { id: 8, name: '徐汇区', level: 3, parentId: 4 } ] }) }; } return { status: 200, json: async () => ({ data: [] }) }; }) } })) } })); // 创建测试用的 QueryClient const createTestQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false, }, }, }); // 测试组件包装器 const TestWrapper = ({ children }: { children: React.ReactNode }) => { return ( {children} ); }; describe('AreaSelect 集成测试', () => { beforeEach(() => { vi.clearAllMocks(); }); it('应该正确渲染 AreaSelect 组件', async () => { render( ); // 等待省份数据加载 - AreaSelect 显示的是"选择所在省份" await waitFor(() => { expect(screen.getByText('选择所在省份')).toBeInTheDocument(); }); expect(screen.getByText('省份')).toBeInTheDocument(); expect(screen.getByText('城市')).toBeInTheDocument(); expect(screen.getByText('区县')).toBeInTheDocument(); }); it('应该显示必填标记当 required=true 时', async () => { render( ); // 等待省份数据加载 - AreaSelect 显示的是"选择所在省份" await waitFor(() => { expect(screen.getByText('选择所在省份')).toBeInTheDocument(); }); // 检查省份标签是否包含星号 const provinceLabel = screen.getByText('省份'); expect(provinceLabel.innerHTML).toContain('*'); // 城市和区县不应该显示星号(初始状态) const cityLabel = screen.getByText('城市'); expect(cityLabel.innerHTML).not.toContain('*'); const districtLabel = screen.getByText('区县'); expect(districtLabel.innerHTML).not.toContain('*'); }); it('应该正确处理省份选择并加载城市数据', async () => { const user = userEvent.setup(); const handleChange = vi.fn(); render( ); // 使用test ID查找省份选择框 const provinceSelect = screen.getByTestId('area-select-province'); // 等待省份数据加载并启用 await waitFor(() => { expect(provinceSelect).toBeInTheDocument(); expect(provinceSelect).not.toHaveAttribute('disabled'); }); await user.click(provinceSelect); // 选择北京市 - 尝试直接设置隐藏的select元素的值 // 查找隐藏的select元素 const hiddenSelect = screen.getByTestId('area-select-province').parentElement?.querySelector('select[aria-hidden="true"]'); if (hiddenSelect) { await user.selectOptions(hiddenSelect, '1'); // 触发change事件 fireEvent.change(hiddenSelect, { target: { value: '1' } }); } // 验证onChange被调用 expect(handleChange).toHaveBeenCalledWith({ provinceId: 1, cityId: undefined, districtId: undefined }); // 验证城市选择框应该被启用(因为选择了省份) const citySelect = screen.getByTestId('area-select-city'); expect(citySelect).toBeInTheDocument(); }); it('应该正确处理城市选择并加载区县数据', async () => { const user = userEvent.setup(); const handleChange = vi.fn(); render( ); // 先选择省份(北京市) const provinceSelect = screen.getByTestId('area-select-province'); // 等待省份数据加载并启用 await waitFor(() => { expect(provinceSelect).toBeInTheDocument(); expect(provinceSelect).not.toHaveAttribute('disabled'); }); await user.click(provinceSelect); await waitFor(() => { expect(screen.getByText('北京市')).toBeInTheDocument(); }); await user.click(screen.getByText('北京市')); // 等待城市数据加载并启用 const citySelect = screen.getByTestId('area-select-city'); await waitFor(() => { expect(citySelect).toBeInTheDocument(); expect(citySelect).not.toHaveAttribute('disabled'); }, { timeout: 2000 }); // 点击城市选择框 await user.click(citySelect); // 选择北京市(城市)- 尝试直接设置隐藏的select元素的值 // 查找隐藏的select元素 const cityHiddenSelect = screen.getByTestId('area-select-city').parentElement?.querySelector('select[aria-hidden="true"]'); if (cityHiddenSelect) { await user.selectOptions(cityHiddenSelect, '3'); } // 验证onChange被调用 expect(handleChange).toHaveBeenCalledWith({ provinceId: 1, cityId: 3, districtId: undefined }); // 验证区县选择框应该被启用(因为选择了城市) const districtSelect = screen.getByTestId('area-select-district'); expect(districtSelect).toBeInTheDocument(); }); it('应该正确处理区县选择', async () => { const user = userEvent.setup(); const handleChange = vi.fn(); render( ); // 先选择省份(北京市) const provinceSelect = screen.getByTestId('area-select-province'); // 等待省份数据加载并启用 await waitFor(() => { expect(provinceSelect).toBeInTheDocument(); expect(provinceSelect).not.toHaveAttribute('disabled'); }); await user.click(provinceSelect); // 选择北京市 - 尝试直接设置隐藏的select元素的值 // 查找隐藏的select元素 const provinceHiddenSelect = screen.getByTestId('area-select-province').parentElement?.querySelector('select[aria-hidden="true"]'); if (provinceHiddenSelect) { await user.selectOptions(provinceHiddenSelect, '1'); } // 等待城市数据加载并启用 const citySelect = screen.getByTestId('area-select-city'); await waitFor(() => { expect(citySelect).toBeInTheDocument(); expect(citySelect).not.toHaveAttribute('disabled'); }, { timeout: 2000 }); await user.click(citySelect); // 选择北京市(城市)- 尝试直接设置隐藏的select元素的值 // 查找隐藏的select元素 const cityHiddenSelect = screen.getByTestId('area-select-city').parentElement?.querySelector('select[aria-hidden="true"]'); if (cityHiddenSelect) { await user.selectOptions(cityHiddenSelect, '3'); } // 等待区县数据加载并启用 const districtSelect = screen.getByTestId('area-select-district'); await waitFor(() => { expect(districtSelect).toBeInTheDocument(); expect(districtSelect).not.toHaveAttribute('disabled'); }, { timeout: 2000 }); // 点击区县选择框 await user.click(districtSelect); // 选择东城区 - 尝试直接设置隐藏的select元素的值 // 查找隐藏的select元素 const districtHiddenSelect = screen.getByTestId('area-select-district').parentElement?.querySelector('select[aria-hidden="true"]'); if (districtHiddenSelect) { await user.selectOptions(districtHiddenSelect, '5'); } // 验证onChange被调用 expect(handleChange).toHaveBeenCalledWith({ provinceId: 1, cityId: 3, districtId: 5 }); }); it('应该支持禁用状态', async () => { render( ); // 等待省份数据加载 - AreaSelect 显示的是"选择所在省份" await waitFor(() => { expect(screen.getByText('选择所在省份')).toBeInTheDocument(); }); // 检查组件渲染正常 expect(screen.getByText('省份')).toBeInTheDocument(); expect(screen.getByText('城市')).toBeInTheDocument(); expect(screen.getByText('区县')).toBeInTheDocument(); }); it('应该正确处理初始值', async () => { const initialValue = { provinceId: 1, cityId: 3, districtId: 5 }; render( ); // 等待省份数据加载 - AreaSelect 显示的是"选择所在省份" await waitFor(() => { expect(screen.getByText('选择所在省份')).toBeInTheDocument(); }); // 组件应该能正常渲染,初始值会在组件内部处理 expect(screen.getByText('省份')).toBeInTheDocument(); expect(screen.getByText('城市')).toBeInTheDocument(); expect(screen.getByText('区县')).toBeInTheDocument(); }); it('当选择新省份时应该清空城市和区县选择', async () => { const user = userEvent.setup(); const handleChange = vi.fn(); render( ); // 先选择省份(北京市) const provinceSelect = screen.getByTestId('area-select-province'); // 等待省份数据加载并启用 await waitFor(() => { expect(provinceSelect).toBeInTheDocument(); expect(provinceSelect).not.toHaveAttribute('disabled'); }); await user.click(provinceSelect); // 选择北京市 - 尝试直接设置隐藏的select元素的值 // 查找隐藏的select元素 const provinceHiddenSelect = screen.getByTestId('area-select-province').parentElement?.querySelector('select[aria-hidden="true"]'); if (provinceHiddenSelect) { await user.selectOptions(provinceHiddenSelect, '1'); } // 等待城市数据加载并启用 const citySelect = screen.getByTestId('area-select-city'); await waitFor(() => { expect(citySelect).toBeInTheDocument(); expect(citySelect).not.toHaveAttribute('disabled'); }, { timeout: 2000 }); await user.click(citySelect); // 选择北京市(城市)- 尝试直接设置隐藏的select元素的值 // 查找隐藏的select元素 const cityHiddenSelect = screen.getByTestId('area-select-city').parentElement?.querySelector('select[aria-hidden="true"]'); if (cityHiddenSelect) { await user.selectOptions(cityHiddenSelect, '3'); } // 等待区县数据加载并启用 const districtSelect = screen.getByTestId('area-select-district'); await waitFor(() => { expect(districtSelect).toBeInTheDocument(); expect(districtSelect).not.toHaveAttribute('disabled'); }, { timeout: 2000 }); await user.click(districtSelect); // 选择东城区 - 尝试直接设置隐藏的select元素的值 // 查找隐藏的select元素 const districtHiddenSelect = screen.getByTestId('area-select-district').parentElement?.querySelector('select[aria-hidden="true"]'); if (districtHiddenSelect) { await user.selectOptions(districtHiddenSelect, '5'); } // 验证已经选择了完整的省市区 expect(handleChange).toHaveBeenLastCalledWith({ provinceId: 1, cityId: 3, districtId: 5 }); // 现在选择新的省份(上海市) await user.click(screen.getByTestId('area-select-province')); // 重新打开省份选择 await waitFor(() => { expect(screen.getByText('上海市')).toBeInTheDocument(); }); await user.click(screen.getByText('上海市')); // 验证onChange被调用,城市和区县被清空 expect(handleChange).toHaveBeenLastCalledWith({ provinceId: 2, cityId: undefined, districtId: undefined }); // 验证城市选择框应该显示"选择城市"(被清空) expect(screen.getByTestId('area-select-city')).toBeInTheDocument(); // 验证区县选择框应该显示"选择区县"(被清空) expect(screen.getByTestId('area-select-district')).toBeInTheDocument(); }); });