import React from 'react'
import { render, fireEvent, waitFor } from '@testing-library/react'
import { LocationSearch } from '../../src/components/LocationSearch'
// Mock API调用
jest.mock('../../src/api', () => ({
locationClient: {
$get: jest.fn().mockImplementation(({ query }) => {
if (query.keyword === '北京') {
return Promise.resolve({
status: 200,
json: jest.fn().mockResolvedValue([
{
id: 1,
name: '北京首都国际机场',
province: '北京市',
city: '北京市',
district: '朝阳区',
address: '北京市朝阳区首都机场路'
},
{
id: 2,
name: '北京南站',
province: '北京市',
city: '北京市',
district: '丰台区',
address: '北京市丰台区永外大街12号'
}
])
})
}
if (query.keyword === '上海') {
return Promise.resolve({
status: 200,
json: jest.fn().mockResolvedValue([
{
id: 3,
name: '上海虹桥机场',
province: '上海市',
city: '上海市',
district: '长宁区',
address: '上海市长宁区虹桥路2550号'
}
])
})
}
return Promise.resolve({
status: 200,
json: jest.fn().mockResolvedValue([])
})
})
}
}))
describe('LocationSearch', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('应该正确渲染初始状态', () => {
const { getByPlaceholderText } = render()
expect(getByPlaceholderText('搜索地点')).toBeTruthy()
})
it('应该显示自定义占位符', () => {
const { getByPlaceholderText } = render(
)
expect(getByPlaceholderText('请输入地点名称')).toBeTruthy()
})
it('应该处理输入变化并显示搜索结果', async () => {
const { getByPlaceholderText, getByText } = render()
const input = getByPlaceholderText('搜索地点')
fireEvent.input(input, { target: { value: '北京' } })
await waitFor(() => {
expect(getByText('北京首都国际机场')).toBeTruthy()
expect(getByText('北京南站')).toBeTruthy()
})
})
it('应该正确选择地点并触发onChange', async () => {
const mockOnChange = jest.fn()
const { getByPlaceholderText, getByText } = render(
)
const input = getByPlaceholderText('搜索地点')
fireEvent.input(input, { target: { value: '北京' } })
await waitFor(() => {
expect(getByText('北京首都国际机场')).toBeTruthy()
})
const locationItem = getByText('北京首都国际机场')
fireEvent.click(locationItem)
expect(mockOnChange).toHaveBeenCalledWith({
id: 1,
name: '北京首都国际机场',
province: '北京市',
city: '北京市',
district: '朝阳区',
address: '北京市朝阳区首都机场路'
})
})
it('应该支持地区筛选', async () => {
const { getByPlaceholderText } = render(
)
const input = getByPlaceholderText('搜索地点')
fireEvent.input(input, { target: { value: '北京' } })
await waitFor(() => {
// 验证API调用时传递了地区筛选参数
const locationClient = require('../../src/api').locationClient
expect(locationClient.$get).toHaveBeenCalledWith({
query: {
keyword: '北京',
provinceId: 1,
cityId: 11,
districtId: 111
}
})
})
})
it('应该处理清除操作', async () => {
const mockOnChange = jest.fn()
const { getByPlaceholderText, getByText } = render(
)
const input = getByPlaceholderText('搜索地点')
fireEvent.input(input, { target: { value: '北京' } })
await waitFor(() => {
expect(getByText('北京首都国际机场')).toBeTruthy()
})
// 选择地点
const locationItem = getByText('北京首都国际机场')
fireEvent.click(locationItem)
// 清除输入
const clearButton = getByText('×')
fireEvent.click(clearButton)
expect(mockOnChange).toHaveBeenCalledWith(null)
})
it('应该显示当前选择的地点', () => {
const selectedLocation = {
id: 1,
name: '北京首都国际机场',
province: '北京市',
city: '北京市',
district: '朝阳区',
address: '北京市朝阳区首都机场路'
}
const { getByText } = render(
)
expect(getByText('已选择: 北京首都国际机场 · 朝阳区 · 北京市 · 北京市')).toBeTruthy()
})
it('应该处理空搜索结果', async () => {
const { getByPlaceholderText, getByText } = render()
const input = getByPlaceholderText('搜索地点')
fireEvent.input(input, { target: { value: '不存在的城市' } })
await waitFor(() => {
expect(getByText('未找到相关地点')).toBeTruthy()
})
})
it('应该处理防抖搜索', async () => {
jest.useFakeTimers()
const { getByPlaceholderText } = render()
const input = getByPlaceholderText('搜索地点')
// 快速输入多个字符
fireEvent.input(input, { target: { value: '北' } })
fireEvent.input(input, { target: { value: '北京' } })
fireEvent.input(input, { target: { value: '北京市' } })
// 验证API只被调用一次(防抖)
const locationClient = require('../../src/api').locationClient
expect(locationClient.$get).not.toHaveBeenCalled()
// 快进防抖时间
jest.advanceTimersByTime(300)
await waitFor(() => {
expect(locationClient.$get).toHaveBeenCalledTimes(1)
expect(locationClient.$get).toHaveBeenCalledWith({
query: {
keyword: '北京市'
}
})
})
jest.useRealTimers()
})
})