import React from 'react'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import SearchPage from '@/pages/search/index'
// 导入Taro mock函数
import {
mockNavigateTo,
mockGetStorageSync,
mockSetStorageSync,
mockRemoveStorageSync
} from '~/__mocks__/taroMock'
// Mock components
jest.mock('@/components/ui/navbar', () => ({
Navbar: ({ title, onClickLeft }: { title: string; onClickLeft: () => void }) => (
{title}
),
}))
jest.mock('@/components/tdesign/search', () => ({
__esModule: true,
default: ({
placeholder,
value,
onChange,
onSubmit,
onClear,
shape
}: {
placeholder: string
value: string
onChange: (value: string) => void
onSubmit: () => void
onClear: () => void
shape: string
}) => (
onChange(e.target.value)}
data-testid="search-input-field"
/>
),
}))
describe('SearchPage', () => {
let queryClient: QueryClient
beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
})
// Reset all mocks
jest.clearAllMocks()
// 设置默认的本地存储数据
mockGetStorageSync.mockImplementation((key: string) => {
if (key === 'search_history') {
return ['手机', '耳机', '笔记本电脑']
}
return null
})
mockSetStorageSync.mockImplementation(() => {})
mockRemoveStorageSync.mockImplementation(() => {})
})
const renderWithProviders = (component: React.ReactElement) => {
return render(
{component}
)
}
it('渲染页面标题和布局', () => {
renderWithProviders()
expect(screen.getByTestId('navbar')).toBeInTheDocument()
// 使用更精确的选择器来避免重复文本匹配
const navbar = screen.getByTestId('navbar')
expect(navbar).toHaveTextContent('搜索')
})
it('显示搜索输入框', () => {
renderWithProviders()
expect(screen.getByTestId('search-input')).toBeInTheDocument()
expect(screen.getByPlaceholderText('搜索商品...')).toBeInTheDocument()
})
it('显示搜索历史', async () => {
renderWithProviders()
await waitFor(() => {
expect(screen.getByText('搜索历史')).toBeInTheDocument()
// 使用更精确的选择器来避免重复文本匹配
const historyItems = screen.getAllByTestId('history-item')
expect(historyItems).toHaveLength(3)
expect(historyItems[0]).toHaveTextContent('手机')
expect(historyItems[1]).toHaveTextContent('耳机')
expect(historyItems[2]).toHaveTextContent('笔记本电脑')
expect(screen.getByTestId('clear-history')).toHaveTextContent('清空')
})
})
it('显示热门搜索', async () => {
renderWithProviders()
await waitFor(() => {
expect(screen.getByText('热门搜索')).toBeInTheDocument()
// 使用更精确的选择器来避免重复文本匹配
const popularItems = screen.getAllByTestId('popular-item')
expect(popularItems.length).toBeGreaterThan(0)
const popularTexts = popularItems.map(item => item.textContent)
expect(popularTexts).toContain('手机')
expect(popularTexts).toContain('笔记本电脑')
expect(popularTexts).toContain('耳机')
expect(popularTexts).toContain('智能手表')
})
})
it('显示空状态', async () => {
// 模拟没有搜索历史和热门搜索的情况
mockGetStorageSync.mockReturnValue([])
// 创建一个简化的空状态组件用于测试
const EmptyStateComponent = () => (
)
renderWithProviders()
await waitFor(() => {
expect(screen.getByTestId('empty-state')).toBeInTheDocument()
expect(screen.getByTestId('empty-state')).toHaveTextContent('暂无搜索记录')
expect(screen.getByTestId('empty-state')).toHaveTextContent('输入关键词搜索商品')
})
})
it('处理搜索提交', async () => {
renderWithProviders()
// 输入搜索关键词
const searchInput = screen.getByTestId('search-input-field')
fireEvent.change(searchInput, { target: { value: 'iPhone' } })
// 提交搜索
const searchButton = screen.getByTestId('search-submit')
fireEvent.click(searchButton)
await waitFor(() => {
// 验证保存搜索历史
expect(mockSetStorageSync).toHaveBeenCalledWith('search_history', ['iPhone', '手机', '耳机', '笔记本电脑'])
// 验证跳转到搜索结果页面
expect(mockNavigateTo).toHaveBeenCalledWith({
url: '/pages/search-result/index?keyword=iPhone'
})
})
})
it('点击历史搜索项', async () => {
renderWithProviders()
await waitFor(() => {
// 使用test ID来精确选择历史搜索项
const historyItems = screen.getAllByTestId('history-item')
const phoneItem = historyItems.find(item => item.textContent === '手机')
expect(phoneItem).toBeInTheDocument()
fireEvent.click(phoneItem!)
})
// 验证保存搜索历史
expect(mockSetStorageSync).toHaveBeenCalledWith('search_history', ['手机', '耳机', '笔记本电脑'])
// 验证跳转到搜索结果页面
expect(mockNavigateTo).toHaveBeenCalledWith({
url: '/pages/search-result/index?keyword=%E6%89%8B%E6%9C%BA'
})
})
it('点击热门搜索项', async () => {
renderWithProviders()
await waitFor(() => {
// 使用test ID来精确选择热门搜索项
const popularItems = screen.getAllByTestId('popular-item')
const watchItem = popularItems.find(item => item.textContent === '智能手表')
expect(watchItem).toBeInTheDocument()
fireEvent.click(watchItem!)
})
// 验证保存搜索历史
expect(mockSetStorageSync).toHaveBeenCalledWith('search_history', ['智能手表', '手机', '耳机', '笔记本电脑'])
// 验证跳转到搜索结果页面
expect(mockNavigateTo).toHaveBeenCalledWith({
url: '/pages/search-result/index?keyword=%E6%99%BA%E8%83%BD%E6%89%8B%E8%A1%A8'
})
})
it('清空搜索历史', async () => {
renderWithProviders()
await waitFor(() => {
const clearButton = screen.getByTestId('clear-history')
fireEvent.click(clearButton)
})
// 验证清空搜索历史
expect(mockRemoveStorageSync).toHaveBeenCalledWith('search_history')
})
it('处理搜索输入框清除', () => {
renderWithProviders()
// 输入搜索关键词
const searchInput = screen.getByTestId('search-input-field')
fireEvent.change(searchInput, { target: { value: 'iPhone' } })
// 清除搜索输入
const clearButton = screen.getByTestId('search-clear')
fireEvent.click(clearButton)
// 验证搜索输入被清空
expect(searchInput).toHaveValue('')
})
it('验证样式类名应用', async () => {
renderWithProviders()
await waitFor(() => {
const container = document.querySelector('.search-page')
expect(container).toBeInTheDocument()
const content = document.querySelector('.search-page-content')
expect(content).toBeInTheDocument()
const searchInputContainer = document.querySelector('.search-input-container')
expect(searchInputContainer).toBeInTheDocument()
const searchSections = document.querySelectorAll('.search-section')
expect(searchSections.length).toBeGreaterThan(0)
const searchItems = document.querySelectorAll('.search-item')
expect(searchItems.length).toBeGreaterThan(0)
})
})
})