import React from 'react' import { renderHook, waitFor } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { AuthProvider, useRequireAuth } from '../../src/hooks/useAuth' import Taro from '@tarojs/taro' // Mock Taro jest.mock('@tarojs/taro', () => ({ showToast: jest.fn(), redirectTo: jest.fn(), getStorageSync: jest.fn(() => null), setStorageSync: jest.fn(), removeStorageSync: jest.fn(), })) // Mock talentAuthClient jest.mock('../../src/api', () => ({ talentAuthClient: { me: { $get: jest.fn(), }, login: { $post: jest.fn(), }, }, })) const mockToast = Taro.showToast as jest.MockedFunction const mockRedirectTo = Taro.redirectTo as jest.MockedFunction describe('useRequireAuth', () => { let queryClient: QueryClient const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) beforeEach(() => { queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: Infinity, refetchOnWindowFocus: false, refetchOnReconnect: false, }, }, }) jest.clearAllMocks() jest.useFakeTimers() }) afterEach(() => { queryClient.clear() jest.useRealTimers() }) it('应该在未登录时显示提示并跳转到登录页', async () => { // 模拟无token的情况 ;(Taro.getStorageSync as jest.Mock).mockReturnValue(null) renderHook(() => useRequireAuth(), { wrapper }) // 等待useEffect执行 await waitFor(() => { expect(mockToast).toHaveBeenCalledWith({ title: '请先登录', icon: 'none', duration: 1500, }) }) // 快进1500ms jest.advanceTimersByTime(1500) // 等待redirectTo被调用 await waitFor(() => { expect(mockRedirectTo).toHaveBeenCalledWith({ url: '/pages/login/index', }) }) }) it('应该在loading状态时不跳转', async () => { // 模拟无token但loading中的情况 ;(Taro.getStorageSync as jest.Mock).mockReturnValue(null) const { result } = renderHook(() => useRequireAuth(), { wrapper }) // 在初始加载期间不应该立即调用redirectTo(需要等待loading结束) // 由于我们模拟的token为null,loading会很快结束 // 这里我们只验证在第一次render时不会调用 expect(mockRedirectTo).not.toHaveBeenCalled() }) it('应该在已登录时不跳转', async () => { // 模拟有token且已登录的情况 ;(Taro.getStorageSync as jest.Mock).mockReturnValue('valid_token') // 模拟API返回用户信息 const { talentAuthClient } = require('../../src/api') talentAuthClient.me.$get.mockResolvedValue({ status: 200, json: async () => ({ id: 1, name: '测试用户' }), }) renderHook(() => useRequireAuth(), { wrapper }) // 等待查询完成 await waitFor(() => { expect(talentAuthClient.me.$get).toHaveBeenCalled() }) // 已登录状态不应该调用redirectTo expect(mockRedirectTo).not.toHaveBeenCalled() }) it('应该在token失效时清除token并跳转', async () => { // 模拟有token但API返回错误的情况 ;(Taro.getStorageSync as jest.Mock).mockReturnValue('invalid_token') const { talentAuthClient } = require('../../src/api') talentAuthClient.me.$get.mockResolvedValue({ status: 401, }) renderHook(() => useRequireAuth(), { wrapper }) // 等待查询完成并验证token被清除 await waitFor(() => { expect(Taro.removeStorageSync).toHaveBeenCalledWith('talent_token') expect(Taro.removeStorageSync).toHaveBeenCalledWith('talent_user') }) // 快进1500ms jest.advanceTimersByTime(1500) // 验证跳转到登录页 await waitFor(() => { expect(mockRedirectTo).toHaveBeenCalledWith({ url: '/pages/login/index', }) }) }) })