import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/estado/estado_alarmas.dart'; import 'package:pluriwave/l10n/gen/app_localizations.dart'; import 'package:pluriwave/modelos/alarma_musical.dart'; import 'package:pluriwave/pantallas/pantalla_vacaciones.dart'; import 'package:pluriwave/servicios/servicio_alarmas.dart'; import 'package:pluriwave/widgets/pluri_push_scaffold.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../helpers/fakes_alarmas.dart'; /// WU9, `alarm-vacation-ranges` spec — the real Vacaciones manager screen /// that WU8's summary row pushes to (replacing WU8's own temporary /// placeholder). Covers: active-range hero (name + days-remaining countdown /// + per-alarm impact line), the "PROGRAMADOS" upcoming list, the "Añadir /// rango" CTA, and the "Rangos pasados" history section. /// /// Clock note: `PantallaVacaciones` calls `EstadoAlarmas.rangoVacacionesActivo()` /// etc. with NO `ahora` argument (production code always uses real /// `DateTime.now()` — design ADR-6 explicitly rejects a testable Clock /// abstraction as out of scope for this redesign). So fixtures here are /// built RELATIVE to `DateTime.now()`, never as hardcoded calendar dates — /// the `ServicioAlarmas`'s own `reloj` (used only for alarm SCHEDULING /// maths, irrelevant to the vacation-query assertions below) is left at its /// real-clock default too, for consistency. DateTime get _hoy => DateTime.now(); DateTime get _hoyDia => DateTime(_hoy.year, _hoy.month, _hoy.day); Future _crearEstado({ List alarmas = const [], List vacaciones = const [], }) async { final android = FakePuertoAlarmasAndroid(); final estado = EstadoAlarmas( servicio: ServicioAlarmas(), android: android, iniciarAutomaticamente: false, ); for (final alarma in alarmas) { await estado.guardarAlarma(alarma); } if (vacaciones.isNotEmpty) { await estado.guardarVacaciones(vacaciones); } return estado; } Widget _buildScreen(EstadoAlarmas estado) { return ChangeNotifierProvider.value( value: estado, child: MaterialApp( locale: const Locale('es'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: const PantallaVacaciones(), ), ); } Future _pumpEstable(WidgetTester tester) async { await tester.pump(); await tester.pumpAndSettle(); } void main() { setUp(() { SharedPreferences.setMockInitialValues({}); }); testWidgets( 'renders inside a PluriPushScaffold; con un rango activo muestra su ' 'nombre, los dias restantes, y la linea de impacto por alarma', (tester) async { final estado = await _crearEstado( alarmas: [ const AlarmaMusical( id: 'p1', nombre: 'Pausada 1', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: [], sonarEnVacaciones: false, ), const AlarmaMusical( id: 'p2', nombre: 'Pausada 2', hora: 13, minuto: 45, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: [], sonarEnVacaciones: false, ), const AlarmaMusical( id: 'n1', nombre: 'Sigue', hora: 8, minuto: 15, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: [], ), ], vacaciones: [ RangoVacaciones( id: 'v1', nombre: 'Julio activo', inicio: _hoyDia.subtract(const Duration(days: 3)), fin: _hoyDia.add(const Duration(days: 5)), ), ], ); addTearDown(estado.dispose); await tester.pumpWidget(_buildScreen(estado)); await _pumpEstable(tester); expect(find.byType(PluriPushScaffold), findsOneWidget); final l10n = AppLocalizations.of( tester.element(find.byType(PantallaVacaciones)), ); expect(find.text('Julio activo'), findsOneWidget); expect(find.text(l10n.vacationSummaryActiveCountdown(5)), findsOneWidget); expect( find.text(l10n.vacationImpactPausedLabel('07:30, 13:45')), findsOneWidget, ); expect( find.text(l10n.vacationImpactContinuesLabel('08:15')), findsOneWidget, ); }, ); testWidgets('sin rango activo, muestra la pista en su lugar', (tester) async { final estado = await _crearEstado(); addTearDown(estado.dispose); await tester.pumpWidget(_buildScreen(estado)); await _pumpEstable(tester); final l10n = AppLocalizations.of( tester.element(find.byType(PantallaVacaciones)), ); expect(find.text(l10n.vacationNoActiveRangeHint), findsOneWidget); }); testWidgets('los rangos futuros aparecen bajo el encabezado PROGRAMADOS', ( tester, ) async { final estado = await _crearEstado( vacaciones: [ RangoVacaciones( id: 'f1', nombre: 'Rango lejano', inicio: _hoyDia.add(const Duration(days: 120)), fin: _hoyDia.add(const Duration(days: 125)), ), RangoVacaciones( id: 'f2', nombre: 'Rango cercano', inicio: _hoyDia.add(const Duration(days: 20)), fin: _hoyDia.add(const Duration(days: 25)), ), ], ); addTearDown(estado.dispose); await tester.pumpWidget(_buildScreen(estado)); await _pumpEstable(tester); final l10n = AppLocalizations.of( tester.element(find.byType(PantallaVacaciones)), ); expect(find.text(l10n.vacationUpcomingSectionTitle), findsOneWidget); expect(find.text('Rango lejano'), findsOneWidget); expect(find.text('Rango cercano'), findsOneWidget); }); testWidgets( 'los rangos pasados aparecen bajo el encabezado "Rangos pasados"', (tester) async { final estado = await _crearEstado( vacaciones: [ RangoVacaciones( id: 'pa1', nombre: 'Rango viejo', inicio: _hoyDia.subtract(const Duration(days: 30)), fin: _hoyDia.subtract(const Duration(days: 20)), ), ], ); addTearDown(estado.dispose); await tester.pumpWidget(_buildScreen(estado)); await _pumpEstable(tester); final l10n = AppLocalizations.of( tester.element(find.byType(PantallaVacaciones)), ); expect(find.text(l10n.vacationPastSectionTitle), findsOneWidget); expect(find.text('Rango viejo'), findsOneWidget); }, ); testWidgets('el CTA "Añadir rango" abre el formulario de alta existente', ( tester, ) async { final estado = await _crearEstado(); addTearDown(estado.dispose); await tester.pumpWidget(_buildScreen(estado)); await _pumpEstable(tester); final l10n = AppLocalizations.of( tester.element(find.byType(PantallaVacaciones)), ); await tester.tap(find.text(l10n.addVacationRangeCta)); await _pumpEstable(tester); expect(find.text(l10n.newVacationRangeTitle), findsOneWidget); }); }