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/modelos/emisora.dart'; import 'package:pluriwave/pantallas/pantalla_alarmas.dart'; import 'package:pluriwave/servicios/servicio_alarmas.dart'; import 'package:pluriwave/tema/pluriwave_tokens.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../helpers/fakes.dart'; import '../helpers/fakes_alarmas.dart'; /// Tier 5 visual-fidelity closeout (items 7.1-7.4, audit id 2521) -- /// screens 4-14 batch. void main() { setUp(() { SharedPreferences.setMockInitialValues({}); }); Future montar( WidgetTester tester, { required EstadoAlarmas estadoAlarmas, EstadoRadio? radio, }) async { tester.view.physicalSize = const Size(1440, 3200); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final estadoRadio = radio ?? EstadoRadio( audio: FakeServicioAudio(), favoritos: FakeServicioFavoritos(), radio: FakeServicioRadio(), servicioEcualizador: FakeServicioEcualizador(), servicioGrabacion: FakeServicioGrabacionRadioInactiva(), iniciarAutomaticamente: false, ); if (radio == null) addTearDown(estadoRadio.dispose); await tester.pumpWidget( MultiProvider( providers: [ ChangeNotifierProvider.value(value: estadoRadio), ChangeNotifierProvider.value(value: estadoAlarmas), ], child: MaterialApp( locale: const Locale('en'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: const Scaffold(body: PantallaAlarmas()), ), ), ); await tester.pump(); await tester.pump(const Duration(milliseconds: 100)); } EstadoAlarmas crearEstado() { final android = FakePuertoAlarmasAndroid(); final estado = EstadoAlarmas( servicio: ServicioAlarmas(reloj: () => DateTime(2026, 6, 11, 6, 0)), android: android, iniciarAutomaticamente: false, ); return estado; } group('visual fidelity (audit 7.1): "New" action', () { testWidgets('is a solid brand-teal pill with an add glyph, not the tonal ' 'auto_awesome button (t4:325)', (tester) async { final estado = crearEstado(); addTearDown(estado.dispose); await montar(tester, estadoAlarmas: estado); expect(find.byIcon(Icons.auto_awesome_rounded), findsNothing); expect(find.byIcon(Icons.add_rounded), findsOneWidget); final l10n = lookupAppLocalizations(const Locale('en')); final boton = tester.widget(find.byType(FilledButton)); final resuelto = boton.style?.backgroundColor?.resolve({}); expect(resuelto, PluriWaveTokens.brand); expect(find.text(l10n.createAlarmAction), findsOneWidget); }); }); group('visual fidelity (audit 7.2): next-alarm banner', () { testWidgets( 'is warmCoral-tinted and the Skip chip sits BESIDE the text, not ' 'below it (t4:327-331)', (tester) async { final estado = crearEstado(); addTearDown(estado.dispose); await estado.guardarAlarma( const AlarmaMusical( id: 'proxima', nombre: 'Despertar', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: [], ), ); await montar(tester, estadoAlarmas: estado); final l10n = lookupAppLocalizations(const Locale('en')); final banner = tester.widget( find.byKey(const ValueKey('next-alarm-banner')), ); final decoration = banner.decoration as BoxDecoration; expect( decoration.color, const Color(0xFFF4B860).withValues(alpha: 0.13), ); final skipY = tester.getCenter(find.text(l10n.alarmHeroSkipAction)).dy; final titleY = tester.getCenter(find.byIcon(Icons.alarm_on)).dy; expect( (skipY - titleY).abs() < 4, isTrue, reason: 'the Skip chip sits on the SAME row as the icon/text', ); }, ); }); group('visual fidelity (audit 7.3): vacation row date pill', () { testWidgets('shows the upcoming range as a "d-d MON" pill (t4:332)', ( tester, ) async { final estado = crearEstado(); addTearDown(estado.dispose); await estado.crearRangoVacaciones( estado.servicio.crearRangoVacaciones( inicio: DateTime(2026, 8, 4), fin: DateTime(2026, 8, 18), nombre: 'Summer', ), ); await montar(tester, estadoAlarmas: estado); expect(find.text('4–18 AUG'), findsOneWidget); }); }); group('visual fidelity (audit 7.4): alarm card', () { testWidgets( 'shows a recurrence label next to the time and a station icon next ' 'to the station name (t4:337-345)', (tester) async { final estado = crearEstado(); addTearDown(estado.dispose); await estado.guardarAlarma( const AlarmaMusical( id: 'tarjeta', nombre: 'Despertar', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: [], emisora: Emisora( uuid: 'e1', nombre: 'Radio Uno', url: 'https://radio.example/stream', ), ), ); await montar(tester, estadoAlarmas: estado); final l10n = lookupAppLocalizations(const Locale('en')); expect( find.descendant( of: find.byKey(const ValueKey('tarjeta-alarma-tarjeta')), matching: find.text(l10n.dailyOption), ), findsOneWidget, ); expect( find.descendant( of: find.byKey(const ValueKey('tarjeta-alarma-tarjeta')), matching: find.byKey(const ValueKey('tarjeta-alarma-arte')), ), findsOneWidget, ); }, ); }); }