import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { BrowserRouter } from 'react-router-dom'; import { AuthContext } from '../../context/AuthContext'; import { SSEContext } from '../../context/SSEContext'; import Events from '../Events'; import axios from 'axios'; // Mocks const mockAuthContext = { auth: { isAuthenticated: true, loading: false, user: { id: 'user123', name: 'Test User' } }, login: jest.fn(), logout: jest.fn(), }; const mockSSEContext = { connected: true, notifications: [], on: jest.fn(), off: jest.fn(), subscribe: jest.fn().mockResolvedValue({ subscribed: [] }), unsubscribe: jest.fn().mockResolvedValue({ unsubscribed: [] }), clearNotification: jest.fn(), clearAllNotifications: jest.fn(), }; jest.mock('axios'); describe('Events Component', () => { const mockEvents = [ { _id: 'event1', title: 'Community Cleanup Day', description: 'Join us for a community cleanup event', date: '2023-06-15T10:00:00.000Z', location: 'Central Park', participants: [], participantsCount: 0, status: 'upcoming', createdAt: '2023-01-01T00:00:00.000Z', }, { _id: 'event2', title: 'Street Maintenance Workshop', description: 'Learn proper street maintenance techniques', date: '2023-06-20T14:00:00.000Z', location: 'Community Center', participants: [ { userId: 'user123', name: 'Test User', joinedAt: '2023-01-01T00:00:00.000Z' } ], participantsCount: 1, status: 'ongoing', createdAt: '2023-01-02T00:00:00.000Z', }, { _id: 'event3', title: 'Completed Event', description: 'This event has already finished', date: '2023-01-01T00:00:00.000Z', location: 'City Hall', participants: [ { userId: 'user123', name: 'Test User', joinedAt: '2023-01-01T00:00:00.000Z' }, { userId: 'user456', name: 'Other User', joinedAt: '2023-01-01T00:00:00.000Z' } ], participantsCount: 2, status: 'completed', createdAt: '2022-12-01T00:00:00.000Z', }, ]; beforeEach(() => { jest.clearAllMocks(); // Mock axios.get to return events axios.get.mockResolvedValue({ data: mockEvents }); // Mock axios.post for event creation axios.post.mockResolvedValue({ data: { ...mockEvents[0], _id: 'event4' } }); // Mock axios.put for event joining axios.put.mockResolvedValue({ data: { ...mockEvents[1], participants: [...mockEvents[1].participants, { userId: 'user123', name: 'Test User', joinedAt: '2023-01-01T00:00:00.000Z' }] } }); }); const renderEvents = () => { return render( ); }; it('renders events list correctly', async () => { renderEvents(); await waitFor(() => { expect(screen.getByText('Community Cleanup Day')).toBeInTheDocument(); expect(screen.getByText('Street Maintenance Workshop')).toBeInTheDocument(); expect(screen.getByText('Completed Event')).toBeInTheDocument(); }); }); it('shows loading state initially', () => { // Mock axios to delay response axios.get.mockImplementation(() => new Promise(resolve => setTimeout(() => resolve({ data: [] }), 100))); renderEvents(); expect(screen.getByText('Loading events...')).toBeInTheDocument(); }); it('displays event status correctly', async () => { renderEvents(); await waitFor(() => { const upcomingEvent = screen.getByText('Community Cleanup Day').closest('[data-status="upcoming"]'); const ongoingEvent = screen.getByText('Street Maintenance Workshop').closest('[data-status="ongoing"]'); const completedEvent = screen.getByText('Completed Event').closest('[data-status="completed"]'); expect(upcomingEvent).toBeInTheDocument(); expect(ongoingEvent).toBeInTheDocument(); expect(completedEvent).toBeInTheDocument(); }); }); it('displays participant count', async () => { renderEvents(); await waitFor(() => { expect(screen.getByText('0 participants')).toBeInTheDocument(); expect(screen.getByText('1 participant')).toBeInTheDocument(); expect(screen.getByText('2 participants')).toBeInTheDocument(); }); }); it('displays event dates correctly', async () => { renderEvents(); await waitFor(() => { expect(screen.getByText('June 15, 2023')).toBeInTheDocument(); expect(screen.getByText('June 20, 2023')).toBeInTheDocument(); }); }); it('displays event locations', async () => { renderEvents(); await waitFor(() => { expect(screen.getByText('Central Park')).toBeInTheDocument(); expect(screen.getByText('Community Center')).toBeInTheDocument(); expect(screen.getByText('City Hall')).toBeInTheDocument(); }); }); it('shows event creation form', async () => { renderEvents(); await waitFor(() => { expect(screen.getByText('Create New Event')).toBeInTheDocument(); expect(screen.getByPlaceholderText('Event title')).toBeInTheDocument(); expect(screen.getByPlaceholderText('Event description')).toBeInTheDocument(); }); }); it('validates event creation form', async () => { renderEvents(); await waitFor(() => { const createButton = screen.getByText('Create Event'); const titleInput = screen.getByPlaceholderText('Event title'); expect(createButton).toBeInTheDocument(); expect(titleInput).toBeInTheDocument(); }); }); it('creates new event successfully', async () => { renderEvents(); await waitFor(() => { const createButton = screen.getByText('Create Event'); const titleInput = screen.getByPlaceholderText('Event title'); const descriptionInput = screen.getByPlaceholderText('Event description'); const dateInput = screen.getByDisplayValue('2023-06-15'); const locationInput = screen.getByPlaceholderText('Event location'); expect(createButton).toBeInTheDocument(); expect(titleInput).toBeInTheDocument(); expect(descriptionInput).toBeInTheDocument(); expect(dateInput).toBeInTheDocument(); expect(locationInput).toBeInTheDocument(); }); // Fill out form fireEvent.change(titleInput, { target: { value: 'New Test Event' } }); fireEvent.change(descriptionInput, { target: { value: 'Test event description' } }); fireEvent.change(dateInput, { target: { value: '2023-07-01' } }); fireEvent.change(locationInput, { target: { value: 'Test Location' } }); // Submit form fireEvent.click(createButton); // Verify axios.post was called await waitFor(() => { expect(axios.post).toHaveBeenCalledWith('/api/events', { title: 'New Test Event', description: 'Test event description', date: '2023-07-01', location: 'Test Location' }); }); }); it('handles event joining', async () => { renderEvents(); await waitFor(() => { const joinButtons = screen.getAllByText('Join Event'); const firstJoinButton = joinButtons[0]; expect(firstJoinButton).toBeInTheDocument(); }); }); it('updates participant count when joining event', async () => { renderEvents(); await waitFor(() => { const joinButtons = screen.getAllByText('Join Event'); const firstJoinButton = joinButtons[0]; fireEvent.click(firstJoinButton); expect(axios.put).toHaveBeenCalledWith('/api/events/event1/join'); }); }); it('shows error message when API fails', async () => { // Mock axios.get to throw error axios.get.mockRejectedValue(new Error('Network error')); renderEvents(); await waitFor(() => { expect(screen.getByText(/Failed to load events/)).toBeInTheDocument(); }); }); it('displays empty state when no events', async () => { // Mock empty response axios.get.mockResolvedValue({ data: [] }); renderEvents(); await waitFor(() => { expect(screen.getByText('No upcoming events')).toBeInTheDocument(); expect(screen.getByText('Be the first to create one!')).toBeInTheDocument(); }); }); it('filters events by status', async () => { renderEvents(); await waitFor(() => { // Look for filter buttons const filterButtons = screen.getAllByRole('button'); const upcomingFilter = filterButtons.find(btn => btn.textContent.includes('Upcoming')); const completedFilter = filterButtons.find(btn => btn.textContent.includes('Completed')); expect(upcomingFilter).toBeInTheDocument(); expect(completedFilter).toBeInTheDocument(); }); }); it('searches events', async () => { renderEvents(); await waitFor(() => { const searchInput = screen.getByPlaceholderText('Search events...'); expect(searchInput).toBeInTheDocument(); }); }); it('shows event details', async () => { renderEvents(); await waitFor(() => { const eventCards = screen.getAllByTestId('event-card'); expect(eventCards.length).toBeGreaterThan(0); // Check first event card const firstCard = eventCards[0]; expect(firstCard).toHaveTextContent('Community Cleanup Day'); }); }); it('handles real-time updates', async () => { const { on } = mockSSEContext; renderEvents(); await waitFor(() => { // Simulate receiving a new event via SSE const sseCallback = on.mock.calls[0][1]; const newEventData = { type: 'new_event', event: { ...mockEvents[0], _id: 'event5' } }; sseCallback(newEventData); // Verify new event appears in the list expect(screen.getByText('Community Cleanup Day')).toBeInTheDocument(); }); }); it('displays user\'s joined events', async () => { renderEvents(); await waitFor(() => { // Look for "My Events" section const myEventsSection = screen.getByText('My Events'); expect(myEventsSection).toBeInTheDocument(); // Should show events user has joined expect(screen.getByText('Street Maintenance Workshop')).toBeInTheDocument(); }); }); it('handles event cancellation', async () => { renderEvents(); await waitFor(() => { const cancelButtons = screen.getAllByText('Cancel'); const firstCancelButton = cancelButtons[0]; expect(firstCancelButton).toBeInTheDocument(); }); }); it('shows event statistics', async () => { renderEvents(); await waitFor(() => { expect(screen.getByText('Total Events: 3')).toBeInTheDocument(); expect(screen.getByText('Upcoming: 1')).toBeInTheDocument(); expect(screen.getByText('Ongoing: 1')).toBeInTheDocument(); expect(screen.getByText('Completed: 1')).toBeInTheDocument(); }); }); it('handles pagination', async () => { renderEvents(); await waitFor(() => { // Look for pagination controls expect(screen.getByText('Next')).toBeInTheDocument(); expect(screen.getByText('Previous')).toBeInTheDocument(); }); }); });