import React from 'react'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { AreaCascader } from '../../src/components/AreaCascader'
// Mock API 客户端
const mockAreaClient = {
provinces: {
$get: jest.fn()
},
cities: {
$get: jest.fn()
},
districts: {
$get: jest.fn()
}
}
jest.mock('../../src/api', () => ({
areaClient: mockAreaClient
}))
// 创建测试用的 QueryClient
const createTestQueryClient = () => new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})
// 包装组件
const Wrapper = ({ children }: { children: React.ReactNode }) => {
const queryClient = createTestQueryClient()
return (
{children}
)
}
// 模拟数据
const mockProvinces = {
success: true,
data: {
provinces: [
{ id: 1, name: '北京市' },
{ id: 2, name: '上海市' },
{ id: 3, name: '广东省' }
]
},
message: ''
}
const mockCities = {
success: true,
data: {
cities: [
{ id: 11, name: '北京市' },
{ id: 12, name: '朝阳区' },
{ id: 13, name: '海淀区' }
]
},
message: ''
}
const mockDistricts = {
success: true,
data: {
districts: [
{ id: 101, name: '朝阳区' },
{ id: 102, name: '海淀区' },
{ id: 103, name: '西城区' }
]
},
message: ''
}
describe('AreaCascader 组件', () => {
beforeEach(() => {
// 重置所有 mock
jest.clearAllMocks()
// 设置默认的 mock 返回值
mockAreaClient.provinces.$get.mockResolvedValue({
status: 200,
json: async () => mockProvinces
})
mockAreaClient.cities.$get.mockResolvedValue({
status: 200,
json: async () => mockCities
})
mockAreaClient.districts.$get.mockResolvedValue({
status: 200,
json: async () => mockDistricts
})
})
test('应该正确渲染组件', async () => {
const onChange = jest.fn()
render(
)
// 检查选择器标签
expect(screen.getByText('省份')).toBeInTheDocument()
expect(screen.getByText('城市')).toBeInTheDocument()
expect(screen.getByText('区县')).toBeInTheDocument()
// 检查默认占位符
expect(screen.getByText('请选择省市区')).toBeInTheDocument()
// 等待数据加载
await waitFor(() => {
expect(mockAreaClient.provinces.$get).toHaveBeenCalled()
})
})
test('应该显示自定义占位符', () => {
const onChange = jest.fn()
render(
)
expect(screen.getByText('选择出发地')).toBeInTheDocument()
})
test('应该初始化选择值', async () => {
const onChange = jest.fn()
render(
)
// 等待数据加载
await waitFor(() => {
expect(mockAreaClient.provinces.$get).toHaveBeenCalled()
})
// 检查是否调用了城市和区县查询
await waitFor(() => {
expect(mockAreaClient.cities.$get).toHaveBeenCalledWith({
query: { provinceId: 1 }
})
})
await waitFor(() => {
expect(mockAreaClient.districts.$get).toHaveBeenCalledWith({
query: { cityId: 11 }
})
})
})
test('应该处理省份选择', async () => {
const onChange = jest.fn()
render(
)
// 等待数据加载
await waitFor(() => {
expect(mockAreaClient.provinces.$get).toHaveBeenCalled()
})
// 模拟选择省份
const provincePicker = screen.getByText('请选择省份')
fireEvent.click(provincePicker)
// 这里我们模拟 Picker 的 onChange 事件
// 在实际测试中,可能需要更复杂的模拟
})
test('应该正确显示已选择的省市区文本', async () => {
const onChange = jest.fn()
render(
)
// 等待数据加载
await waitFor(() => {
expect(mockAreaClient.provinces.$get).toHaveBeenCalled()
})
await waitFor(() => {
expect(mockAreaClient.cities.$get).toHaveBeenCalled()
})
await waitFor(() => {
expect(mockAreaClient.districts.$get).toHaveBeenCalled()
})
// 检查显示文本
await waitFor(() => {
expect(screen.getByText('北京市 北京市 朝阳区')).toBeInTheDocument()
})
})
test('应该处理部分选择的情况', async () => {
const onChange = jest.fn()
render(
)
// 等待数据加载
await waitFor(() => {
expect(mockAreaClient.provinces.$get).toHaveBeenCalled()
})
await waitFor(() => {
expect(mockAreaClient.cities.$get).toHaveBeenCalled()
})
// 检查显示文本
await waitFor(() => {
expect(screen.getByText('北京市 北京市')).toBeInTheDocument()
})
})
test('应该处理只有省份选择的情况', async () => {
const onChange = jest.fn()
render(
)
// 等待数据加载
await waitFor(() => {
expect(mockAreaClient.provinces.$get).toHaveBeenCalled()
})
// 检查显示文本
await waitFor(() => {
expect(screen.getByText('北京市')).toBeInTheDocument()
})
})
test('应该处理 API 错误', async () => {
// 模拟 API 错误
mockAreaClient.provinces.$get.mockResolvedValue({
status: 500,
json: async () => ({ success: false, message: '服务器错误' })
})
const onChange = jest.fn()
render(
)
// 组件应该正常渲染,即使 API 调用失败
expect(screen.getByText('省份')).toBeInTheDocument()
// 等待 API 调用
await waitFor(() => {
expect(mockAreaClient.provinces.$get).toHaveBeenCalled()
})
})
test('应该禁用城市选择器当没有选择省份时', async () => {
const onChange = jest.fn()
render(
)
// 等待数据加载
await waitFor(() => {
expect(mockAreaClient.provinces.$get).toHaveBeenCalled()
})
// 检查城市选择器是否被禁用
const cityPicker = screen.getByText('请选择城市')
expect(cityPicker.parentElement).toHaveClass('bg-gray-100')
expect(cityPicker.parentElement).toHaveClass('text-gray-400')
})
test('应该禁用区县选择器当没有选择城市时', async () => {
const onChange = jest.fn()
render(
)
// 等待数据加载
await waitFor(() => {
expect(mockAreaClient.provinces.$get).toHaveBeenCalled()
})
// 检查区县选择器是否被禁用
const districtPicker = screen.getByText('请选择区县')
expect(districtPicker.parentElement).toHaveClass('bg-gray-100')
expect(districtPicker.parentElement).toHaveClass('text-gray-400')
})
})