import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:network_app/features/events/presentation/events_screen.dart'; import 'package:network_app/shared/services/api_client.dart'; import 'package:mocktail/mocktail.dart'; class MockApiClient extends Mock implements ApiClient {} void main() { late MockApiClient mockApiClient; setUp(() { mockApiClient = MockApiClient(); }); Widget createTestWidget() { return ProviderScope( overrides: [ apiClientProvider.overrideWithValue(mockApiClient), ], child: MaterialApp( home: const EventsScreen(), ), ); } final now = DateTime.now(); final testEvents = [ { 'event': { 'id': '1', 'type': 'birthday', 'title': "John's Birthday", 'date': now.add(const Duration(days: 5)).toIso8601String(), 'recurring': true, }, 'client': { 'id': 'c1', 'firstName': 'John', 'lastName': 'Doe', }, }, { 'event': { 'id': '2', 'type': 'anniversary', 'title': "Jane's Anniversary", 'date': now.add(const Duration(days: 10)).toIso8601String(), 'recurring': true, }, 'client': { 'id': 'c2', 'firstName': 'Jane', 'lastName': 'Smith', }, }, ]; group('EventsScreen Widget Tests', () { testWidgets('shows loading indicator initially', (tester) async { final completer = Completer>>(); when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) => completer.future); await tester.pumpWidget(createTestWidget()); await tester.pump(); expect(find.byType(CircularProgressIndicator), findsOneWidget); // Complete to cleanup completer.complete(testEvents); await tester.pumpAndSettle(); }); testWidgets('displays events list', (tester) async { when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) async => testEvents); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text("John's Birthday"), findsOneWidget); expect(find.text("Jane's Anniversary"), findsOneWidget); }); testWidgets('displays client names', (tester) async { when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) async => testEvents); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('John Doe'), findsOneWidget); expect(find.text('Jane Smith'), findsOneWidget); }); testWidgets('shows birthday icon for birthday events', (tester) async { when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) async => [testEvents[0]]); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.byIcon(Icons.cake), findsOneWidget); }); testWidgets('shows heart icon for anniversary events', (tester) async { when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) async => [testEvents[1]]); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.byIcon(Icons.favorite), findsOneWidget); }); testWidgets('shows empty state when no events', (tester) async { when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) async => []); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('No upcoming events'), findsOneWidget); expect(find.text('Add birthdays and anniversaries to clients'), findsOneWidget); }); testWidgets('shows error state on API failure', (tester) async { when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenThrow(Exception('Network error')); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('Failed to load events'), findsOneWidget); expect(find.text('Retry'), findsOneWidget); }); testWidgets('shows days until event', (tester) async { when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) async => testEvents); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Should show "In X days" for upcoming events expect(find.textContaining('In'), findsWidgets); expect(find.textContaining('days'), findsWidgets); }); testWidgets('shows Today for same-day events', (tester) async { final todayEvent = [ { 'event': { 'id': '1', 'type': 'birthday', 'title': "John's Birthday", 'date': DateTime.now().toIso8601String(), 'recurring': true, }, 'client': { 'id': 'c1', 'firstName': 'John', 'lastName': 'Doe', }, }, ]; when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) async => todayEvent); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('Today!'), findsOneWidget); }); testWidgets('shows Tomorrow for next-day events', (tester) async { // Use a date that's 36 hours ahead to ensure inDays == 1 final tomorrow = DateTime.now().add(const Duration(hours: 36)); final tomorrowEvent = [ { 'event': { 'id': '1', 'type': 'birthday', 'title': "John's Birthday", 'date': tomorrow.toIso8601String(), 'recurring': true, }, 'client': { 'id': 'c1', 'firstName': 'John', 'lastName': 'Doe', }, }, ]; when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) async => tomorrowEvent); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('Tomorrow'), findsOneWidget); }); testWidgets('has app bar with title', (tester) async { when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays'))) .thenAnswer((_) async => testEvents); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('Upcoming Events'), findsOneWidget); }); }); }