useRequireAuth.test.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import React from 'react'
  2. import { renderHook, waitFor } from '@testing-library/react'
  3. import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
  4. import { AuthProvider, useRequireAuth } from '../../src/hooks/useAuth'
  5. import Taro from '@tarojs/taro'
  6. // Mock Taro
  7. jest.mock('@tarojs/taro', () => ({
  8. showToast: jest.fn(),
  9. redirectTo: jest.fn(),
  10. getStorageSync: jest.fn(() => null),
  11. setStorageSync: jest.fn(),
  12. removeStorageSync: jest.fn(),
  13. }))
  14. // Mock talentAuthClient
  15. jest.mock('../../src/api', () => ({
  16. talentAuthClient: {
  17. me: {
  18. $get: jest.fn(),
  19. },
  20. login: {
  21. $post: jest.fn(),
  22. },
  23. },
  24. }))
  25. const mockToast = Taro.showToast as jest.MockedFunction<typeof Taro.showToast>
  26. const mockRedirectTo = Taro.redirectTo as jest.MockedFunction<typeof Taro.redirectTo>
  27. describe('useRequireAuth', () => {
  28. let queryClient: QueryClient
  29. const wrapper = ({ children }: { children: React.ReactNode }) => (
  30. <QueryClientProvider client={queryClient}>
  31. <AuthProvider>{children}</AuthProvider>
  32. </QueryClientProvider>
  33. )
  34. beforeEach(() => {
  35. queryClient = new QueryClient({
  36. defaultOptions: {
  37. queries: {
  38. retry: false,
  39. staleTime: Infinity,
  40. refetchOnWindowFocus: false,
  41. refetchOnReconnect: false,
  42. },
  43. },
  44. })
  45. jest.clearAllMocks()
  46. jest.useFakeTimers()
  47. })
  48. afterEach(() => {
  49. queryClient.clear()
  50. jest.useRealTimers()
  51. })
  52. it('应该在未登录时显示提示并跳转到登录页', async () => {
  53. // 模拟无token的情况
  54. ;(Taro.getStorageSync as jest.Mock).mockReturnValue(null)
  55. renderHook(() => useRequireAuth(), { wrapper })
  56. // 等待useEffect执行
  57. await waitFor(() => {
  58. expect(mockToast).toHaveBeenCalledWith({
  59. title: '请先登录',
  60. icon: 'none',
  61. duration: 1500,
  62. })
  63. })
  64. // 快进1500ms
  65. jest.advanceTimersByTime(1500)
  66. // 等待redirectTo被调用
  67. await waitFor(() => {
  68. expect(mockRedirectTo).toHaveBeenCalledWith({
  69. url: '/pages/login/index',
  70. })
  71. })
  72. })
  73. it('应该在loading状态时不跳转', async () => {
  74. // 模拟无token但loading中的情况
  75. ;(Taro.getStorageSync as jest.Mock).mockReturnValue(null)
  76. const { result } = renderHook(() => useRequireAuth(), { wrapper })
  77. // 在初始加载期间不应该立即调用redirectTo(需要等待loading结束)
  78. // 由于我们模拟的token为null,loading会很快结束
  79. // 这里我们只验证在第一次render时不会调用
  80. expect(mockRedirectTo).not.toHaveBeenCalled()
  81. })
  82. it('应该在已登录时不跳转', async () => {
  83. // 模拟有token且已登录的情况
  84. ;(Taro.getStorageSync as jest.Mock).mockReturnValue('valid_token')
  85. // 模拟API返回用户信息
  86. const { talentAuthClient } = require('../../src/api')
  87. talentAuthClient.me.$get.mockResolvedValue({
  88. status: 200,
  89. json: async () => ({ id: 1, name: '测试用户' }),
  90. })
  91. renderHook(() => useRequireAuth(), { wrapper })
  92. // 等待查询完成
  93. await waitFor(() => {
  94. expect(talentAuthClient.me.$get).toHaveBeenCalled()
  95. })
  96. // 已登录状态不应该调用redirectTo
  97. expect(mockRedirectTo).not.toHaveBeenCalled()
  98. })
  99. it('应该在token失效时清除token并跳转', async () => {
  100. // 模拟有token但API返回错误的情况
  101. ;(Taro.getStorageSync as jest.Mock).mockReturnValue('invalid_token')
  102. const { talentAuthClient } = require('../../src/api')
  103. talentAuthClient.me.$get.mockResolvedValue({
  104. status: 401,
  105. })
  106. renderHook(() => useRequireAuth(), { wrapper })
  107. // 等待查询完成并验证token被清除
  108. await waitFor(() => {
  109. expect(Taro.removeStorageSync).toHaveBeenCalledWith('talent_token')
  110. expect(Taro.removeStorageSync).toHaveBeenCalledWith('talent_user')
  111. })
  112. // 快进1500ms
  113. jest.advanceTimersByTime(1500)
  114. // 验证跳转到登录页
  115. await waitFor(() => {
  116. expect(mockRedirectTo).toHaveBeenCalledWith({
  117. url: '/pages/login/index',
  118. })
  119. })
  120. })
  121. })