TarjetaEmisora had the only good fallback for a station with no artwork -- a deterministic pick from 4 bundled illustrations with a gradient/glyph last resort. FilaEmisoraPlana's flat rows, the Escuchar hero, the "Tus emisoras" grid cell, the mini player and the full player each had their own, separate, flat primaryContainer square instead. Extract the good fallback into PluriStationArtFallback and use it from every one of those call sites. The selection formula (asset order, codeUnits-sum modulo) is preserved exactly, since navegacion_auto.dart mirrors the same formula independently for Android Auto's own drawable rotation.
117 lines
3.8 KiB
Dart
117 lines
3.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:pluriwave/estado/estado_radio.dart';
|
|
import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
|
import 'package:pluriwave/widgets/pluri_station_art_fallback.dart';
|
|
import 'package:pluriwave/widgets/tarjeta_emisora.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../helpers/fakes.dart';
|
|
|
|
/// S6 (Tier 1 visual fidelity): the prototype's station thumbnail is a
|
|
/// plain 44-48px square, radius 11/12, with NO ring or glow (t4 lines 84
|
|
/// `44px/radius 11`, 175 `46px/radius 12`, 227 `48px/radius 12`, 302
|
|
/// `48px/radius 12`, 614 `44px/radius 12`). The compact row variant used to
|
|
/// paint a 58x58 circle with a `SweepGradient` ring and a 22-blur glow
|
|
/// behind a 50x50 `ClipRRect(18)`.
|
|
void main() {
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
});
|
|
|
|
Widget host(Widget child) {
|
|
final estado = EstadoRadio(
|
|
audio: FakeServicioAudio(),
|
|
favoritos: FakeServicioFavoritos(),
|
|
radio: FakeServicioRadio(),
|
|
servicioEcualizador: FakeServicioEcualizador(),
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(estado.dispose);
|
|
return ChangeNotifierProvider<EstadoRadio>.value(
|
|
value: estado,
|
|
child: MaterialApp(
|
|
locale: const Locale('en'),
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
home: Scaffold(body: Center(child: child)),
|
|
),
|
|
);
|
|
}
|
|
|
|
bool isRingContainer(Widget w) =>
|
|
w is Container &&
|
|
w.decoration is BoxDecoration &&
|
|
(w.decoration as BoxDecoration).shape == BoxShape.circle &&
|
|
(w.decoration as BoxDecoration).gradient is SweepGradient;
|
|
|
|
testWidgets('esCompacta (row) thumbnail is a 48x48 square, radius 12, no '
|
|
'ring/glow', (tester) async {
|
|
await tester.pumpWidget(
|
|
host(
|
|
SizedBox(
|
|
width: 320,
|
|
child: TarjetaEmisora(
|
|
emisora: emisoraDemo(uuid: 'row', nombre: 'Row FM'),
|
|
esCompacta: true,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pump();
|
|
|
|
expect(find.byWidgetPredicate(isRingContainer), findsNothing);
|
|
|
|
// Not `.first` — the outer PluriGlassSurface also renders its own
|
|
// ClipRRect (radius 14, the row surface radius from S4), so the
|
|
// thumbnail's own clip must be found by its distinct radius instead of
|
|
// tree order.
|
|
final thumbnailClip = find.byWidgetPredicate(
|
|
(w) => w is ClipRRect && w.borderRadius == BorderRadius.circular(12),
|
|
);
|
|
expect(thumbnailClip, findsOneWidget);
|
|
expect(tester.getSize(thumbnailClip), const Size(48, 48));
|
|
});
|
|
|
|
testWidgets(
|
|
'issue 6 (feedback-pruebas): a station with no favicon renders via the '
|
|
'shared PluriStationArtFallback (extracted from this exact fallback)',
|
|
(tester) async {
|
|
await tester.pumpWidget(
|
|
host(
|
|
TarjetaEmisora(
|
|
emisora: emisoraDemo(uuid: 'sin-arte', nombre: 'Sin Arte'),
|
|
esCompacta: true,
|
|
),
|
|
),
|
|
);
|
|
await tester.pump();
|
|
|
|
expect(find.byType(PluriStationArtFallback), findsOneWidget);
|
|
},
|
|
);
|
|
|
|
testWidgets('esCompacta shimmer placeholder is a square block, not a '
|
|
'circle', (tester) async {
|
|
await tester.pumpWidget(
|
|
host(const TarjetaEmisoraShimmer(esCompacta: true)),
|
|
);
|
|
await tester.pump();
|
|
|
|
expect(find.byWidgetPredicate(isRingContainer), findsNothing);
|
|
final circles = tester
|
|
.widgetList<Container>(find.byType(Container))
|
|
.where(
|
|
(c) =>
|
|
c.decoration is BoxDecoration &&
|
|
(c.decoration as BoxDecoration).shape == BoxShape.circle,
|
|
);
|
|
expect(
|
|
circles,
|
|
isEmpty,
|
|
reason: 'the real thumbnail is square now — its shimmer must match',
|
|
);
|
|
});
|
|
}
|