import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/estado/estado_busqueda.dart'; import 'package:pluriwave/estado/estado_ecualizador.dart'; import 'package:pluriwave/estado/estado_grabacion.dart'; import 'package:pluriwave/estado/estado_radio.dart'; import 'package:pluriwave/l10n/gen/app_localizations.dart'; import 'package:pluriwave/pantallas/pantalla_buscar.dart'; import 'package:pluriwave/widgets/tarjeta_emisora.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../helpers/fakes.dart'; /// S5-R6: the search loading state uses shimmer placeholders, not a bare /// spinner, to stay consistent with the rest of the app. /// /// WU6 correction: `PantallaBuscar` now also renders a discovery LANDING /// state (task 6.5) that reads `EstadoRadio` directly, so any widget test /// mounting it — this one included — must provide a full `EstadoRadio` /// provider from the start, not just `EstadoBusqueda`. A non-empty query is /// entered first so the screen leaves the landing state and reaches the /// search-results branch this test actually targets. /// /// Bugfix (surfaced while landing audit item 18's "Explorar por" grid): /// on the FIRST pump here (landing state, empty `EstadoRadio`, default /// narrow test viewport, no `_setLargeSurfaceSize`), `_gridEmisoras` /// renders its empty-state branch — `PluriEmptyState`, in a fixed /// `SizedBox(height: 260)`. That widget's own Column overflowed by 10px /// at this width with real ARB copy (`pluri_premium_widgets.dart`, /// fixed by wrapping it in a `SingleChildScrollView` — a no-op whenever /// content already fits, which is every real device width). This test IS /// the end-to-end regression guard for that fix: it renders the exact /// scenario that surfaced it. class _BusquedaCargando extends EstadoBusqueda { _BusquedaCargando() : super(radio: FakeServicioRadio()); @override bool get cargando => true; } void main() { setUp(() { SharedPreferences.setMockInitialValues({}); }); testWidgets('PantallaBuscar muestra shimmer mientras carga', (tester) async { final busqueda = _BusquedaCargando(); addTearDown(busqueda.dispose); final estado = EstadoRadio( audio: FakeServicioAudio(), favoritos: FakeServicioFavoritos(), radio: FakeServicioRadio(), servicioEcualizador: FakeServicioEcualizador(), servicioGrabacion: FakeServicioGrabacionRadio(), resolverArchivoCustom: () async => throw UnimplementedError(), iniciarAutomaticamente: false, ); addTearDown(estado.dispose); await tester.pumpWidget( MultiProvider( providers: [ ChangeNotifierProvider.value(value: estado), ListenableProvider.value( value: estado.ecualizador, ), ListenableProvider.value(value: estado.grabacion), // Overrides EstadoRadio's own (unused here) EstadoBusqueda with a // fake pinned to cargando: true. ListenableProvider.value(value: busqueda), ], child: MaterialApp( locale: const Locale('es'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: const Scaffold(body: PantallaBuscar()), ), ), ); await tester.pump(); // Still the landing state — no query entered yet. expect(find.byType(TarjetaEmisoraShimmer), findsNothing); await tester.enterText(find.byType(SearchBar), 'jazz'); await tester.pump(); expect(find.byType(TarjetaEmisoraShimmer), findsWidgets); expect(find.byType(CircularProgressIndicator), findsNothing); }); testWidgets('visual fidelity (audit 13.3/13.4): the "BUSCANDO EMISORAS..." ' 'eyebrow renders above the loading rows, spaced 4px apart (t4:649-651)', ( tester, ) async { final busqueda = _BusquedaCargando(); addTearDown(busqueda.dispose); final estado = EstadoRadio( audio: FakeServicioAudio(), favoritos: FakeServicioFavoritos(), radio: FakeServicioRadio(), servicioEcualizador: FakeServicioEcualizador(), servicioGrabacion: FakeServicioGrabacionRadio(), resolverArchivoCustom: () async => throw UnimplementedError(), iniciarAutomaticamente: false, ); addTearDown(estado.dispose); await tester.pumpWidget( MultiProvider( providers: [ ChangeNotifierProvider.value(value: estado), ListenableProvider.value( value: estado.ecualizador, ), ListenableProvider.value(value: estado.grabacion), ListenableProvider.value(value: busqueda), ], child: MaterialApp( locale: const Locale('es'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: const Scaffold(body: PantallaBuscar()), ), ), ); await tester.pump(); await tester.enterText(find.byType(SearchBar), 'jazz'); await tester.pump(); final l10n = AppLocalizations.of( tester.element(find.byType(PantallaBuscar)), ); expect(find.text(l10n.searchLoadingStationsLabel), findsOneWidget); final filas = find.byType(TarjetaEmisoraShimmer); expect(filas, findsWidgets); final primeraAbajo = tester.getBottomLeft(filas.at(0)).dy; final segundaArriba = tester.getTopLeft(filas.at(1)).dy; expect(segundaArriba - primeraAbajo, 4); }); }