import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/estado/estado_alarmas.dart'; import 'package:pluriwave/estado/estado_busqueda.dart'; import 'package:pluriwave/estado/estado_ecualizador.dart'; import 'package:pluriwave/estado/estado_entitlement.dart'; import 'package:pluriwave/estado/estado_grabacion.dart'; import 'package:pluriwave/estado/estado_idioma.dart'; import 'package:pluriwave/estado/estado_radio.dart'; import 'package:pluriwave/l10n/gen/app_localizations.dart'; import 'package:pluriwave/pantallas/pantalla_ajustes.dart'; import 'package:pluriwave/pantallas/pantalla_alarmas.dart'; import 'package:pluriwave/pantallas/pantalla_buscar.dart'; import 'package:pluriwave/pantallas/pantalla_favoritos.dart'; import 'package:pluriwave/servicios/servicio_alarmas.dart'; import 'package:pluriwave/servicios/servicio_anuncios.dart'; import 'package:pluriwave/widgets/pluri_root_header.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../helpers/fakes.dart'; import '../helpers/fakes_alarmas.dart'; /// S2 (Tier 1 visual fidelity): `PluriScreenHeader` — a 38-radius glass /// hero with an aurora banner, a black scrim, two radial orbs, a 120px /// watermark and a tri-gradient glyph badge — is not in the prototype at /// all (`t4` never draws it). It is retired from all 4 roots that used it; /// each root's ONLY title chrome is now `PluriRootHeader` (S1). Its /// subtitle text was the one thing the hero rendered that nothing else on /// these screens does — its absence is this suite's signal that the hero /// is really gone, since the class itself is deleted and can no longer be /// referenced by type from a test. /// /// Pre-existing project constraint (see `pantalla_ajustes_test.dart`): /// PluriGlassSurface paints a background over ListTile's ink layer, which /// Flutter flags as a warning-level assertion, not a correctness bug. void _suppressListTileInkAssertion() { final original = FlutterError.onError; FlutterError.onError = (details) { if (details.exceptionAsString().contains( 'ListTile background color or ink splashes may be invisible', )) { return; } original?.call(details); }; addTearDown(() => FlutterError.onError = original); } void main() { setUp(() { SharedPreferences.setMockInitialValues({}); }); Future archivoCustomVacio() async => File( '${Directory.current.path}/test/fixtures/emisoras_custom_vacio.json', ); EstadoRadio crearEstadoRadio() => EstadoRadio( esPremium: () => true, audio: FakeServicioAudio(), favoritos: FakeServicioFavoritos(), radio: FakeServicioRadio(), servicioEcualizador: FakeServicioEcualizador(), servicioGrabacion: FakeServicioGrabacionRadioInactiva(), resolverArchivoCustom: archivoCustomVacio, iniciarAutomaticamente: false, ); Widget testApp(EstadoRadio estado, Widget body, {EstadoAlarmas? alarmas}) { return MultiProvider( providers: [ ChangeNotifierProvider.value(value: estado), ListenableProvider.value(value: estado.ecualizador), ListenableProvider.value(value: estado.grabacion), ListenableProvider.value(value: estado.busqueda), ChangeNotifierProvider.value(value: EstadoIdioma()), ChangeNotifierProvider( create: (_) => EstadoEntitlement(prefs: null), ), Provider( create: (_) => ServicioAnuncios(esPremium: () => true), ), if (alarmas != null) ChangeNotifierProvider.value(value: alarmas), ], child: MaterialApp( locale: const Locale('en'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: Scaffold(body: body), ), ); } void setLargeSurface(WidgetTester tester) { tester.view.physicalSize = const Size(1440, 3200); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); } Future pumpStable(WidgetTester tester) async { await tester.pump(); await tester.pump(const Duration(milliseconds: 100)); } testWidgets( 'Buscar: the retired hero subtitle is gone, PluriRootHeader is the ' 'only header, and the filters entry point is still reachable', (tester) async { setLargeSurface(tester); final estado = crearEstadoRadio(); addTearDown(estado.dispose); final l10n = await AppLocalizations.delegate.load(const Locale('en')); await tester.pumpWidget(testApp(estado, const PantallaBuscar())); await pumpStable(tester); expect(find.text(l10n.searchScreenSubtitle), findsNothing); expect(find.byType(PluriRootHeader), findsOneWidget); expect(find.text(l10n.searchFiltersLabel), findsWidgets); }, ); testWidgets('Favoritos (empty state): the retired hero subtitle is gone', ( tester, ) async { setLargeSurface(tester); final estado = crearEstadoRadio(); addTearDown(estado.dispose); final l10n = await AppLocalizations.delegate.load(const Locale('en')); await tester.pumpWidget(testApp(estado, const PantallaFavoritos())); await pumpStable(tester); expect(find.text(l10n.favoritesHeaderSubtitle), findsNothing); expect(find.byType(PluriRootHeader), findsOneWidget); }); testWidgets( 'Alarmas: the retired hero subtitle is gone, and the create-alarm ' 'action is still reachable', (tester) async { setLargeSurface(tester); final estado = crearEstadoRadio(); final alarmas = EstadoAlarmas( esPremium: () => true, servicio: ServicioAlarmas(reloj: () => DateTime(2026, 6, 1, 6, 0)), android: FakePuertoAlarmasAndroid(), iniciarAutomaticamente: false, ); addTearDown(estado.dispose); addTearDown(alarmas.dispose); final l10n = await AppLocalizations.delegate.load(const Locale('en')); await tester.pumpWidget( testApp(estado, const PantallaAlarmas(), alarmas: alarmas), ); await pumpStable(tester); expect(find.text(l10n.alarmScreenSubtitle), findsNothing); expect(find.byType(PluriRootHeader), findsOneWidget); expect(find.text(l10n.createAlarmAction), findsOneWidget); }, ); testWidgets('Ajustes: the retired hero subtitle is gone', (tester) async { _suppressListTileInkAssertion(); setLargeSurface(tester); final estado = crearEstadoRadio(); addTearDown(estado.dispose); final l10n = await AppLocalizations.delegate.load(const Locale('en')); await tester.pumpWidget(testApp(estado, const PantallaAjustes())); await pumpStable(tester); expect(find.text(l10n.settingsSubtitle), findsNothing); expect(find.byType(PluriRootHeader), findsOneWidget); }); }