Files
adopt-a-street/frontend/src/components/__tests__/Register.test.js
T
William Valentin 2df5a303ed refactor: convert frontend from submodule to true monorepo
Convert frontend from Git submodule to a regular monorepo directory for simplified development workflow.

Changes:
- Remove frontend submodule tracking (mode 160000 gitlink)
- Add all frontend source files directly to main repository
- Remove frontend/.git directory
- Update CLAUDE.md to clarify true monorepo structure
- Update Frontend Architecture documentation (React Router v6, Socket.IO, Leaflet, ErrorBoundary)

Benefits of Monorepo:
- Single git clone for entire project
- Unified commit history
- Simpler CI/CD pipeline
- Easier for new developers
- No submodule sync issues
- Atomic commits across frontend and backend

Frontend Files Added:
- All React components (MapView, ErrorBoundary, TaskList, SocialFeed, etc.)
- Context providers (AuthContext, SocketContext)
- Complete test suite with MSW
- Dependencies and configuration files

Branch Cleanup:
- Using 'main' as default branch (develop deleted)
- Frontend no longer has separate Git history

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 11:01:06 -07:00

263 lines
9.3 KiB
JavaScript

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(
<BrowserRouter>
<AuthContext.Provider value={contextValue}>
<Register />
</AuthContext.Provider>
</BrowserRouter>
);
};
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();
});
});
});