273 lines
9.2 KiB
Dart
273 lines
9.2 KiB
Dart
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/dashboard/data/models/patient_summary_model.dart';
|
||
|
|
import 'package:hidalyx_drluiscastro/features/dashboard/data/services/dashboard_service.dart';
|
||
|
|
import 'package:hidalyx_drluiscastro/features/dashboard/presentation/views/appointments_modal_view.dart';
|
||
|
|
import 'package:hidalyx_drluiscastro/features/dashboard/presentation/widgets/today_appointments_card.dart';
|
||
|
|
|
||
|
|
class MockDashboardService extends DashboardService {
|
||
|
|
final Map<String, List<PatientSummaryModel>> appointmentsByDate;
|
||
|
|
|
||
|
|
MockDashboardService({Map<String, List<PatientSummaryModel>>? data})
|
||
|
|
: appointmentsByDate = data ?? {};
|
||
|
|
|
||
|
|
String _formatKey(DateTime d) {
|
||
|
|
final day = d.day.toString().padLeft(2, '0');
|
||
|
|
final month = d.month.toString().padLeft(2, '0');
|
||
|
|
final year = d.year.toString();
|
||
|
|
return '$day/$month/$year';
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Future<List<PatientSummaryModel>> getAppointmentsByDate(DateTime date, {int limit = 100}) async {
|
||
|
|
final key = _formatKey(date);
|
||
|
|
return appointmentsByDate[key] ?? [];
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Future<List<PatientSummaryModel>> getTodayAppointments({int limit = 5}) async {
|
||
|
|
return getAppointmentsByDate(DateTime.now(), limit: limit);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void main() {
|
||
|
|
group('AppointmentsModalView Tests', () {
|
||
|
|
final now = DateTime.now();
|
||
|
|
final todayDay = now.day.toString().padLeft(2, '0');
|
||
|
|
final todayMonth = now.month.toString().padLeft(2, '0');
|
||
|
|
final todayKey = '$todayDay/$todayMonth/${now.year}';
|
||
|
|
|
||
|
|
final List<PatientSummaryModel> mockTodayPatients = [
|
||
|
|
const PatientSummaryModel(
|
||
|
|
id: 101,
|
||
|
|
nombre: 'CARLOS',
|
||
|
|
apellido: 'MARTINEZ',
|
||
|
|
cedula: '001-9999999-1',
|
||
|
|
telefono: '809-555-1122',
|
||
|
|
fecReg: '20/09/2026',
|
||
|
|
fecProxCita: '20/09/2026',
|
||
|
|
seguro: 'Sí',
|
||
|
|
motCons: 'Seguimiento postquirúrgico',
|
||
|
|
),
|
||
|
|
const PatientSummaryModel(
|
||
|
|
id: 102,
|
||
|
|
nombre: 'ANA',
|
||
|
|
apellido: 'PERDOMO',
|
||
|
|
cedula: '402-8888888-2',
|
||
|
|
telefono: '809-555-3344',
|
||
|
|
fecReg: '20/09/2026',
|
||
|
|
fecProxCita: '20/09/2026',
|
||
|
|
seguro: 'No',
|
||
|
|
motCons: 'Chequeo urológico anual',
|
||
|
|
),
|
||
|
|
];
|
||
|
|
|
||
|
|
final otherDate = DateTime(2026, 10, 25);
|
||
|
|
const otherDateKey = '25/10/2026';
|
||
|
|
final List<PatientSummaryModel> mockOtherPatients = [
|
||
|
|
const PatientSummaryModel(
|
||
|
|
id: 201,
|
||
|
|
nombre: 'RAMON',
|
||
|
|
apellido: 'VALDEZ',
|
||
|
|
cedula: '001-7777777-3',
|
||
|
|
telefono: '809-555-5566',
|
||
|
|
fecReg: '25/10/2026',
|
||
|
|
fecProxCita: '25/10/2026',
|
||
|
|
seguro: 'Sí',
|
||
|
|
motCons: 'Control de litiasis renal',
|
||
|
|
),
|
||
|
|
];
|
||
|
|
|
||
|
|
late MockDashboardService mockService;
|
||
|
|
|
||
|
|
setUp(() {
|
||
|
|
mockService = MockDashboardService(data: {
|
||
|
|
todayKey: mockTodayPatients,
|
||
|
|
otherDateKey: mockOtherPatients,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets('renders modal header, footer, and active filter indicator as "Hoy"', (tester) async {
|
||
|
|
tester.view.physicalSize = const Size(1280, 900);
|
||
|
|
tester.view.devicePixelRatio = 1.0;
|
||
|
|
addTearDown(() => tester.view.resetPhysicalSize());
|
||
|
|
|
||
|
|
await tester.pumpWidget(
|
||
|
|
MaterialApp(
|
||
|
|
home: Scaffold(
|
||
|
|
body: AppointmentsModalView(
|
||
|
|
service: mockService,
|
||
|
|
initialDate: now,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
// Check titles & subtitles
|
||
|
|
expect(find.text('Agenda de Citas Médicas'), findsOneWidget);
|
||
|
|
expect(find.text('Consulte y gestione los pacientes citados por fecha de consulta'), findsOneWidget);
|
||
|
|
|
||
|
|
// Verify that when filtered by today, it displays "Hoy (DD/MM/YYYY)"
|
||
|
|
expect(find.textContaining('Hoy ($todayKey)'), findsOneWidget);
|
||
|
|
|
||
|
|
// Verify patient records for today are rendered
|
||
|
|
expect(find.text('CARLOS MARTINEZ'), findsOneWidget);
|
||
|
|
expect(find.text('ANA PERDOMO'), findsOneWidget);
|
||
|
|
expect(find.text('Seguimiento postquirúrgico'), findsOneWidget);
|
||
|
|
expect(find.text('Chequeo urológico anual'), findsOneWidget);
|
||
|
|
|
||
|
|
// Verify footer buttons & chip
|
||
|
|
expect(find.text('ESC para salir'), findsOneWidget);
|
||
|
|
expect(find.text('Cerrar'), findsOneWidget);
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets('displays specific date filter when viewing date other than today', (tester) async {
|
||
|
|
await tester.pumpWidget(
|
||
|
|
MaterialApp(
|
||
|
|
home: Scaffold(
|
||
|
|
body: AppointmentsModalView(
|
||
|
|
service: mockService,
|
||
|
|
initialDate: otherDate,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
// Should show the specific date without "Hoy" in the filter badge
|
||
|
|
expect(find.textContaining('Filtro: 25/10/2026'), findsOneWidget);
|
||
|
|
expect(find.textContaining('Hoy'), findsWidgets); // The "Hoy" quick button is present, but the filter text is 25/10/2026
|
||
|
|
|
||
|
|
// Verify patient for that specific date
|
||
|
|
expect(find.text('RAMON VALDEZ'), findsOneWidget);
|
||
|
|
expect(find.text('Control de litiasis renal'), findsOneWidget);
|
||
|
|
expect(find.text('CARLOS MARTINEZ'), findsNothing);
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets('navigation buttons change date and "Hoy" button resets to current day', (tester) async {
|
||
|
|
await tester.pumpWidget(
|
||
|
|
MaterialApp(
|
||
|
|
home: Scaffold(
|
||
|
|
body: AppointmentsModalView(
|
||
|
|
service: mockService,
|
||
|
|
initialDate: otherDate,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
expect(find.textContaining('Filtro: 25/10/2026'), findsOneWidget);
|
||
|
|
|
||
|
|
// Tap Día siguiente chevron
|
||
|
|
await tester.tap(find.byTooltip('Día siguiente'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.textContaining('Filtro: 26/10/2026'), findsOneWidget);
|
||
|
|
expect(find.text('No hay citas registradas para el 26/10/2026'), findsOneWidget);
|
||
|
|
|
||
|
|
// Tap Día anterior chevron twice
|
||
|
|
await tester.tap(find.byTooltip('Día anterior'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.textContaining('Filtro: 25/10/2026'), findsOneWidget);
|
||
|
|
|
||
|
|
// Tap "Hoy" button to return to today
|
||
|
|
await tester.tap(find.text('Hoy'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
// Must now show "Hoy (DD/MM/YYYY)" again
|
||
|
|
expect(find.textContaining('Filtro: Hoy ($todayKey)'), findsOneWidget);
|
||
|
|
expect(find.text('CARLOS MARTINEZ'), findsOneWidget);
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets('footer container adheres to institutional padding (horizontal: 20, vertical: 12)', (tester) async {
|
||
|
|
await tester.pumpWidget(
|
||
|
|
MaterialApp(
|
||
|
|
home: Scaffold(
|
||
|
|
body: AppointmentsModalView(
|
||
|
|
service: mockService,
|
||
|
|
initialDate: now,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
final footerFinder = find.ancestor(
|
||
|
|
of: find.text('Cerrar'),
|
||
|
|
matching: find.byType(Container),
|
||
|
|
);
|
||
|
|
expect(footerFinder, findsWidgets);
|
||
|
|
|
||
|
|
final footerContainer = tester.widget<Container>(footerFinder.first);
|
||
|
|
expect(footerContainer.padding, equals(const EdgeInsets.symmetric(horizontal: 20, vertical: 12)));
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets('closes modal with Cerrar button and with ESC key', (tester) async {
|
||
|
|
await tester.pumpWidget(
|
||
|
|
MaterialApp(
|
||
|
|
home: Scaffold(
|
||
|
|
body: Builder(
|
||
|
|
builder: (ctx) => ElevatedButton(
|
||
|
|
onPressed: () => showMedicalModal(
|
||
|
|
context: ctx,
|
||
|
|
builder: (_) => AppointmentsModalView(
|
||
|
|
service: mockService,
|
||
|
|
initialDate: now,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
child: const Text('Abrir Citas'),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
|
||
|
|
// 1. Open modal and close via Cerrar button
|
||
|
|
await tester.tap(find.text('Abrir Citas'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.text('Agenda de Citas Médicas'), findsOneWidget);
|
||
|
|
|
||
|
|
await tester.tap(find.text('Cerrar'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.text('Agenda de Citas Médicas'), findsNothing);
|
||
|
|
|
||
|
|
// 2. Open modal and close via ESC key
|
||
|
|
await tester.tap(find.text('Abrir Citas'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.text('Agenda de Citas Médicas'), findsOneWidget);
|
||
|
|
|
||
|
|
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
expect(find.text('Agenda de Citas Médicas'), findsNothing);
|
||
|
|
});
|
||
|
|
|
||
|
|
testWidgets('TodayAppointmentsCard item tap invokes onPatientTap callback', (tester) async {
|
||
|
|
PatientSummaryModel? tappedPatient;
|
||
|
|
|
||
|
|
await tester.pumpWidget(
|
||
|
|
MaterialApp(
|
||
|
|
home: Scaffold(
|
||
|
|
body: TodayAppointmentsCard(
|
||
|
|
appointments: mockTodayPatients,
|
||
|
|
onPatientTap: (p) => tappedPatient = p,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
expect(find.text('CARLOS MARTINEZ'), findsOneWidget);
|
||
|
|
|
||
|
|
await tester.tap(find.text('CARLOS MARTINEZ'));
|
||
|
|
await tester.pumpAndSettle();
|
||
|
|
|
||
|
|
expect(tappedPatient, isNotNull);
|
||
|
|
expect(tappedPatient!.id, equals(101));
|
||
|
|
expect(tappedPatient!.nombre, equals('CARLOS'));
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|