import React from 'react' import { renderHook, act, waitFor } from '@/__tests__/utils/test-helpers' import { AuthProvider, AuthContext } from './auth-provider' import { useAuth } from '@/hooks/use-auth' import { apiClient } from '@/lib/api-client' import { mockLoginResponse } from '@/__tests__/fixtures/mock-data' jest.mock('@/lib/api-client') jest.mock('next/navigation', () => ({ useRouter: () => ({ push: jest.fn(), pathname: '/', }), })) describe('AuthProvider', () => { beforeEach(() => { jest.clearAllMocks() localStorage.clear() }) describe('initialization', () => { it('should initialize with no user when no token exists', async () => { const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) expect(result.current.user).toBeNull() expect(result.current.isAuthenticated).toBe(false) }) it('should load user from localStorage if token exists', async () => { const token = 'test-token' localStorage.setItem('auth_token', token) const mockUser = { id: 1, name: 'Test', email: 'test@example.com', created_at: '', updated_at: '' } ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: mockUser, }) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) expect(result.current.token).toBe(token) expect(result.current.user).toEqual(mockUser) }) it('should clear invalid token and set loading to false', async () => { localStorage.setItem('auth_token', 'invalid-token') ;(apiClient.get as jest.Mock).mockRejectedValueOnce( new Error('Unauthorized') ) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) expect(localStorage.getItem('auth_token')).toBeNull() expect(result.current.token).toBeNull() }) }) describe('login', () => { it('should successfully login user', async () => { const credentials = { email: 'test@example.com', password: 'password123' } ;(apiClient.post as jest.Mock).mockResolvedValueOnce(mockLoginResponse) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) await act(async () => { await result.current.login(credentials) }) expect(result.current.token).toBe(mockLoginResponse.data.token) expect(result.current.user).toEqual(mockLoginResponse.data.user) expect(result.current.isAuthenticated).toBe(true) expect(localStorage.getItem('auth_token')).toBe( mockLoginResponse.data.token ) }) it('should throw error on login failure', async () => { const credentials = { email: 'test@example.com', password: 'wrong' } const errorMessage = 'Invalid credentials' ;(apiClient.post as jest.Mock).mockRejectedValueOnce( new Error(errorMessage) ) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) await expect( act(async () => { await result.current.login(credentials) }) ).rejects.toThrow(errorMessage) expect(result.current.isAuthenticated).toBe(false) }) it('should handle failed login response', async () => { const credentials = { email: 'test@example.com', password: 'password' } ;(apiClient.post as jest.Mock).mockResolvedValueOnce({ success: false, message: 'Login failed', }) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) await expect( act(async () => { await result.current.login(credentials) }) ).rejects.toThrow('Login failed') }) }) describe('register', () => { it('should successfully register user', async () => { const data = { name: 'New User', email: 'newuser@example.com', password: 'password123', password_confirmation: 'password123', } ;(apiClient.post as jest.Mock).mockResolvedValueOnce(mockLoginResponse) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) await act(async () => { await result.current.register(data) }) expect(result.current.token).toBe(mockLoginResponse.data.token) expect(result.current.user).toEqual(mockLoginResponse.data.user) expect(result.current.isAuthenticated).toBe(true) }) it('should throw error on registration failure', async () => { const data = { name: 'New User', email: 'newuser@example.com', password: 'password123', password_confirmation: 'password123', } const errorMessage = 'Email already exists' ;(apiClient.post as jest.Mock).mockRejectedValueOnce( new Error(errorMessage) ) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) await expect( act(async () => { await result.current.register(data) }) ).rejects.toThrow(errorMessage) expect(result.current.isAuthenticated).toBe(false) }) }) describe('logout', () => { it('should clear user and token on logout', async () => { const token = 'test-token' localStorage.setItem('auth_token', token) const mockUser = { id: 1, name: 'Test', email: 'test@example.com', created_at: '', updated_at: '' } ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: mockUser, }) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) act(() => { result.current.logout() }) expect(result.current.token).toBeNull() expect(result.current.user).toBeNull() expect(result.current.isAuthenticated).toBe(false) expect(localStorage.getItem('auth_token')).toBeNull() }) }) describe('authentication state', () => { it('should correctly report isAuthenticated status', async () => { // Set token BEFORE rendering hook so AuthProvider sees it on mount localStorage.setItem('auth_token', 'test-token') const mockUser = { id: 1, name: 'Test', email: 'test@example.com', created_at: '', updated_at: '' } ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: mockUser, }) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) const { result } = renderHook(() => useAuth(), { wrapper }) // Should be loading initially expect(result.current.isLoading).toBe(true) // After loading completes await waitFor(() => { expect(result.current.isLoading).toBe(false) }) // Token and authentication should be set expect(result.current.token).toBe('test-token') expect(result.current.isAuthenticated).toBe(true) }) }) })