import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { BrowserRouter } from 'react-router-dom'; import Register from '../Register'; import { AuthContext } from '../../context/AuthContext'; const mockedNavigate = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), Navigate: ({ to }) => { mockedNavigate(to); return null; }, })); describe('Register Component', () => { const mockRegister = jest.fn(); const mockAuthContext = { auth: { isAuthenticated: false, loading: false, user: null, }, register: mockRegister, }; const renderRegister = (contextValue = mockAuthContext) => { return render( ); }; beforeEach(() => { mockRegister.mockClear(); mockedNavigate.mockClear(); }); describe('Rendering', () => { it('should render registration form', () => { renderRegister(); expect(screen.getByRole('heading', { name: /register/i })).toBeInTheDocument(); expect(screen.getByPlaceholderText(/name/i)).toBeInTheDocument(); expect(screen.getByPlaceholderText(/email/i)).toBeInTheDocument(); expect(screen.getByPlaceholderText(/^password$/i)).toBeInTheDocument(); expect(screen.getByPlaceholderText(/confirm password/i)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /register/i })).toBeInTheDocument(); }); it('should render all required input fields', () => { renderRegister(); const nameInput = screen.getByPlaceholderText(/name/i); const emailInput = screen.getByPlaceholderText(/email/i); const passwordInput = screen.getByPlaceholderText(/^password$/i); const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i); expect(nameInput).toBeRequired(); expect(emailInput).toBeRequired(); expect(passwordInput).toBeRequired(); expect(confirmPasswordInput).toBeRequired(); }); }); describe('Form Input Changes', () => { it('should update name field on change', () => { renderRegister(); const nameInput = screen.getByPlaceholderText(/name/i); fireEvent.change(nameInput, { target: { value: 'John Doe' } }); expect(nameInput).toHaveValue('John Doe'); }); it('should update email field on change', () => { renderRegister(); const emailInput = screen.getByPlaceholderText(/email/i); fireEvent.change(emailInput, { target: { value: 'john@example.com' } }); expect(emailInput).toHaveValue('john@example.com'); }); it('should update password field on change', () => { renderRegister(); const passwordInput = screen.getByPlaceholderText(/^password$/i); fireEvent.change(passwordInput, { target: { value: 'password123' } }); expect(passwordInput).toHaveValue('password123'); }); it('should update confirm password field on change', () => { renderRegister(); const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i); fireEvent.change(confirmPasswordInput, { target: { value: 'password123' } }); expect(confirmPasswordInput).toHaveValue('password123'); }); }); describe('Form Submission', () => { it('should call register function with valid data', async () => { mockRegister.mockResolvedValue({ success: true }); renderRegister(); const nameInput = screen.getByPlaceholderText(/name/i); const emailInput = screen.getByPlaceholderText(/email/i); const passwordInput = screen.getByPlaceholderText(/^password$/i); const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i); const submitButton = screen.getByRole('button', { name: /register/i }); fireEvent.change(nameInput, { target: { value: 'John Doe' } }); fireEvent.change(emailInput, { target: { value: 'john@example.com' } }); fireEvent.change(passwordInput, { target: { value: 'password123' } }); fireEvent.change(confirmPasswordInput, { target: { value: 'password123' } }); fireEvent.click(submitButton); await waitFor(() => { expect(mockRegister).toHaveBeenCalledWith('John Doe', 'john@example.com', 'password123'); }); }); it('should disable form during submission', async () => { mockRegister.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100))); renderRegister(); const nameInput = screen.getByPlaceholderText(/name/i); const emailInput = screen.getByPlaceholderText(/email/i); const passwordInput = screen.getByPlaceholderText(/^password$/i); const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i); const submitButton = screen.getByRole('button', { name: /register/i }); fireEvent.change(nameInput, { target: { value: 'John Doe' } }); fireEvent.change(emailInput, { target: { value: 'john@example.com' } }); fireEvent.change(passwordInput, { target: { value: 'password123' } }); fireEvent.change(confirmPasswordInput, { target: { value: 'password123' } }); fireEvent.click(submitButton); await waitFor(() => { expect(nameInput).toBeDisabled(); expect(emailInput).toBeDisabled(); expect(passwordInput).toBeDisabled(); expect(confirmPasswordInput).toBeDisabled(); expect(submitButton).toBeDisabled(); }); }); }); describe('Password Validation', () => { it('should validate minimum password length', async () => { renderRegister(); const passwordInput = screen.getByPlaceholderText(/^password$/i); const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i); fireEvent.change(passwordInput, { target: { value: '12345' } }); fireEvent.change(confirmPasswordInput, { target: { value: '12345' } }); // Password should have minLength attribute expect(passwordInput).toHaveAttribute('minLength'); }); it('should show error when passwords do not match', async () => { renderRegister(); const nameInput = screen.getByPlaceholderText(/name/i); const emailInput = screen.getByPlaceholderText(/email/i); const passwordInput = screen.getByPlaceholderText(/^password$/i); const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i); const submitButton = screen.getByRole('button', { name: /register/i }); fireEvent.change(nameInput, { target: { value: 'John Doe' } }); fireEvent.change(emailInput, { target: { value: 'john@example.com' } }); fireEvent.change(passwordInput, { target: { value: 'password123' } }); fireEvent.change(confirmPasswordInput, { target: { value: 'different' } }); fireEvent.click(submitButton); // Should not call register if passwords don't match expect(mockRegister).not.toHaveBeenCalled(); }); }); describe('Authentication State', () => { it('should redirect to /map when already authenticated', () => { const authenticatedContext = { auth: { isAuthenticated: true, loading: false, user: { name: 'Test User' }, }, register: mockRegister, }; renderRegister(authenticatedContext); expect(mockedNavigate).toHaveBeenCalledWith('/map'); }); it('should show loading spinner when auth is loading', () => { const loadingContext = { auth: { isAuthenticated: false, loading: true, user: null, }, register: mockRegister, }; renderRegister(loadingContext); expect(screen.getByRole('status')).toBeInTheDocument(); }); }); describe('Field Types', () => { it('should have correct input types', () => { renderRegister(); const emailInput = screen.getByPlaceholderText(/email/i); const passwordInput = screen.getByPlaceholderText(/^password$/i); const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i); expect(emailInput).toHaveAttribute('type', 'email'); expect(passwordInput).toHaveAttribute('type', 'password'); expect(confirmPasswordInput).toHaveAttribute('type', 'password'); }); }); describe('Error Handling', () => { it('should handle registration errors', async () => { const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); mockRegister.mockRejectedValue(new Error('Registration failed')); renderRegister(); const nameInput = screen.getByPlaceholderText(/name/i); const emailInput = screen.getByPlaceholderText(/email/i); const passwordInput = screen.getByPlaceholderText(/^password$/i); const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i); const submitButton = screen.getByRole('button', { name: /register/i }); fireEvent.change(nameInput, { target: { value: 'John Doe' } }); fireEvent.change(emailInput, { target: { value: 'john@example.com' } }); fireEvent.change(passwordInput, { target: { value: 'password123' } }); fireEvent.change(confirmPasswordInput, { target: { value: 'password123' } }); fireEvent.click(submitButton); await waitFor(() => { expect(consoleErrorSpy).toHaveBeenCalled(); }); consoleErrorSpy.mockRestore(); }); }); });