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/clients/presentation/clients_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 ClientsScreen(), ), ); } final testClients = [ { 'id': '1', 'firstName': 'John', 'lastName': 'Doe', 'email': 'john@example.com', 'company': 'Acme Corp', 'tags': ['vip', 'active'], }, { 'id': '2', 'firstName': 'Jane', 'lastName': 'Smith', 'email': 'jane@example.com', 'company': 'Tech Inc', 'tags': ['new'], }, ]; group('ClientsScreen Widget Tests', () { testWidgets('shows loading indicator initially', (tester) async { final completer = Completer>>(); when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenAnswer((_) => completer.future); await tester.pumpWidget(createTestWidget()); await tester.pump(); expect(find.byType(CircularProgressIndicator), findsOneWidget); // Complete the future to cleanup completer.complete(testClients); await tester.pumpAndSettle(); }); testWidgets('displays client list', (tester) async { when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenAnswer((_) async => testClients); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('John Doe'), findsOneWidget); expect(find.text('Jane Smith'), findsOneWidget); }); testWidgets('displays company names', (tester) async { when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenAnswer((_) async => testClients); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('Acme Corp'), findsOneWidget); expect(find.text('Tech Inc'), findsOneWidget); }); testWidgets('displays client tags', (tester) async { when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenAnswer((_) async => testClients); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('vip'), findsOneWidget); expect(find.text('active'), findsOneWidget); expect(find.text('new'), findsOneWidget); }); testWidgets('shows empty state when no clients', (tester) async { when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenAnswer((_) async => []); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('No clients yet'), findsOneWidget); expect(find.text('Add Client'), findsOneWidget); }); testWidgets('shows search bar', (tester) async { when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenAnswer((_) async => testClients); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.byType(TextField), findsOneWidget); expect(find.text('Search clients...'), findsOneWidget); }); testWidgets('has floating action button', (tester) async { when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenAnswer((_) async => testClients); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.byType(FloatingActionButton), findsOneWidget); expect(find.byIcon(Icons.add), findsOneWidget); }); testWidgets('shows error state on API failure', (tester) async { when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenThrow(Exception('Network error')); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('Failed to load clients'), findsOneWidget); expect(find.text('Retry'), findsOneWidget); }); testWidgets('displays client initials in avatar', (tester) async { when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenAnswer((_) async => testClients); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('JD'), findsOneWidget); // John Doe expect(find.text('JS'), findsOneWidget); // Jane Smith }); testWidgets('search filters results', (tester) async { when(() => mockApiClient.getClients(search: null)) .thenAnswer((_) async => testClients); when(() => mockApiClient.getClients(search: 'John')) .thenAnswer((_) async => [testClients[0]]); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Initial state shows all clients expect(find.text('John Doe'), findsOneWidget); expect(find.text('Jane Smith'), findsOneWidget); // Enter search await tester.enterText(find.byType(TextField), 'John'); await tester.testTextInput.receiveAction(TextInputAction.done); await tester.pumpAndSettle(); // Should only show John expect(find.text('John Doe'), findsOneWidget); expect(find.text('Jane Smith'), findsNothing); }); testWidgets('shows no results message for search', (tester) async { when(() => mockApiClient.getClients(search: null)) .thenAnswer((_) async => testClients); when(() => mockApiClient.getClients(search: 'xyz')) .thenAnswer((_) async => []); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); await tester.enterText(find.byType(TextField), 'xyz'); await tester.testTextInput.receiveAction(TextInputAction.done); await tester.pumpAndSettle(); expect(find.text('No clients found'), findsOneWidget); }); testWidgets('limits displayed tags to 3', (tester) async { final clientWithManyTags = [ { 'id': '1', 'firstName': 'John', 'lastName': 'Doe', 'email': 'john@example.com', 'company': 'Acme', 'tags': ['tag1', 'tag2', 'tag3', 'tag4', 'tag5'], }, ]; when(() => mockApiClient.getClients(search: any(named: 'search'))) .thenAnswer((_) async => clientWithManyTags); await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); expect(find.text('tag1'), findsOneWidget); expect(find.text('tag2'), findsOneWidget); expect(find.text('tag3'), findsOneWidget); expect(find.text('tag4'), findsNothing); // Should not show 4th tag }); }); }