Files
maternal-app/maternal-web/__tests__/lib/auth/AuthContext.test.tsx
andupetcu b6ed413e0c Add Phase 4, 5 & 6: AI Assistant, Analytics & Testing
Phase 4: AI Assistant Integration
- AI chat interface with suggested questions
- Real-time messaging with backend OpenAI integration
- Material UI chat bubbles and animations
- Medical disclaimer and user-friendly UX

Phase 5: Pattern Recognition & Analytics
- Analytics dashboard with tabbed interface
- Weekly sleep chart with bar/line visualizations
- Feeding frequency graphs with type distribution
- Growth curve with WHO percentiles (0-24 months)
- Pattern insights with AI-powered recommendations
- PDF report export functionality
- Recharts integration for all data visualizations

Phase 6: Testing & Optimization
- Jest and React Testing Library setup
- Unit tests for auth, API client, and components
- Integration tests with full coverage
- WCAG AA accessibility compliance testing
- Performance optimizations (SWC, image optimization)
- Accessibility monitoring with axe-core
- 70% code coverage threshold

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-30 21:38:45 +03:00

318 lines
8.6 KiB
TypeScript

import { renderHook, waitFor, act } from '@testing-library/react';
import { AuthProvider, useAuth } from '@/lib/auth/AuthContext';
import apiClient from '@/lib/api/client';
import { useRouter } from 'next/navigation';
// Mock dependencies
jest.mock('@/lib/api/client');
jest.mock('next/navigation', () => ({
useRouter: jest.fn(),
}));
const mockApiClient = apiClient as jest.Mocked<typeof apiClient>;
const mockUseRouter = useRouter as jest.MockedFunction<typeof useRouter>;
describe('AuthContext', () => {
const mockRouter = {
push: jest.fn(),
replace: jest.fn(),
refresh: jest.fn(),
back: jest.fn(),
forward: jest.fn(),
prefetch: jest.fn(),
};
beforeEach(() => {
jest.clearAllMocks();
mockUseRouter.mockReturnValue(mockRouter as any);
localStorage.clear();
});
describe('login', () => {
it('should login successfully and store tokens', async () => {
const mockUser = {
id: 'usr_123',
email: 'test@example.com',
name: 'Test User',
role: 'parent',
};
const mockTokens = {
accessToken: 'access_token_123',
refreshToken: 'refresh_token_123',
};
mockApiClient.post.mockResolvedValueOnce({
data: {
success: true,
data: {
user: mockUser,
tokens: mockTokens,
},
},
});
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await act(async () => {
await result.current.login({
email: 'test@example.com',
password: 'password123',
});
});
await waitFor(() => {
expect(result.current.user).toEqual(mockUser);
expect(result.current.isAuthenticated).toBe(true);
expect(localStorage.setItem).toHaveBeenCalledWith('accessToken', mockTokens.accessToken);
expect(localStorage.setItem).toHaveBeenCalledWith('refreshToken', mockTokens.refreshToken);
expect(mockRouter.push).toHaveBeenCalledWith('/');
});
});
it('should handle login failure', async () => {
mockApiClient.post.mockRejectedValueOnce({
response: {
data: {
message: 'Invalid credentials',
},
},
});
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await expect(
result.current.login({
email: 'test@example.com',
password: 'wrong_password',
})
).rejects.toThrow('Invalid credentials');
expect(result.current.user).toBeNull();
expect(result.current.isAuthenticated).toBe(false);
});
});
describe('register', () => {
it('should register successfully and redirect to onboarding', async () => {
const mockUser = {
id: 'usr_123',
email: 'newuser@example.com',
name: 'New User',
role: 'parent',
};
const mockTokens = {
accessToken: 'access_token_123',
refreshToken: 'refresh_token_123',
};
mockApiClient.post.mockResolvedValueOnce({
data: {
success: true,
data: {
user: mockUser,
tokens: mockTokens,
family: { id: 'fam_123' },
},
},
});
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await act(async () => {
await result.current.register({
email: 'newuser@example.com',
password: 'password123',
name: 'New User',
});
});
await waitFor(() => {
expect(result.current.user).toEqual(mockUser);
expect(localStorage.setItem).toHaveBeenCalledWith('accessToken', mockTokens.accessToken);
expect(mockRouter.push).toHaveBeenCalledWith('/onboarding');
});
});
it('should handle registration failure with invalid tokens', async () => {
mockApiClient.post.mockResolvedValueOnce({
data: {
success: true,
data: {
user: {},
tokens: {}, // Invalid tokens
},
},
});
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await expect(
result.current.register({
email: 'newuser@example.com',
password: 'password123',
name: 'New User',
})
).rejects.toThrow('Invalid response from server');
});
});
describe('logout', () => {
it('should logout successfully and clear tokens', async () => {
mockApiClient.post.mockResolvedValueOnce({
data: { success: true },
});
localStorage.setItem('accessToken', 'token_123');
localStorage.setItem('refreshToken', 'refresh_123');
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await act(async () => {
await result.current.logout();
});
await waitFor(() => {
expect(localStorage.removeItem).toHaveBeenCalledWith('accessToken');
expect(localStorage.removeItem).toHaveBeenCalledWith('refreshToken');
expect(result.current.user).toBeNull();
expect(mockRouter.push).toHaveBeenCalledWith('/login');
});
});
it('should clear tokens even if API call fails', async () => {
mockApiClient.post.mockRejectedValueOnce(new Error('Network error'));
localStorage.setItem('accessToken', 'token_123');
localStorage.setItem('refreshToken', 'refresh_123');
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await act(async () => {
await result.current.logout();
});
await waitFor(() => {
expect(localStorage.removeItem).toHaveBeenCalledWith('accessToken');
expect(localStorage.removeItem).toHaveBeenCalledWith('refreshToken');
expect(mockRouter.push).toHaveBeenCalledWith('/login');
});
});
});
describe('refreshUser', () => {
it('should refresh user data successfully', async () => {
const mockUser = {
id: 'usr_123',
email: 'test@example.com',
name: 'Updated Name',
role: 'parent',
};
mockApiClient.get.mockResolvedValueOnce({
data: {
data: mockUser,
},
});
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await act(async () => {
await result.current.refreshUser();
});
await waitFor(() => {
expect(result.current.user).toEqual(mockUser);
});
});
it('should handle refresh failure gracefully', async () => {
mockApiClient.get.mockRejectedValueOnce(new Error('Unauthorized'));
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await act(async () => {
await result.current.refreshUser();
});
// User should remain null, no error thrown
expect(result.current.user).toBeNull();
});
});
describe('authentication check', () => {
it('should check auth on mount with valid token', async () => {
const mockUser = {
id: 'usr_123',
email: 'test@example.com',
name: 'Test User',
role: 'parent',
};
localStorage.setItem('accessToken', 'valid_token');
mockApiClient.get.mockResolvedValueOnce({
data: {
data: mockUser,
},
});
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.user).toEqual(mockUser);
expect(result.current.isAuthenticated).toBe(true);
});
});
it('should handle auth check failure', async () => {
localStorage.setItem('accessToken', 'invalid_token');
mockApiClient.get.mockRejectedValueOnce(new Error('Unauthorized'));
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.user).toBeNull();
expect(localStorage.removeItem).toHaveBeenCalledWith('accessToken');
expect(localStorage.removeItem).toHaveBeenCalledWith('refreshToken');
});
});
it('should not check auth if no token exists', async () => {
const { result } = renderHook(() => useAuth(), {
wrapper: AuthProvider,
});
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
expect(result.current.user).toBeNull();
expect(mockApiClient.get).not.toHaveBeenCalled();
});
});
});
});