AuthProvider.test.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
  2. import { screen, act, fireEvent } from '@testing-library/react';
  3. import { AuthProvider, useAuth } from '../../src/hooks/AuthProvider';
  4. import React from 'react';
  5. import { renderWithProviders } from '../test-utils';
  6. // Mock axios
  7. vi.mock('axios', () => ({
  8. default: {
  9. defaults: {
  10. headers: {
  11. common: {},
  12. },
  13. },
  14. },
  15. }));
  16. // Mock auth client
  17. vi.mock('../../src/api/authClient', () => ({
  18. authClient: {
  19. login: {
  20. $post: vi.fn(),
  21. },
  22. logout: {
  23. $post: vi.fn(),
  24. },
  25. me: {
  26. $get: vi.fn(),
  27. },
  28. },
  29. }));
  30. // Mock localStorage
  31. const localStorageMock = {
  32. getItem: vi.fn(),
  33. setItem: vi.fn(),
  34. removeItem: vi.fn(),
  35. clear: vi.fn(),
  36. };
  37. Object.defineProperty(window, 'localStorage', {
  38. value: localStorageMock,
  39. });
  40. // Test component that uses useAuth
  41. const TestComponent = () => {
  42. const auth = useAuth();
  43. return (
  44. <div>
  45. <div data-testid="user">{auth.user ? 'authenticated' : 'unauthenticated'}</div>
  46. <div data-testid="isAuthenticated">{auth.isAuthenticated ? 'true' : 'false'}</div>
  47. <div data-testid="isLoading">{auth.isLoading ? 'true' : 'false'}</div>
  48. <button onClick={() => auth.login('test', 'password')}>Login</button>
  49. <button onClick={() => auth.logout()}>Logout</button>
  50. </div>
  51. );
  52. };
  53. describe('AuthProvider', () => {
  54. beforeEach(() => {
  55. vi.clearAllMocks();
  56. localStorageMock.getItem.mockReturnValue(null);
  57. });
  58. afterEach(() => {
  59. vi.restoreAllMocks();
  60. });
  61. it('应该提供认证上下文', () => {
  62. renderWithProviders(
  63. <AuthProvider>
  64. <TestComponent />
  65. </AuthProvider>
  66. );
  67. expect(screen.getByTestId('user')).toHaveTextContent('unauthenticated');
  68. expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
  69. });
  70. it('应该在没有AuthProvider时抛出错误', () => {
  71. // 抑制控制台错误输出
  72. const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
  73. expect(() => {
  74. renderWithProviders(<TestComponent />);
  75. }).toThrow('useAuth必须在AuthProvider内部使用');
  76. consoleError.mockRestore();
  77. });
  78. it('应该处理登录成功', async () => {
  79. const mockLoginResponse = {
  80. status: 200,
  81. json: vi.fn().mockResolvedValue({
  82. token: 'test-token',
  83. user: { id: 1, username: 'test', email: 'test@example.com' },
  84. }),
  85. };
  86. const { authClient } = await import('../../src/api/authClient');
  87. (authClient.login.$post as any).mockResolvedValue(mockLoginResponse);
  88. renderWithProviders(
  89. <AuthProvider>
  90. <TestComponent />
  91. </AuthProvider>
  92. );
  93. const loginButton = screen.getByText('Login');
  94. await act(async () => {
  95. fireEvent.click(loginButton);
  96. });
  97. expect(authClient.login.$post).toHaveBeenCalledWith({
  98. json: {
  99. username: 'test',
  100. password: 'password',
  101. },
  102. });
  103. });
  104. it('应该处理登录失败', async () => {
  105. const mockLoginResponse = {
  106. status: 401,
  107. json: vi.fn().mockResolvedValue({
  108. message: 'Invalid credentials',
  109. }),
  110. };
  111. const { authClient } = await import('../../src/api/authClient');
  112. (authClient.login.$post as any).mockResolvedValue(mockLoginResponse);
  113. renderWithProviders(
  114. <AuthProvider>
  115. <TestComponent />
  116. </AuthProvider>
  117. );
  118. const loginButton = screen.getByText('Login');
  119. await act(async () => {
  120. fireEvent.click(loginButton);
  121. });
  122. expect(authClient.login.$post).toHaveBeenCalledWith({
  123. json: {
  124. username: 'test',
  125. password: 'password',
  126. },
  127. });
  128. });
  129. it('应该处理登出', async () => {
  130. const { authClient } = await import('../../src/api/authClient');
  131. (authClient.logout.$post as any).mockResolvedValue({});
  132. localStorageMock.getItem.mockReturnValue('test-token');
  133. renderWithProviders(
  134. <AuthProvider>
  135. <TestComponent />
  136. </AuthProvider>
  137. );
  138. const logoutButton = screen.getByText('Logout');
  139. await act(async () => {
  140. fireEvent.click(logoutButton);
  141. });
  142. expect(authClient.logout.$post).toHaveBeenCalled();
  143. expect(localStorageMock.removeItem).toHaveBeenCalledWith('token');
  144. });
  145. });