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/config/data/services/config_service.dart'; import 'package:hidalyx_drluiscastro/features/config/presentation/store/config_store.dart'; import 'package:hidalyx_drluiscastro/features/config/presentation/views/config_modal_view.dart'; import 'package:hidalyx_drluiscastro/features/dashboard/data/models/doctor_info_model.dart'; class MockConfigService extends ConfigService { DoctorInfoModel currentConfig; bool shouldThrowOnSave = false; bool shouldThrowOnLoad = false; MockConfigService({ DoctorInfoModel? initialConfig, }) : currentConfig = initialConfig ?? const DoctorInfoModel( id: 1, nombreDoctor: 'Dr. Luis A. Castro Sosa', especialidad: 'Cirujano Urólogo', descripEspecialidad: 'Enfermedades Vías Urinarias, Riñones', descripGeneral: 'Santiago, R.D. Tel: 809-583-2394', ); @override Future getConfig() async { if (shouldThrowOnLoad) { throw Exception('Error al cargar configuración de prueba'); } return currentConfig; } @override Future updateConfig(DoctorInfoModel model) async { if (shouldThrowOnSave) { throw Exception('Error al guardar configuración de prueba'); } currentConfig = model.copyWith(id: 1); return currentConfig; } } void main() { group('DoctorInfoModel Serialization & Immutability', () { test('serializes and deserializes properly from Directus format', () { final json = { 'id': 1, 'nombre_doctor': 'Dr. Luis A. Castro Sosa', 'especialidad': 'Cirujano Urólogo', 'descrip_especialidad': 'Urología Oncológica y Laparoscopía', 'descrip_general': 'Consultorio 204, Santiago. Tel: 809-555-0100', }; final model = DoctorInfoModel.fromJson(json); expect(model.id, equals(1)); expect(model.nombreDoctor, equals('Dr. Luis A. Castro Sosa')); expect(model.especialidad, equals('Cirujano Urólogo')); expect(model.descripEspecialidad, equals('Urología Oncológica y Laparoscopía')); expect(model.descripGeneral, equals('Consultorio 204, Santiago. Tel: 809-555-0100')); // toJson without ID for Directus PATCH singleton final jsonWithoutId = model.toJson(); expect(jsonWithoutId.containsKey('id'), isFalse); expect(jsonWithoutId['nombre_doctor'], equals('Dr. Luis A. Castro Sosa')); expect(jsonWithoutId['especialidad'], equals('Cirujano Urólogo')); // toJson with ID final jsonWithId = model.toJson(includeId: true); expect(jsonWithId['id'], equals(1)); }); test('copyWith properly modifies target fields while preserving others', () { const original = DoctorInfoModel( id: 1, nombreDoctor: 'Dr. Luis Castro', especialidad: 'Urólogo', descripEspecialidad: 'General', descripGeneral: 'Santiago', ); final updated = original.copyWith( especialidad: 'Urología Robótica', ); expect(updated.id, equals(1)); expect(updated.nombreDoctor, equals('Dr. Luis Castro')); expect(updated.especialidad, equals('Urología Robótica')); expect(updated.descripEspecialidad, equals('General')); expect(updated.descripGeneral, equals('Santiago')); }); }); group('ConfigStore State Management & Validation', () { test('validation requires nombreDoctor and especialidad', () { final mockService = MockConfigService(); final store = ConfigStore(configService: mockService); expect(store.isValid, isFalse); store.nombreDoctor = 'Dr. Luis Castro'; expect(store.isValid, isFalse); store.especialidad = 'Cirujano Urólogo'; expect(store.isValid, isTrue); store.nombreDoctor = ' '; expect(store.isValid, isFalse); }); test('loadConfig successfully loads data into store observables', () async { final mockService = MockConfigService(); final store = ConfigStore(configService: mockService); expect(store.isLoading, isFalse); final loadFuture = store.loadConfig(); expect(store.isLoading, isTrue); await loadFuture; expect(store.isLoading, isFalse); expect(store.id, equals(1)); expect(store.nombreDoctor, equals('Dr. Luis A. Castro Sosa')); expect(store.especialidad, equals('Cirujano Urólogo')); expect(store.errorMessage, isNull); }); test('loadConfig sets errorMessage on failure', () async { final mockService = MockConfigService()..shouldThrowOnLoad = true; final store = ConfigStore(configService: mockService); await store.loadConfig(); expect(store.isLoading, isFalse); expect(store.errorMessage, isNotNull); expect(store.errorMessage, contains('Error al cargar configuración')); }); test('saveConfig validates and updates via service', () async { final mockService = MockConfigService(); final store = ConfigStore(configService: mockService); // Attempt save with invalid state final invalidResult = await store.saveConfig(); expect(invalidResult, isNull); expect(store.errorMessage, isNotNull); // Set valid state and save store.nombreDoctor = 'Dr. Juan Perez'; store.especialidad = 'Cardiólogo'; store.descripEspecialidad = 'Arritmias y Marcapasos'; store.descripGeneral = 'Clínica Corominas'; final savedResult = await store.saveConfig(); expect(savedResult, isNotNull); expect(savedResult!.nombreDoctor, equals('Dr. Juan Perez')); expect(savedResult.especialidad, equals('Cardiólogo')); expect(store.successMessage, isNotNull); expect(store.isSaving, isFalse); expect(store.errorMessage, isNull); }); test('saveConfig handles service error gracefully', () async { final mockService = MockConfigService()..shouldThrowOnSave = true; final store = ConfigStore(configService: mockService); store.nombreDoctor = 'Dr. Luis Castro'; store.especialidad = 'Urología'; final result = await store.saveConfig(); expect(result, isNull); expect(store.isSaving, isFalse); expect(store.errorMessage, contains('Error al guardar configuración')); }); }); group('ConfigModalView Widget Tests', () { testWidgets('renders all fields and sections properly', (tester) async { final mockService = MockConfigService(); final store = ConfigStore(configService: mockService); await store.loadConfig(); await tester.pumpWidget( MaterialApp( home: Scaffold( body: ConfigModalView( store: store, ), ), ), ); await tester.pumpAndSettle(); // Check titles & subtitles expect(find.text('Configuración del Consultorio'), findsOneWidget); expect(find.text('Identidad del Profesional'), findsOneWidget); expect(find.text('Áreas de Enfoque y Subespecialidades'), findsOneWidget); expect(find.text('Datos del Consultorio y Contacto'), findsOneWidget); // Check buttons and footer elements expect(find.text('Cancelar'), findsOneWidget); expect(find.text('Guardar Configuración'), findsOneWidget); expect(find.text('ESC para salir'), findsOneWidget); // Check populated initial values expect(find.text('Dr. Luis A. Castro Sosa'), findsOneWidget); expect(find.text('Cirujano Urólogo'), findsOneWidget); }); testWidgets('footer container has established padding separating buttons from extremities', (tester) async { final mockService = MockConfigService(); final store = ConfigStore(configService: mockService); await store.loadConfig(); await tester.pumpWidget( MaterialApp( home: Scaffold( body: ConfigModalView( store: store, ), ), ), ); await tester.pumpAndSettle(); // Find the footer Container that holds the buttons and ESC hint final footerFinder = find.ancestor( of: find.text('Guardar Configuración'), matching: find.byType(Container), ); expect(footerFinder, findsWidgets); final footerContainer = tester.widget(footerFinder.first); expect(footerContainer.padding, equals(const EdgeInsets.symmetric(horizontal: 20, vertical: 12))); }); testWidgets('cancels and closes when Cancelar button is pressed', (tester) async { final mockService = MockConfigService(); final store = ConfigStore(configService: mockService); await store.loadConfig(); await tester.pumpWidget( MaterialApp( home: Scaffold( body: Builder( builder: (ctx) => ElevatedButton( onPressed: () => showMedicalModal( context: ctx, builder: (_) => ConfigModalView(store: store), ), child: const Text('Abrir Modal'), ), ), ), ), ); // Open modal await tester.tap(find.text('Abrir Modal')); await tester.pumpAndSettle(); expect(find.text('Configuración del Consultorio'), findsOneWidget); // Tap Cancelar await tester.tap(find.text('Cancelar')); await tester.pumpAndSettle(); // Modal is dismissed expect(find.text('Configuración del Consultorio'), findsNothing); }); testWidgets('closes modal when ESC key is pressed', (tester) async { final mockService = MockConfigService(); final store = ConfigStore(configService: mockService); await store.loadConfig(); await tester.pumpWidget( MaterialApp( home: Scaffold( body: Builder( builder: (ctx) => ElevatedButton( onPressed: () => showMedicalModal( context: ctx, builder: (_) => ConfigModalView(store: store), ), child: const Text('Abrir Modal'), ), ), ), ), ); // Open modal await tester.tap(find.text('Abrir Modal')); await tester.pumpAndSettle(); expect(find.text('Configuración del Consultorio'), findsOneWidget); // Simulate ESC key await tester.sendKeyEvent(LogicalKeyboardKey.escape); await tester.pumpAndSettle(); // Modal must be dismissed via ESC expect(find.text('Configuración del Consultorio'), findsNothing); }); testWidgets('saves modified configuration and triggers onSaved callback', (tester) async { final mockService = MockConfigService(); final store = ConfigStore(configService: mockService); await store.loadConfig(); DoctorInfoModel? savedModel; await tester.pumpWidget( MaterialApp( home: Scaffold( body: Builder( builder: (ctx) => ElevatedButton( onPressed: () => showMedicalModal( context: ctx, builder: (_) => ConfigModalView( store: store, onSaved: (m) => savedModel = m, ), ), child: const Text('Abrir Modal'), ), ), ), ), ); // Open modal await tester.tap(find.text('Abrir Modal')); await tester.pumpAndSettle(); // Enter updated doctor name final nameField = find.widgetWithText(TextField, 'Dr. Luis A. Castro Sosa'); await tester.enterText(nameField, 'Dr. Luis Alberto Castro'); await tester.pumpAndSettle(); // Tap Guardar await tester.tap(find.text('Guardar Configuración')); await tester.pumpAndSettle(); // Verify callback was called with updated name and modal closed expect(savedModel, isNotNull); expect(savedModel!.nombreDoctor, equals('Dr. Luis Alberto Castro')); expect(find.text('Configuración del Consultorio'), findsNothing); expect(find.text('Configuración del consultorio guardada correctamente'), findsOneWidget); }); }); }