import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hidalyx_drluiscastro/core/widgets/medical_modal.dart'; import 'package:hidalyx_drluiscastro/features/patient_search/data/models/patient_search_item_model.dart'; import 'package:hidalyx_drluiscastro/features/patient_search/data/services/patient_search_service.dart'; import 'package:hidalyx_drluiscastro/features/patient_search/presentation/store/patient_search_store.dart'; import 'package:hidalyx_drluiscastro/features/patient_search/presentation/widgets/search_input_bar.dart'; class MockPatientSearchService extends PatientSearchService { @override Future> searchPatients(String rawQuery, {int limit = 25}) async { if (rawQuery == 'error') { throw Exception('Simulated network error'); } if (rawQuery == 'Carlos') { return [ const PatientSearchItemModel( id: 13717, nombre: 'CARLOS JUAN', apellido: 'ARIAS GOMEZ', cedula: '001-1234567-8', telefono: '809-555-0101', edad: '45', sexo: 'M', fecUltcon: '15/09/2026', seguro: 'SENASA', motCons: 'Control Rutinario', ), ]; } return []; } } void main() { group('PatientSearchItemModel', () { test('correctly parses JSON and provides full name', () { final json = { 'id': '123', 'nombre': 'JUAN', 'apellido': 'PEREZ', 'cedula': '001-0000000-0', 'telefono': '8090000000', 'edad': '30', 'sexo': 'M', 'fec_ultcon': '01/01/2026', 'seguro': 'HUMANO', 'mot_cons': 'Chequeo', }; final model = PatientSearchItemModel.fromJson(json); expect(model.id, equals(123)); expect(model.fullName, equals('JUAN PEREZ')); expect(model.cedula, equals('001-0000000-0')); }); }); group('PatientSearchStore', () { test('successful search populates results and updates hasSearched', () async { final store = PatientSearchStore(searchService: MockPatientSearchService()); expect(store.hasSearched, isFalse); expect(store.resultsCount, equals(0)); await store.search('Carlos'); expect(store.hasSearched, isTrue); expect(store.isLoading, isFalse); expect(store.errorMessage, isNull); expect(store.resultsCount, equals(1)); expect(store.results.first.fullName, equals('CARLOS JUAN ARIAS GOMEZ')); }); test('error handling updates errorMessage', () async { final store = PatientSearchStore(searchService: MockPatientSearchService()); await store.search('error'); expect(store.hasSearched, isTrue); expect(store.isLoading, isFalse); expect(store.errorMessage, contains('Simulated network error')); expect(store.resultsCount, equals(0)); }); test('clear resets the store state', () async { final store = PatientSearchStore(searchService: MockPatientSearchService()); await store.search('Carlos'); expect(store.resultsCount, equals(1)); store.clear(); expect(store.resultsCount, equals(0)); expect(store.hasSearched, isFalse); expect(store.query, isEmpty); }); }); group('SearchInputBar widget', () { testWidgets('triggers onSearch ONLY when Enter is pressed or Buscar is clicked', (tester) async { String? searchedQuery; await tester.pumpWidget( MaterialApp( home: Scaffold( body: SearchInputBar( onSearch: (q) => searchedQuery = q, onClear: () {}, ), ), ), ); // Typing alone should not trigger search await tester.enterText(find.byType(TextField), 'Carlos Arias'); await tester.pump(); expect(searchedQuery, isNull); // Submitting via Enter key await tester.testTextInput.receiveAction(TextInputAction.search); await tester.pump(); expect(searchedQuery, equals('Carlos Arias')); }); }); group('MedicalModalDialog and showMedicalModal behavior', () { testWidgets('modal does NOT close when tapping outside (barrierDismissible is false)', (tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold( body: Builder( builder: (context) => ElevatedButton( onPressed: () { showMedicalModal( context: context, builder: (_) => const Text('Modal Body'), ); }, child: const Text('Open Modal'), ), ), ), ), ); await tester.tap(find.text('Open Modal')); await tester.pumpAndSettle(); expect(find.text('Modal Body'), findsOneWidget); // Tap on the outside barrier (top-left corner outside dialog) await tester.tapAt(const Offset(5, 5)); await tester.pumpAndSettle(); // Modal MUST still be present expect(find.text('Modal Body'), findsOneWidget); }); testWidgets('modal closes when close icon (X) button is clicked', (tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold( body: Builder( builder: (context) => ElevatedButton( onPressed: () { showMedicalModal( context: context, builder: (_) => const MedicalModalDialog( title: 'Test Modal', child: Text('Content'), ), ); }, child: const Text('Open Modal'), ), ), ), ), ); await tester.tap(find.text('Open Modal')); await tester.pumpAndSettle(); expect(find.text('Test Modal'), findsOneWidget); // Click the top-right close button (X) await tester.tap(find.byTooltip('Cerrar (Esc)')); await tester.pumpAndSettle(); // Modal must be closed expect(find.text('Test Modal'), findsNothing); }); testWidgets('modal closes when ESC key is pressed', (tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold( body: Builder( builder: (context) => ElevatedButton( onPressed: () { showMedicalModal( context: context, builder: (_) => const MedicalModalDialog( title: 'Test Modal', child: Text('Content'), ), ); }, child: const Text('Open Modal'), ), ), ), ), ); await tester.tap(find.text('Open Modal')); await tester.pumpAndSettle(); expect(find.text('Test Modal'), findsOneWidget); // Send ESC key await tester.sendKeyEvent(LogicalKeyboardKey.escape); await tester.pumpAndSettle(); // Modal must be closed expect(find.text('Test Modal'), findsNothing); }); }); }