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_diagnostico_alarmas.dart'; import 'package:pluriwave/servicios/servicio_alarmas.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../helpers/fakes_alarmas.dart'; final _l10n = lookupAppLocalizations(const Locale('en')); Future _crearEstado({ required FakePuertoAlarmasAndroid android, List alarmas = const [], bool cargarDiagnostico = true, }) async { final estado = EstadoAlarmas( esPremium: () => true, servicio: ServicioAlarmas(), android: android, iniciarAutomaticamente: false, ); for (final alarma in alarmas) { await estado.guardarAlarma(alarma); } if (cargarDiagnostico) { await estado.cargarDiagnostico(); } return estado; } Widget _buildScreen(EstadoAlarmas estado) { return ChangeNotifierProvider.value( value: estado, child: MaterialApp( locale: const Locale('en'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: const PantallaDiagnosticoAlarmas(), ), ); } Future _montarPantalla(WidgetTester tester, EstadoAlarmas estado) async { tester.view.physicalSize = const Size(1440, 3200); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); await tester.pumpWidget(_buildScreen(estado)); await tester.pumpAndSettle(); } AlarmaMusical _alarmaActiva() => const AlarmaMusical( id: 'a1', nombre: 'Despertar', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: [], ); void main() { setUp(() { SharedPreferences.setMockInitialValues({}); }); testWidgets( 'caso todo OK: no muestra ningun boton "Fix" ni el estado de atencion, ' 'y sin fabricante conocido no muestra la guia de autostart', (tester) async { final android = FakePuertoAlarmasAndroid() ..fabricante = 'Google' // A fresh fake defaults alarmasNativasPendientes to 0, which // WOULD read as needs-attention once an alarm is active (that // combination is exactly the diagnostic signal this screen // exists to surface) -- give it a registered count so this // specific scenario is genuinely all-OK. ..alarmasNativasPendientes = 1; final estado = await _crearEstado( android: android, alarmas: [_alarmaActiva()], ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); expect(find.text(_l10n.androidReliabilityTitle), findsOneWidget); expect(find.text(_l10n.alarmDiagnosticsFixAction), findsNothing); expect( find.text(_l10n.alarmDiagnosticsNeedsAttentionStatus), findsNothing, ); expect(find.text(_l10n.alarmDiagnosticsAutostartTitle), findsNothing); expect(find.text('Google'), findsOneWidget); expect(find.text('35'), findsOneWidget); }, ); testWidgets( 'alarmas exactas en atencion: muestra el boton Fix y lo invoca via ' 'solicitarPermisoAlarmasExactas', (tester) async { final android = FakePuertoAlarmasAndroid() ..fabricante = 'Google' ..puedeProgramarExactas = false; final estado = await _crearEstado( android: android, alarmas: [_alarmaActiva()], ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); expect(find.text(_l10n.alarmDiagnosticsExactAlarmsTitle), findsOneWidget); expect(find.text(_l10n.alarmDiagnosticsFixAction), findsOneWidget); // guardarAlarma's own onboarding request // (EstadoAlarmas._solicitarPermisosNecesariosParaAlarma) already fires // once for this same failing field before the screen even mounts, so // the assertion checks the DELTA the button tap itself caused. final antes = android.solicitudesPermisoAlarmasExactas; await tester.tap(find.text(_l10n.alarmDiagnosticsFixAction)); await tester.pumpAndSettle(); expect(android.solicitudesPermisoAlarmasExactas, antes + 1); }, ); testWidgets('notificaciones en atencion: el boton Fix llama a ' 'abrirConfiguracionNotificaciones (deep link a Settings, no el permiso ' 'runtime)', (tester) async { final android = FakePuertoAlarmasAndroid() ..fabricante = 'Google' ..notificacionesPermitidas = false; final estado = await _crearEstado( android: android, alarmas: [_alarmaActiva()], ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); await tester.tap(find.text(_l10n.alarmDiagnosticsFixAction)); await tester.pumpAndSettle(); expect(android.aperturasConfiguracionNotificaciones, 1); }); testWidgets('pantalla completa en atencion: el boton Fix llama a ' 'solicitarPermisoPantallaCompleta', (tester) async { final android = FakePuertoAlarmasAndroid() ..fabricante = 'Google' ..puedeUsarPantallaCompleta = false; final estado = await _crearEstado( android: android, alarmas: [_alarmaActiva()], ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); // Same delta reasoning as the exact-alarms test above: the alarm's own // onboarding request already fired once for this field before mount. final antes = android.solicitudesPermisoPantallaCompleta; await tester.tap(find.text(_l10n.alarmDiagnosticsFixAction)); await tester.pumpAndSettle(); expect(android.solicitudesPermisoPantallaCompleta, antes + 1); }); testWidgets('optimizacion de bateria en atencion: el boton Fix llama a ' 'solicitarExencionBateria', (tester) async { final android = FakePuertoAlarmasAndroid() ..fabricante = 'Google' ..ignoraOptimizacionBateria = false; final estado = await _crearEstado( android: android, alarmas: [_alarmaActiva()], ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); // Same delta reasoning as the exact-alarms test above: guardarAlarma's // own onboarding request already fired once for this field before the // screen mounts. final antes = android.solicitudesExencionBateria; await tester.tap(find.text(_l10n.alarmDiagnosticsFixAction)); await tester.pumpAndSettle(); expect(android.solicitudesExencionBateria, antes + 1); }); testWidgets( 'accion de sistema que falla (ROM sin esa pantalla) muestra un aviso ' 'en vez de fallar en silencio', (tester) async { final android = FakePuertoAlarmasAndroid() ..fabricante = 'Google' ..puedeProgramarExactas = false ..fallaAccionSistema = true; final estado = await _crearEstado( android: android, alarmas: [_alarmaActiva()], ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); await tester.tap(find.text(_l10n.alarmDiagnosticsFixAction)); await tester.pump(); await tester.pump(const Duration(milliseconds: 300)); expect( find.text(_l10n.alarmDiagnosticsIntentUnavailable), findsOneWidget, ); }, ); testWidgets( 'alarmas nativas pendientes: con una alarma activa y conteo 0 muestra ' 'el aviso de atencion (la senal mas diagnostica del reporte)', (tester) async { final android = FakePuertoAlarmasAndroid() ..fabricante = 'Google' ..alarmasNativasPendientes = 0; final estado = await _crearEstado( android: android, alarmas: [_alarmaActiva()], ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); expect( find.text(_l10n.alarmDiagnosticsNativeCountValue(0)), findsOneWidget, ); expect( find.text(_l10n.alarmDiagnosticsNativeCountAttentionHint), findsOneWidget, ); }, ); testWidgets( 'alarmas nativas pendientes: con al menos una registrada no muestra ' 'el aviso de atencion', (tester) async { final android = FakePuertoAlarmasAndroid() ..fabricante = 'Google' ..alarmasNativasPendientes = 2; final estado = await _crearEstado( android: android, alarmas: [_alarmaActiva()], ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); expect( find.text(_l10n.alarmDiagnosticsNativeCountValue(2)), findsOneWidget, ); expect( find.text(_l10n.alarmDiagnosticsNativeCountAttentionHint), findsNothing, ); }, ); testWidgets('fabricante Xiaomi muestra la guia de autostart con el nombre ' 'interpolado', (tester) async { final android = FakePuertoAlarmasAndroid()..fabricante = 'Xiaomi'; final estado = await _crearEstado( android: android, alarmas: [_alarmaActiva()], ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); expect(find.text(_l10n.alarmDiagnosticsAutostartTitle), findsOneWidget); expect( find.text(_l10n.alarmDiagnosticsAutostartBody('Xiaomi')), findsOneWidget, ); }); testWidgets( 'diagnostico aun no disponible (null) no falla y muestra un aviso en ' 'vez de romper la pantalla', (tester) async { // No alarms saved either: guardarAlarma() itself populates // _diagnostico as a side effect of its own onboarding permission // check, so reaching a genuinely null diagnostic requires an // EstadoAlarmas that never called guardarAlarma or cargarDiagnostico. final android = FakePuertoAlarmasAndroid(); final estado = await _crearEstado( android: android, cargarDiagnostico: false, ); addTearDown(estado.dispose); addTearDown(android.dispose); await _montarPantalla(tester, estado); expect(find.text(_l10n.alarmDiagnosticsUnavailableHint), findsOneWidget); }, ); }