diff --git a/frontend/src/components/__tests__/event-list.test.tsx b/frontend/src/components/__tests__/event-list.test.tsx new file mode 100644 index 0000000..45c67b2 --- /dev/null +++ b/frontend/src/components/__tests__/event-list.test.tsx @@ -0,0 +1,163 @@ +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { EventList } from '../event-list'; +import { api } from '@/lib/api'; + +// Mock the API +jest.mock('@/lib/api', () => ({ + api: { + getEvents: jest.fn(), + }, + Event: {}, +})); + +describe('EventList', () => { + const mockEvents = [ + { + id: '1', + name: 'Marathon 2024', + description: 'Annual city marathon', + eventDate: '2024-06-15', + location: 'City Center', + status: 'Published', + category: 'Running', + tags: ['marathon', 'running'], + maxParticipants: 100, + currentRegistrations: 45, + }, + { + id: '2', + name: 'Cycling Race', + description: 'Mountain cycling event', + eventDate: '2024-07-20', + location: 'Mountain Trail', + status: 'Draft', + category: 'Cycling', + tags: ['cycling', 'mountain'], + maxParticipants: 50, + currentRegistrations: 20, + }, + ]; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + // Positive Tests + describe('Positive Tests', () => { + it('renders loading state initially', () => { + (api.getEvents as jest.Mock).mockImplementation(() => new Promise(() => {})); + + render(); + + expect(screen.getByText(/loading events/i)).toBeInTheDocument(); + }); + + it('renders list of events', async () => { + (api.getEvents as jest.Mock).mockResolvedValue(mockEvents); + + render(); + + await waitFor(() => { + expect(screen.getByText('Marathon 2024')).toBeInTheDocument(); + }); + + expect(screen.getByText('Cycling Race')).toBeInTheDocument(); + expect(screen.getByText('Annual city marathon')).toBeInTheDocument(); + }); + + it('filters events by category', async () => { + (api.getEvents as jest.Mock).mockResolvedValue([mockEvents[0]]); + + render(); + + await waitFor(() => { + expect(screen.queryByText(/loading/i)).not.toBeInTheDocument(); + }); + + fireEvent.change(screen.getByLabelText(/category/i), { + target: { value: 'Running' }, + }); + + await waitFor(() => { + expect(api.getEvents).toHaveBeenCalledWith(expect.objectContaining({ category: 'Running' })); + }); + }); + + it('filters events by status', async () => { + (api.getEvents as jest.Mock).mockResolvedValue([mockEvents[1]]); + + render(); + + await waitFor(() => { + expect(screen.queryByText(/loading/i)).not.toBeInTheDocument(); + }); + + fireEvent.change(screen.getByLabelText(/status/i), { + target: { value: 'Draft' }, + }); + + await waitFor(() => { + expect(api.getEvents).toHaveBeenCalledWith(expect.objectContaining({ status: 'Draft' })); + }); + }); + + it('displays event details correctly', async () => { + (api.getEvents as jest.Mock).mockResolvedValue([mockEvents[0]]); + + render(); + + await waitFor(() => { + expect(screen.getByText('Marathon 2024')).toBeInTheDocument(); + }); + + expect(screen.getByText('City Center')).toBeInTheDocument(); + expect(screen.getByText(/45.*\/.*100.*registered/i)).toBeInTheDocument(); + }); + + it('shows view details link for events', async () => { + (api.getEvents as jest.Mock).mockResolvedValue([mockEvents[0]]); + + render(); + + await waitFor(() => { + expect(screen.getByText('Marathon 2024')).toBeInTheDocument(); + }); + + const viewLink = screen.getByRole('link', { name: /view details/i }); + expect(viewLink).toHaveAttribute('href', '/events/1'); + }); + }); + + // Negative Tests + describe('Negative Tests', () => { + it('displays error message when API fails', async () => { + (api.getEvents as jest.Mock).mockRejectedValue(new Error('Failed to fetch')); + + render(); + + await waitFor(() => { + expect(screen.getByText(/failed to load events/i)).toBeInTheDocument(); + }); + }); + + it('shows empty state when no events', async () => { + (api.getEvents as jest.Mock).mockResolvedValue([]); + + render(); + + await waitFor(() => { + expect(screen.getByText(/no events found/i)).toBeInTheDocument(); + }); + }); + + it('handles network error gracefully', async () => { + (api.getEvents as jest.Mock).mockRejectedValue(new Error('Network error')); + + render(); + + await waitFor(() => { + expect(screen.getByText(/network error/i)).toBeInTheDocument(); + }); + }); + }); +});