import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/intl.dart'; import 'package:pluriwave/estado/estado_alarmas.dart'; import 'package:pluriwave/estado/estado_radio.dart'; import 'package:pluriwave/l10n/formato_fechas.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'; /// S5-R4: short dates must follow the active locale, not a hardcoded /// DD/MM/YYYY pattern. void main() { test('en-US usa el orden mes/día, no el formato fijo DD/MM/YYYY', () { final fecha = DateTime(2026, 6, 11); final resultado = fechaCortaLocalizada('en-US', fecha); expect(resultado, DateFormat.yMd('en-US').format(fecha)); expect(resultado, isNot('11/06/2026')); expect(resultado, '6/11/2026'); }); test('es usa el orden día/mes', () async { await initializeDateFormatting('es'); final fecha = DateTime(2026, 6, 11); expect( fechaCortaLocalizada('es', fecha), DateFormat.yMd('es').format(fecha), ); }); group('WU10 — la seccion Avanzada del editor conserva fecha, respaldo y ' 'sonido', () { final l10n = lookupAppLocalizations(const Locale('es')); setUp(() { SharedPreferences.setMockInitialValues({}); }); Future abrirEditorNuevo(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: 'gamma', nombre: 'Gamma 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 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(); await tester.tap(find.text(l10n.createAlarmAction)); await tester.pumpAndSettle(); return estadoAlarmas; } Future expandirAvanzado(WidgetTester tester) async { await tester.ensureVisible(find.text(l10n.alarmAdvancedSectionTitle)); await tester.tap(find.text(l10n.alarmAdvancedSectionTitle)); await tester.pumpAndSettle(); } testWidgets( 'los circulos de dia de la semana son siempre visibles, incluso ' 'cuando el tipo es Una vez (por defecto en una alarma nueva)', (tester) async { await abrirEditorNuevo(tester); expect(find.text(l10n.weekdayShortMonday), findsOneWidget); expect(find.text(l10n.weekdayShortSunday), findsOneWidget); }, ); testWidgets( 'permite crear una alarma de fecha unica desde la seccion Avanzada ' '(colapsada por defecto)', (tester) async { final estadoAlarmas = await abrirEditorNuevo(tester); // A brand-new alarm already defaults to "one time" — the date field // is reachable as soon as Advanced is expanded, no mode switch // needed first. await expandirAvanzado(tester); await tester.ensureVisible(find.text(l10n.dateField)); await tester.tap(find.text(l10n.dateField)); await tester.pumpAndSettle(); final okLabel = MaterialLocalizations.of( tester.element(find.text(l10n.dateField)), ).okButtonLabel; await tester.tap(find.text(okLabel)); await tester.pumpAndSettle(); await tester.ensureVisible(find.text(l10n.saveAlarmAction)); await tester.tap(find.text(l10n.saveAlarmAction)); await tester.pumpAndSettle(); final alarma = estadoAlarmas.alarmas.single; expect(alarma.tipoProgramacion, TipoProgramacionAlarma.unica); expect(alarma.fechaUnica, isNotNull); expect(alarma.diasSemana, isEmpty); }, ); testWidgets( 'el selector de emisora de respaldo sigue alcanzable desde Avanzado ' 'y persiste (S2-R9)', (tester) async { final estadoAlarmas = await abrirEditorNuevo(tester); await expandirAvanzado(tester); await tester.ensureVisible( find.byKey(const ValueKey('alarm-fallback-station-field')), ); await tester.tap( find.byKey(const ValueKey('alarm-fallback-station-field')), ); await tester.pumpAndSettle(); // Scoped to the just-opened picker sheet's list: the primary // station field auto-selects the sole registered favorite too // (`EstadoRadio.emisoraPreferida` falls back to the first // favorite), so an unscoped `find.text('Gamma FM')` would match // twice — once there, once in this sheet. final lista = find.byType(ListView).last; expect( find.descendant(of: lista, matching: find.text('Gamma FM')), findsOneWidget, ); await tester.tap( find.descendant(of: lista, matching: find.text('Gamma FM')), ); await tester.pumpAndSettle(); await tester.ensureVisible(find.text(l10n.saveAlarmAction)); await tester.tap(find.text(l10n.saveAlarmAction)); await tester.pumpAndSettle(); final alarma = estadoAlarmas.alarmas.single; expect(alarma.emisoraFallback?.nombre, 'Gamma FM'); }, ); testWidgets( 'el dropdown de sonido interno sigue alcanzable desde Avanzado y ' 'persiste', (tester) async { final estadoAlarmas = await abrirEditorNuevo(tester); await expandirAvanzado(tester); await tester.ensureVisible( find.byType(DropdownButtonFormField), ); await tester.tap( find.byType(DropdownButtonFormField), ); await tester.pumpAndSettle(); await tester.tap(find.text(l10n.soundSoftBell).last); await tester.pumpAndSettle(); await tester.ensureVisible(find.text(l10n.saveAlarmAction)); await tester.tap(find.text(l10n.saveAlarmAction)); await tester.pumpAndSettle(); final alarma = estadoAlarmas.alarmas.single; expect(alarma.sonidoInterno, SonidoInternoAlarma.campanaSuave); }, ); }); }