feat(connectivity): restyle offline and reconnect banners

Tint the mini player's reconnecting/error sub-states with the
offlineAccent token (added in WU1, unused until now): the status
label, the reconnect spinner, and the error retry icon now read as
visually distinct "connectivity trouble" states instead of blending
into the ordinary loading/paused look. Plain buffering keeps the
default colour, confirmed by a dedicated regression test.

Verify-first gate (task 16.1): ControladorReconexion.intentos exists,
but ServicioAudio never surfaces it past a debug log line, and its
estadoStream only carries the EstadoReproduccion enum. Adding an
attempt-count label would require a getter/stream on
servicio_audio.dart, one of the files this change must keep at an
empty diff against main. Ship the restyle without the counter, per
the risk register's own fallback.

WU16.
This commit is contained in:
2026-07-29 14:43:34 +02:00
parent dc21732027
commit 862197ab48
3 changed files with 168 additions and 17 deletions
+117
View File
@@ -3,6 +3,7 @@ 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/servicios/servicio_audio.dart';
import 'package:pluriwave/tema/pluriwave_tokens.dart';
import 'package:pluriwave/widgets/mini_reproductor.dart';
import 'package:provider/provider.dart';
@@ -93,4 +94,120 @@ void main() {
expect(find.byType(AlertDialog), findsNothing);
expect(find.byIcon(Icons.refresh_rounded), findsOneWidget);
});
// WU16: the offline/reconnect banner restyle. `offlineAccent` (added in
// WU1, previously unused by any screen) tints the connectivity-trouble
// sub-states so they read as visually distinct from ordinary loading.
testWidgets(
'reconnecting state tints the spinner and status label with the offline accent',
(tester) async {
final audio = FakeServicioAudio();
final estado = _estadoRadio(audio);
addTearDown(estado.dispose);
await audio.reproducir(emisoraDemo(uuid: 'r1', nombre: 'Radio Uno'));
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: MaterialApp(
locale: const Locale('es'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: const Scaffold(body: MiniReproductor()),
),
),
);
await tester.pumpAndSettle();
audio.emitirEstado(EstadoReproduccion.reconectando);
await tester.pump();
await tester.pump();
expect(
tester
.widget<CircularProgressIndicator>(
find.byType(CircularProgressIndicator),
)
.color,
PluriWaveTokens.dark.offlineAccent,
);
expect(
tester.widget<Text>(find.text('Reconectando...')).style?.color,
PluriWaveTokens.dark.offlineAccent,
);
},
);
testWidgets(
'plain buffering keeps the default spinner colour, not the offline accent',
(tester) async {
final audio = FakeServicioAudio();
final estado = _estadoRadio(audio);
addTearDown(estado.dispose);
await audio.reproducir(emisoraDemo(uuid: 'r1', nombre: 'Radio Uno'));
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: MaterialApp(
locale: const Locale('es'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: const Scaffold(body: MiniReproductor()),
),
),
);
await tester.pumpAndSettle();
audio.emitirEstado(EstadoReproduccion.cargando);
await tester.pump();
await tester.pump();
expect(
tester
.widget<CircularProgressIndicator>(
find.byType(CircularProgressIndicator),
)
.color,
isNull,
reason: 'cargando is not a connectivity problem, keep the default',
);
},
);
testWidgets(
'error state tints the retry icon and status label with the offline accent',
(tester) async {
final audio = FakeServicioAudio();
final estado = _estadoRadio(audio);
addTearDown(estado.dispose);
await audio.reproducir(emisoraDemo(uuid: 'r1', nombre: 'Radio Uno'));
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: MaterialApp(
locale: const Locale('es'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: const Scaffold(body: MiniReproductor()),
),
),
);
await tester.pumpAndSettle();
audio.emitirEstado(EstadoReproduccion.error);
await tester.pump();
await tester.pump();
expect(
tester.widget<Icon>(find.byIcon(Icons.refresh_rounded)).color,
PluriWaveTokens.dark.offlineAccent,
);
expect(
tester.widget<Text>(find.text('Error de conexión')).style?.color,
PluriWaveTokens.dark.offlineAccent,
);
},
);
}