first commit
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
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<List<PatientSearchItemModel>> 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user