import React from 'react' import { render, screen, fireEvent, waitFor } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import SearchResultPage from '@/pages/search-result/index' // 导入Taro mock函数 import { mockNavigateTo, mockGetCurrentInstance, mockStopPullDownRefresh } from '~/__mocks__/taroMock' // Mock components jest.mock('@/components/ui/navbar', () => ({ Navbar: ({ title, onClickLeft }: { title: string; onClickLeft: () => void }) => (
{title}
), })) jest.mock('@/components/goods-list', () => ({ __esModule: true, default: ({ goodsList, onClick, onAddCart }: { goodsList: any[], onClick: (goods: any) => void, onAddCart: (goods: any) => void }) => (
{goodsList.map((goods, index) => (
onClick(goods)} > {goods.name} {goods.price}
))}
), })) // Mock API client jest.mock('@/api', () => ({ goodsClient: { $get: jest.fn() } })) // Mock cart hook jest.mock('@/utils/cart', () => ({ useCart: () => ({ addToCart: jest.fn() }) })) describe('SearchResultPage', () => { let queryClient: QueryClient beforeEach(() => { queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }) // Reset all mocks jest.clearAllMocks() // Mock Taro.getCurrentInstance mockGetCurrentInstance.mockReturnValue({ router: { params: { keyword: '手机' } } }) // Mock API response const { goodsClient } = require('@/api') goodsClient.$get.mockResolvedValue({ status: 200, json: () => Promise.resolve({ data: [ { id: 1, name: 'iPhone 15', price: 599900, originPrice: 699900, stock: 10, salesNum: 100, imageFile: { fullUrl: 'https://example.com/iphone15.jpg' } }, { id: 2, name: 'MacBook Pro', price: 1299900, originPrice: 1499900, stock: 5, salesNum: 50, imageFile: { fullUrl: 'https://example.com/macbook.jpg' } } ], pagination: { current: 1, pageSize: 10, total: 2, totalPages: 1 } }) }) }) const renderWithProviders = (component: React.ReactElement) => { return render( {component} ) } it('渲染页面标题和布局', async () => { renderWithProviders() await waitFor(() => { expect(screen.getByTestId('navbar')).toBeInTheDocument() expect(screen.getByTestId('navbar')).toHaveTextContent('搜索结果') }) }) it('显示搜索栏和关键词', async () => { renderWithProviders() await waitFor(() => { // 验证搜索栏存在 const searchInput = document.querySelector('.search-input') as HTMLInputElement expect(searchInput).toBeInTheDocument() expect(searchInput.placeholder).toBe('搜索商品...') expect(searchInput.value).toBe('手机') // 验证搜索结果标题 expect(screen.getByText('搜索结果:"手机"')).toBeInTheDocument() expect(screen.getByText('共找到 2 件商品')).toBeInTheDocument() }) }) it('显示搜索结果列表', async () => { renderWithProviders() await waitFor(() => { expect(screen.getByTestId('goods-list')).toBeInTheDocument() const goodsItems = screen.getAllByTestId(/goods-item-\d+/) expect(goodsItems).toHaveLength(2) expect(screen.getByText('iPhone 15')).toBeInTheDocument() expect(screen.getByText('MacBook Pro')).toBeInTheDocument() }) }) it('处理搜索提交', async () => { renderWithProviders() await waitFor(() => { const searchInput = document.querySelector('.search-input') as HTMLInputElement // 修改搜索关键词 fireEvent.change(searchInput, { target: { value: 'iPad' } }) // 提交搜索 fireEvent.keyPress(searchInput, { key: 'Enter', code: 'Enter' }) }) // 验证API被重新调用 await waitFor(() => { const { goodsClient } = require('@/api') // 由于React Query的异步特性,这里可能被调用多次,我们检查最后一次调用 const lastCall = goodsClient.$get.mock.calls[goodsClient.$get.mock.calls.length - 1] expect(lastCall[0]).toEqual({ query: { page: 1, pageSize: 10, keyword: 'iPad', filters: JSON.stringify({ state: 1 }) } }) }) }) it('显示空状态', async () => { // Mock empty response const { goodsClient } = require('@/api') goodsClient.$get.mockResolvedValue({ status: 200, json: () => Promise.resolve({ data: [], pagination: { current: 1, pageSize: 10, total: 0, totalPages: 0 } }) }) renderWithProviders() await waitFor(() => { expect(screen.getByText('暂无相关商品')).toBeInTheDocument() expect(screen.getByText('换个关键词试试吧')).toBeInTheDocument() }) }) it('处理商品点击', async () => { renderWithProviders() await waitFor(() => { const firstGoodsItem = screen.getByTestId('goods-item-0') fireEvent.click(firstGoodsItem) }) // 验证跳转到商品详情页面 expect(mockNavigateTo).toHaveBeenCalledWith({ url: '/pages/goods-detail/index?id=1' }) }) it('处理添加到购物车', async () => { renderWithProviders() await waitFor(() => { const addCartButtons = screen.getAllByTestId('add-cart-btn') fireEvent.click(addCartButtons[0]) }) // 验证购物车功能被调用 const { useCart } = require('@/utils/cart') const { addToCart } = useCart() expect(addToCart).toHaveBeenCalledWith({ id: 1, name: 'iPhone 15', price: 599900, image: 'https://example.com/iphone15.jpg', stock: 10, quantity: 1 }) }) it('处理下拉刷新', async () => { renderWithProviders() await waitFor(() => { // 模拟下拉刷新 - 直接调用onRefresherRefresh const scrollView = document.querySelector('.search-result-content') if (scrollView) { // 触发下拉刷新事件 const event = new Event('refresherrefresh') scrollView.dispatchEvent(event) } }) // 验证API被重新调用 await waitFor(() => { const { goodsClient } = require('@/api') expect(goodsClient.$get).toHaveBeenCalled() }) }) it('处理清除搜索输入', async () => { renderWithProviders() await waitFor(() => { const searchInput = document.querySelector('.search-input') as HTMLInputElement // 输入内容 fireEvent.change(searchInput, { target: { value: '测试商品' } }) // 验证清除按钮出现 const clearIcon = document.querySelector('.clear-icon') expect(clearIcon).toBeInTheDocument() // 点击清除按钮 fireEvent.click(clearIcon!) // 验证搜索输入被清空 expect(searchInput.value).toBe('') }) }) it('验证样式类名应用', async () => { renderWithProviders() await waitFor(() => { const container = document.querySelector('.search-result-page') expect(container).toBeInTheDocument() const content = document.querySelector('.search-result-content') expect(content).toBeInTheDocument() const searchBar = document.querySelector('.search-bar-container') expect(searchBar).toBeInTheDocument() const resultContainer = document.querySelector('.result-container') expect(resultContainer).toBeInTheDocument() const goodsListContainer = document.querySelector('.goods-list-container') expect(goodsListContainer).toBeInTheDocument() }) }) it('显示加载状态', async () => { // Mock loading state const { goodsClient } = require('@/api') goodsClient.$get.mockImplementation(() => new Promise(() => {})) // Never resolves renderWithProviders() await waitFor(() => { expect(screen.getByText('搜索中...')).toBeInTheDocument() }) }) })