| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- 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 }) => (
- <div data-testid="navbar">
- <span>{title}</span>
- <button onClick={onClickLeft}>返回</button>
- </div>
- ),
- }))
- jest.mock('@/components/goods-list', () => ({
- __esModule: true,
- default: ({
- goodsList,
- onClick,
- onAddCart
- }: {
- goodsList: any[],
- onClick: (goods: any) => void,
- onAddCart: (goods: any) => void
- }) => (
- <div data-testid="goods-list">
- {goodsList.map((goods, index) => (
- <div
- key={goods.id}
- data-testid={`goods-item-${index}`}
- onClick={() => onClick(goods)}
- >
- <span data-testid="goods-name">{goods.name}</span>
- <span data-testid="goods-price">{goods.price}</span>
- <button
- data-testid="add-cart-btn"
- onClick={() => onAddCart(goods)}
- >
- 加入购物车
- </button>
- </div>
- ))}
- </div>
- ),
- }))
- // 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(
- <QueryClientProvider client={queryClient}>
- {component}
- </QueryClientProvider>
- )
- }
- it('渲染页面标题和布局', async () => {
- renderWithProviders(<SearchResultPage />)
- await waitFor(() => {
- expect(screen.getByTestId('navbar')).toBeInTheDocument()
- expect(screen.getByTestId('navbar')).toHaveTextContent('搜索结果')
- })
- })
- it('显示搜索栏和关键词', async () => {
- renderWithProviders(<SearchResultPage />)
- 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(<SearchResultPage />)
- 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(<SearchResultPage />)
- 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(<SearchResultPage />)
- await waitFor(() => {
- expect(screen.getByText('暂无相关商品')).toBeInTheDocument()
- expect(screen.getByText('换个关键词试试吧')).toBeInTheDocument()
- })
- })
- it('处理商品点击', async () => {
- renderWithProviders(<SearchResultPage />)
- 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(<SearchResultPage />)
- 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(<SearchResultPage />)
- 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(<SearchResultPage />)
- 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(<SearchResultPage />)
- 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(<SearchResultPage />)
- await waitFor(() => {
- expect(screen.getByText('搜索中...')).toBeInTheDocument()
- })
- })
- })
|