/** * Tests for Password Reset Confirm Content Component * Verifies token validation and form rendering */ import { render, screen, act } from '@testing-library/react'; import { useSearchParams } from 'next/navigation'; import { useRouter } from '@/lib/i18n/routing'; import PasswordResetConfirmContent from '@/app/[locale]/(auth)/password-reset/confirm/PasswordResetConfirmContent'; // Mock Next.js navigation jest.mock('next/navigation', () => ({ useSearchParams: jest.fn(), default: jest.fn(), })); // Mock i18n routing jest.mock('@/lib/i18n/routing', () => ({ useRouter: jest.fn(), Link: ({ children, href }: { children: React.ReactNode; href: string }) => ( {children} ), })); // Mock dynamic import jest.mock('next/dynamic', () => ({ __esModule: true, default: (_importFn: () => Promise, _options?: any) => { const Component = ({ onSuccess }: { onSuccess?: () => void }) => (
); Component.displayName = 'PasswordResetConfirmForm'; return Component; }, })); // Mock Alert component jest.mock('@/components/ui/alert', () => ({ Alert: ({ children }: { children: React.ReactNode }) =>
{children}
, })); describe('PasswordResetConfirmContent', () => { let mockPush: jest.Mock; beforeEach(() => { jest.clearAllMocks(); jest.useFakeTimers(); mockPush = jest.fn(); (useRouter as jest.Mock).mockReturnValue({ push: mockPush, }); }); afterEach(() => { jest.runOnlyPendingTimers(); jest.useRealTimers(); }); describe('With valid token', () => { beforeEach(() => { (useSearchParams as jest.Mock).mockReturnValue({ get: jest.fn((key: string) => (key === 'token' ? 'valid-token-123' : null)), }); }); it('renders without crashing', () => { render(); expect(screen.getByText('Set new password')).toBeInTheDocument(); }); it('renders heading and description', () => { render(); expect(screen.getByRole('heading', { name: /set new password/i })).toBeInTheDocument(); expect(screen.getByText(/choose a strong password/i)).toBeInTheDocument(); }); it('renders PasswordResetConfirmForm with token', () => { render(); expect(screen.getByTestId('password-reset-confirm-form')).toBeInTheDocument(); }); it('redirects to login after successful password reset', () => { render(); const submitButton = screen.getByRole('button', { name: /submit/i }); // Trigger success handler act(() => { submitButton.click(); }); // Fast-forward time by 3 seconds act(() => { jest.advanceTimersByTime(3000); }); expect(mockPush).toHaveBeenCalledWith('/login'); }); it('cleans up timeout on unmount', () => { const { unmount } = render(); const submitButton = screen.getByRole('button', { name: /submit/i }); // Trigger success handler act(() => { submitButton.click(); }); // Unmount before timeout fires unmount(); // Fast-forward time act(() => { jest.advanceTimersByTime(3000); }); // Should not redirect because component was unmounted expect(mockPush).not.toHaveBeenCalled(); }); }); describe('Without token', () => { beforeEach(() => { (useSearchParams as jest.Mock).mockReturnValue({ get: jest.fn(() => null), }); }); it('shows invalid reset link error', () => { render(); expect(screen.getByRole('heading', { name: /invalid reset link/i })).toBeInTheDocument(); expect(screen.getByTestId('alert')).toBeInTheDocument(); }); it('shows error message', () => { render(); expect( screen.getByText(/this password reset link is invalid or has expired/i) ).toBeInTheDocument(); }); it('shows link to request new reset', () => { render(); const link = screen.getByRole('link', { name: /request new reset link/i }); expect(link).toBeInTheDocument(); expect(link).toHaveAttribute('href', '/password-reset'); }); it('does not render form when token is missing', () => { render(); expect(screen.queryByTestId('password-reset-confirm-form')).not.toBeInTheDocument(); }); }); });