import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/estado/estado_alarmas.dart'; import 'package:pluriwave/estado/estado_radio.dart'; import 'package:pluriwave/l10n/gen/app_localizations.dart'; import 'package:pluriwave/modelos/alarma_musical.dart'; import 'package:pluriwave/pantallas/pantalla_alarmas.dart'; import 'package:pluriwave/servicios/servicio_alarmas.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../helpers/fakes.dart'; import '../helpers/fakes_alarmas.dart'; class _Entorno { _Entorno({required this.estadoAlarmas}); final EstadoAlarmas estadoAlarmas; } Future<_Entorno> _abrirEditor(WidgetTester tester) async { tester.view.physicalSize = const Size(1440, 3200); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final favoritos = FakeServicioFavoritos(); await favoritos.agregar(emisoraDemo(uuid: 'alfa', nombre: 'Alfa FM')); await favoritos.agregar(emisoraDemo(uuid: 'beta', nombre: 'Beta FM')); final radio = EstadoRadio( audio: FakeServicioAudio(), favoritos: favoritos, radio: FakeServicioRadio(), servicioEcualizador: FakeServicioEcualizador(), servicioGrabacion: FakeServicioGrabacionRadioInactiva(), iniciarAutomaticamente: false, ); addTearDown(radio.dispose); await radio.cargarFavoritos(); final android = FakePuertoAlarmasAndroid(); final estadoAlarmas = EstadoAlarmas( servicio: ServicioAlarmas(reloj: DateTime.now), android: android, iniciarAutomaticamente: false, ); addTearDown(estadoAlarmas.dispose); addTearDown(android.dispose); await estadoAlarmas.guardarAlarma( const AlarmaMusical( id: 'ed1', nombre: 'Semanal', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.diasSemana, diasSemana: [DateTime.monday], ), ); await tester.pumpWidget( MultiProvider( providers: [ ChangeNotifierProvider.value(value: radio), ChangeNotifierProvider.value(value: estadoAlarmas), ], child: MaterialApp( locale: const Locale('es'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: const Scaffold(body: PantallaAlarmas()), ), ), ); await tester.pumpAndSettle(); final l10n = lookupAppLocalizations(const Locale('es')); await tester.ensureVisible(find.text(l10n.editAction).first); await tester.pumpAndSettle(); await tester.tap(find.text(l10n.editAction).first); await tester.pumpAndSettle(); return _Entorno(estadoAlarmas: estadoAlarmas); } String _textoPreview(WidgetTester tester) { final texto = tester.widget( find.descendant( of: find.byKey(const ValueKey('next-trigger-preview')), matching: find.byType(Text), ), ); return texto.data ?? ''; } void main() { final l10n = lookupAppLocalizations(const Locale('es')); setUp(() { SharedPreferences.setMockInitialValues({}); }); testWidgets( 'muestra la proxima ejecucion y la actualiza al cambiar la recurrencia ' '(S2-R8)', (tester) async { await _abrirEditor(tester); expect( find.byKey(const ValueKey('next-trigger-preview')), findsOneWidget, ); final antes = _textoPreview(tester); expect(antes, isNot(l10n.alarmNoNextExecution)); // Lunes -> Martes: la fecha calculada SIEMPRE cambia, sea cual sea hoy. await tester.tap(find.text(l10n.weekdayShortTuesday)); await tester.pumpAndSettle(); await tester.tap(find.text(l10n.weekdayShortMonday)); await tester.pumpAndSettle(); final despues = _textoPreview(tester); expect(despues, isNot(l10n.alarmNoNextExecution)); expect(despues, isNot(antes)); }, ); testWidgets( 'el selector de emisora abre un bottom sheet con buscador (S2-R9)', (tester) async { await _abrirEditor(tester); await tester.ensureVisible( find.byKey(const ValueKey('alarm-station-field')), ); await tester.pumpAndSettle(); await tester.tap(find.byKey(const ValueKey('alarm-station-field'))); await tester.pumpAndSettle(); expect(find.byType(SearchBar), findsOneWidget); final lista = find.byType(ListView).last; expect( find.descendant(of: lista, matching: find.text('Alfa FM')), findsOneWidget, ); expect( find.descendant(of: lista, matching: find.text('Beta FM')), findsOneWidget, ); await tester.enterText(find.byType(TextField).last, 'beta'); await tester.pumpAndSettle(); expect( find.descendant(of: lista, matching: find.text('Alfa FM')), findsNothing, ); expect( find.descendant(of: lista, matching: find.text('Beta FM')), findsOneWidget, ); }, ); testWidgets( 'tambien existe un selector para la emisora de respaldo (S2-R9)', (tester) async { await _abrirEditor(tester); await tester.ensureVisible( find.byKey(const ValueKey('alarm-fallback-station-field')), ); await tester.pumpAndSettle(); await tester.tap( find.byKey(const ValueKey('alarm-fallback-station-field')), ); await tester.pumpAndSettle(); expect(find.byType(SearchBar), findsOneWidget); }, ); testWidgets('permite configurar la duracion del snooze (S2-R10)', ( tester, ) async { final entorno = await _abrirEditor(tester); await tester.ensureVisible(find.text(l10n.alarmSnoozeDurationTitle)); await tester.pumpAndSettle(); expect(find.byType(SegmentedButton), findsWidgets); await tester.tap(find.text(l10n.alarmSnoozeOptionLabel(10))); await tester.pumpAndSettle(); await tester.ensureVisible(find.text(l10n.saveAlarmAction)); await tester.pumpAndSettle(); await tester.tap(find.text(l10n.saveAlarmAction)); await tester.pumpAndSettle(); expect(entorno.estadoAlarmas.alarmas.single.snoozeMinutos, 10); }); testWidgets('el slider de volumen permite bajar hasta 0.0 (S2-R11)', ( tester, ) async { await _abrirEditor(tester); final sliders = tester.widgetList(find.byType(Slider)); final volumen = sliders.firstWhere((slider) => slider.max == 1.0); expect(volumen.min, 0.0); }); }