import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { LoginForm } from '../login-form'; import { useAuth } from '@/lib/auth-context'; import { useRouter } from 'next/navigation'; // Mock the auth context jest.mock('@/lib/auth-context', () => ({ useAuth: jest.fn(), })); // Mock next/navigation jest.mock('next/navigation', () => ({ useRouter: jest.fn(), })); describe('LoginForm', () => { const mockLogin = jest.fn(); const mockPush = jest.fn(); beforeEach(() => { jest.clearAllMocks(); (useAuth as jest.Mock).mockReturnValue({ login: mockLogin, error: null, }); (useRouter as jest.Mock).mockReturnValue({ push: mockPush, }); }); // Positive Tests describe('Positive Tests', () => { it('renders login form with all fields', () => { render(); expect(screen.getByLabelText(/email/i)).toBeInTheDocument(); expect(screen.getByLabelText(/password/i)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /login/i })).toBeInTheDocument(); }); it('submits form with valid credentials', async () => { mockLogin.mockResolvedValueOnce(undefined); render(); fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'user@example.com' }, }); fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password123' }, }); fireEvent.click(screen.getByRole('button', { name: /login/i })); await waitFor(() => { expect(mockLogin).toHaveBeenCalledWith('user@example.com', 'password123'); }); await waitFor(() => { expect(mockPush).toHaveBeenCalledWith('/dashboard'); }); }); it('shows loading state while submitting', async () => { mockLogin.mockImplementation(() => new Promise(() => {})); // Never resolves render(); fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'user@example.com' }, }); fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password123' }, }); fireEvent.click(screen.getByRole('button', { name: /login/i })); expect(screen.getByText(/logging in/i)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /logging in/i })).toBeDisabled(); }); it('displays error message from auth context', () => { (useAuth as jest.Mock).mockReturnValue({ login: mockLogin, error: 'Invalid credentials', }); render(); expect(screen.getByText(/invalid credentials/i)).toBeInTheDocument(); }); }); // Negative Tests describe('Negative Tests', () => { it('prevents submission when email is empty', () => { render(); fireEvent.change(screen.getByLabelText(/email/i), { target: { value: '' }, }); // Form element exists with proper structure expect(screen.getByLabelText(/email/i)).toBeInTheDocument(); }); it('prevents submission when password is empty', () => { render(); fireEvent.change(screen.getByLabelText(/password/i), { target: { value: '' }, }); // Form element exists with proper structure expect(screen.getByLabelText(/password/i)).toBeInTheDocument(); }); it('enforces minimum password length of 8 characters', () => { render(); const passwordInput = screen.getByLabelText(/password/i); expect(passwordInput).toHaveAttribute('minLength', '8'); }); it('handles login failure gracefully', async () => { mockLogin.mockRejectedValueOnce(new Error('Login failed')); render(); fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'user@example.com' }, }); fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password123' }, }); fireEvent.click(screen.getByRole('button', { name: /login/i })); await waitFor(() => { expect(mockPush).not.toHaveBeenCalled(); }); }); }); });