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();
});
});