fix(paises,buscar): tappable ISO rows and a reconnect card

Item 24 / audit 5.1-5.3, 5.5 (t4:255-269): Paises' "Tus idiomas" was
a Wrap of non-interactive Chips, and the full list was a plain
ListTile with no ISO column and no onTap. Both now share one tappable
row (ISO code, name, station count, chevron); the first "Tus idiomas"
row gets the prototype's teal-tinted highlight. The one production
call site wires the tap to filter Buscar by that country's code and
pop back -- EstadoBusqueda.buscar(pais: ...) already accepts any ISO
alpha-2 code, not just the ~10 presets in the filter sheet.

Item 25 / audit 13.2 (t4:643-646): a reconnect card (rotating ring,
station name, "Reconectando...", a stop affordance) replaces the
complete absence of any reconnect signal outside a word in the mini
player. Ships WITHOUT the prototype's attempt counter: the only live
ControladorReconexion instance is a private field of
PluriWaveAudioHandler inside servicio_audio.dart, a file this task
requires stay byte-identical to main, and nothing else re-exposes it.
Reconstructing a count from estadoStream's reconectando emissions
would not be faithful (the stream can emit it many times per actual
backoff attempt), so that was deliberately not attempted.
This commit is contained in:
2026-07-30 11:48:25 +02:00
parent 955682271c
commit 3303bd387c
4 changed files with 676 additions and 38 deletions
+124 -4
View File
@@ -66,9 +66,12 @@ void main() {
// Full alphabetical list: all 3 fetched countries render.
expect(find.text('Argentina'), findsOneWidget);
expect(find.text('France'), findsOneWidget);
// 'Spain' renders twice: "Tus idiomas" (locale es -> representative
// country ES) AND the full list below.
// 'France' and 'Spain' each render twice: once in "Tus idiomas" (FR
// and ES both back a supported locale) and once in the full list
// below. Item 24's row now shows the name as its OWN Text (not
// concatenated with the count into one Chip label), so both are
// independently findable in each section.
expect(find.text('France'), findsWidgets);
expect(find.text('Spain'), findsWidgets);
// Each entry's parsed `stationcount` renders via the existing
@@ -77,10 +80,127 @@ void main() {
// as a plain number here, never throwing a cast error.
expect(find.text(l10n.stationsCount(482)), findsWidgets);
expect(find.text(l10n.stationsCount(120)), findsOneWidget);
expect(find.text(l10n.stationsCount(75)), findsOneWidget);
// Also doubled, for the same reason as 'France' above.
expect(find.text(l10n.stationsCount(75)), findsWidgets);
},
);
// Item 24 / audit 5.1-5.3, 5.5 (t4:256-269): tappable ISO rows, not
// non-interactive chips/plain ListTiles.
group('Item 24 -- tappable ISO rows', () {
testWidgets('rows show the ISO code and no longer use Chip or ListTile', (
tester,
) async {
final estado = EstadoBusqueda(
radio: FakeServicioRadio(
paises: const [
PaisRadio(nombre: 'Spain', codigoIso: 'ES', numeroEmisoras: 482),
PaisRadio(
nombre: 'Argentina',
codigoIso: 'AR',
numeroEmisoras: 120,
),
],
),
);
addTearDown(estado.dispose);
await tester.pumpWidget(buildScreen(estado));
await pumpEstable(tester);
expect(
find.byType(Chip),
findsNothing,
reason: 'audit 5.1 replaces the Wrap-of-Chip with tappable rows',
);
expect(
find.byType(ListTile),
findsNothing,
reason:
'audit 5.3 rows need an ISO-code column a plain ListTile '
'has no slot for',
);
expect(find.text('AR'), findsOneWidget);
// 'ES' renders in BOTH the "Tus idiomas" and "Todos" rows.
expect(find.text('ES'), findsWidgets);
expect(find.byIcon(Icons.chevron_right_rounded), findsWidgets);
});
testWidgets('tapping a row invokes onPaisSeleccionado with that country', (
tester,
) async {
final estado = EstadoBusqueda(
radio: FakeServicioRadio(
paises: const [
PaisRadio(
nombre: 'Argentina',
codigoIso: 'AR',
numeroEmisoras: 120,
),
],
),
);
addTearDown(estado.dispose);
PaisRadio? seleccionado;
await tester.pumpWidget(
ChangeNotifierProvider<EstadoBusqueda>.value(
value: estado,
child: MaterialApp(
locale: const Locale('es'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: PantallaPaises(
onPaisSeleccionado: (pais) => seleccionado = pais,
),
),
),
);
await pumpEstable(tester);
await tester.tap(find.text('Argentina'));
await pumpEstable(tester);
expect(seleccionado?.codigoIso, 'AR');
});
testWidgets(
'the first "Tus idiomas" row is highlighted: teal-tinted background, '
'bold name',
(tester) async {
final estado = EstadoBusqueda(
radio: FakeServicioRadio(
paises: const [
PaisRadio(nombre: 'Spain', codigoIso: 'ES', numeroEmisoras: 482),
],
),
);
addTearDown(estado.dispose);
await tester.pumpWidget(buildScreen(estado));
await pumpEstable(tester);
final nombre = tester.widget<Text>(find.text('Spain').first);
expect(nombre.style?.fontWeight, FontWeight.w800);
final decorado = tester.widget<DecoratedBox>(
find
.ancestor(
of: find.text('Spain').first,
matching: find.byType(DecoratedBox),
)
.first,
);
final decoration = decorado.decoration as BoxDecoration;
expect(
decoration.color,
isNot(Colors.transparent),
reason: 't4:256 the first row is teal-tinted, not transparent',
);
},
);
});
testWidgets(
'cargarPaises no se re-dispara si ya hay datos en caché al reconstruir',
(tester) async {