|
|
@@ -0,0 +1,239 @@
|
|
|
+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 }) => (
|
|
|
+ <div data-testid="navbar">
|
|
|
+ <span>{title}</span>
|
|
|
+ <button onClick={onClickLeft}>返回</button>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+}))
|
|
|
+
|
|
|
+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
|
|
|
+ }) => (
|
|
|
+ <div data-testid="search-input">
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ placeholder={placeholder}
|
|
|
+ value={value}
|
|
|
+ onChange={(e) => onChange(e.target.value)}
|
|
|
+ data-testid="search-input-field"
|
|
|
+ />
|
|
|
+ <button onClick={onSubmit} data-testid="search-submit">搜索</button>
|
|
|
+ <button onClick={onClear} data-testid="search-clear">清除</button>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+}))
|
|
|
+
|
|
|
+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(
|
|
|
+ <QueryClientProvider client={queryClient}>
|
|
|
+ {component}
|
|
|
+ </QueryClientProvider>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ it('渲染页面标题和布局', () => {
|
|
|
+ renderWithProviders(<SearchPage />)
|
|
|
+
|
|
|
+ expect(screen.getByTestId('navbar')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('搜索')).toBeInTheDocument()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('显示搜索输入框', () => {
|
|
|
+ renderWithProviders(<SearchPage />)
|
|
|
+
|
|
|
+ expect(screen.getByTestId('search-input')).toBeInTheDocument()
|
|
|
+ expect(screen.getByPlaceholderText('搜索商品...')).toBeInTheDocument()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('显示搜索历史', async () => {
|
|
|
+ renderWithProviders(<SearchPage />)
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('搜索历史')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('手机')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('耳机')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('笔记本电脑')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('清空')).toBeInTheDocument()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('显示热门搜索', async () => {
|
|
|
+ renderWithProviders(<SearchPage />)
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('热门搜索')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('手机')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('笔记本电脑')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('耳机')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('智能手表')).toBeInTheDocument()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('显示空状态', async () => {
|
|
|
+ // 模拟没有搜索历史和热门搜索的情况
|
|
|
+ mockGetStorageSync.mockReturnValue([])
|
|
|
+
|
|
|
+ renderWithProviders(<SearchPage />)
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByText('暂无搜索记录')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('输入关键词搜索商品')).toBeInTheDocument()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('处理搜索提交', async () => {
|
|
|
+ renderWithProviders(<SearchPage />)
|
|
|
+
|
|
|
+ // 输入搜索关键词
|
|
|
+ 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(<SearchPage />)
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ const historyItem = screen.getByText('手机')
|
|
|
+ fireEvent.click(historyItem)
|
|
|
+
|
|
|
+ // 验证保存搜索历史
|
|
|
+ expect(mockSetStorageSync).toHaveBeenCalledWith('search_history', ['手机', '耳机', '笔记本电脑'])
|
|
|
+ // 验证跳转到搜索结果页面
|
|
|
+ expect(mockNavigateTo).toHaveBeenCalledWith({
|
|
|
+ url: '/pages/search-result/index?keyword=手机'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('点击热门搜索项', async () => {
|
|
|
+ renderWithProviders(<SearchPage />)
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ const popularItem = screen.getByText('智能手表')
|
|
|
+ fireEvent.click(popularItem)
|
|
|
+
|
|
|
+ // 验证保存搜索历史
|
|
|
+ expect(mockSetStorageSync).toHaveBeenCalledWith('search_history', ['智能手表', '手机', '耳机', '笔记本电脑'])
|
|
|
+ // 验证跳转到搜索结果页面
|
|
|
+ expect(mockNavigateTo).toHaveBeenCalledWith({
|
|
|
+ url: '/pages/search-result/index?keyword=智能手表'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('清空搜索历史', async () => {
|
|
|
+ renderWithProviders(<SearchPage />)
|
|
|
+
|
|
|
+ await waitFor(() => {
|
|
|
+ const clearButton = screen.getByText('清空')
|
|
|
+ fireEvent.click(clearButton)
|
|
|
+
|
|
|
+ // 验证清空搜索历史
|
|
|
+ expect(mockRemoveStorageSync).toHaveBeenCalledWith('search_history')
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('处理搜索输入框清除', () => {
|
|
|
+ renderWithProviders(<SearchPage />)
|
|
|
+
|
|
|
+ // 输入搜索关键词
|
|
|
+ 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(<SearchPage />)
|
|
|
+
|
|
|
+ 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)
|
|
|
+ })
|
|
|
+ })
|
|
|
+})
|