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();
+ });
+ });
+ });
+});