import { render, act, waitFor, screen } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { BrowserRouter, MemoryRouter } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ProtectedRoute } from './ProtectedRoute'; // Mock useAuth hook const mockIsAuthenticated = vi.fn(() => false); const mockUser = vi.fn(() => null); vi.mock('@/features/auth/hooks/useAuth', () => ({ useAuth: () => ({ isAuthenticated: mockIsAuthenticated(), user: mockUser(), }), })); vi.mock('@/features/auth/store/authStore', () => ({ useAuthStore: () => ({ isAuthenticated: mockIsAuthenticated(), isLoading: false, }), })); vi.mock('@/services/tokenStorage', () => ({ TokenStorage: { getAccessToken: () => mockIsAuthenticated() ? 'mock-token' : null, }, })); const createTestQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); const TestWrapper = ({ children }: { children: React.ReactNode }) => { const queryClient = createTestQueryClient(); return ( {children} ); }; describe('ProtectedRoute Component', () => { beforeEach(() => { vi.clearAllMocks(); vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it('renders children when user is authenticated', async () => { mockIsAuthenticated.mockReturnValue(true); mockUser.mockReturnValue({ id: '1', name: 'Test' }); const { getByText } = render(
Protected Content
, ); // Wait for the checking timer (200ms) await act(async () => { vi.advanceTimersByTime(300); }); expect(getByText('Protected Content')).toBeInTheDocument(); }); it('redirects to login when user is not authenticated', () => { mockIsAuthenticated.mockReturnValue(false); mockUser.mockReturnValue(null); render(
Protected Content
, ); // Wait for the checking timer vi.advanceTimersByTime(300); // Le composant Navigate devrait rediriger vers /login // On vérifie que le contenu protégé n'est pas visible const protectedContent = document.querySelector('div'); expect(protectedContent?.textContent).not.toBe('Protected Content'); }); it('uses replace navigation when redirecting', () => { mockIsAuthenticated.mockReturnValue(false); mockUser.mockReturnValue(null); const { container } = render(
Protected Content
, ); vi.advanceTimersByTime(300); // Le composant Navigate devrait être présent avec replace expect(container).toBeInTheDocument(); }); it('renders multiple children correctly when authenticated', async () => { mockIsAuthenticated.mockReturnValue(true); mockUser.mockReturnValue({ id: '1', name: 'Test' }); render(
Child 1
Child 2
, ); await act(async () => { vi.advanceTimersByTime(300); }); // waitFor needs real timers to poll; switch to real timers then assert vi.useRealTimers(); await screen.findByText('Child 1', { timeout: 500 }); expect(screen.getByText('Child 2')).toBeInTheDocument(); vi.useFakeTimers(); }); it('handles empty children gracefully', () => { mockIsAuthenticated.mockReturnValue(true); mockUser.mockReturnValue({ id: '1', name: 'Test' }); const { container } = render( {null} , ); vi.advanceTimersByTime(300); expect(container).toBeInTheDocument(); }); });