test(ci): stabilize hidden failures
Build & Deploy PluriWave / Análisis de código (push) Failing after 13m34s
Build & Deploy PluriWave / Build APK + AAB release (push) Has been skipped

This commit is contained in:
2026-05-28 23:37:51 +02:00
parent e47c0a88e0
commit cf994757a4
5 changed files with 187 additions and 120 deletions
+18
View File
@@ -0,0 +1,18 @@
[
{
"uuid": "custom-1",
"nombre": "Custom Uno",
"url": "https://stream.demo/radio",
"favicon": null,
"pais": null,
"codigo_pais": null,
"idioma": null,
"tags": null,
"codec": null,
"bitrate": null,
"votes": 0,
"clickcount": 0,
"orden": 0,
"grupo_id": "sin_asignar"
}
]
+1
View File
@@ -0,0 +1 @@
[]
+160 -110
View File
@@ -1,115 +1,135 @@
import 'dart:convert';
import 'dart:async';
import 'dart:io';
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/pantallas/pantalla_favoritos.dart';
import 'package:pluriwave/pantallas/pantalla_inicio.dart';
import 'package:pluriwave/servicios/servicio_grabacion_radio.dart';
import 'package:pluriwave/widgets/tarjeta_emisora.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../helpers/fakes.dart';
void main() {
testWidgets(
'PantallaInicio muestra custom, reproducir usa EstadoRadio y favorito usa flujo existente',
(tester) async {
final audio = FakeServicioAudio();
final favoritos = FakeServicioFavoritos();
final radio = FakeServicioRadio();
final custom = emisoraDemo(uuid: 'custom-1', nombre: 'Custom Uno');
final archivo = await _crearArchivoCustom([custom]);
final estado = EstadoRadio(
audio: audio,
favoritos: favoritos,
radio: radio,
servicioEcualizador: FakeServicioEcualizador(),
resolverArchivoCustom: () async => archivo,
iniciarAutomaticamente: false,
);
await estado.inicializar();
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: const MaterialApp(
home: Scaffold(body: PantallaInicio()),
),
),
);
await tester.pumpAndSettle();
expect(find.text('Custom Uno'), findsOneWidget);
await tester.tap(find.text('Custom Uno'));
await tester.pumpAndSettle();
expect(audio.emisorasReproducidas.map((e) => e.uuid), contains('custom-1'));
expect(radio.ultimoUuidClick, 'custom-1');
final tarjetaCustom = find.ancestor(
of: find.text('Custom Uno'),
matching: find.byType(Card),
);
final botonFavorito = find.descendant(
of: tarjetaCustom.first,
matching: find.byIcon(Icons.favorite_outline_rounded),
);
expect(botonFavorito, findsOneWidget);
await tester.tap(botonFavorito);
await tester.pumpAndSettle();
expect(favoritos.toggleCalls, 1);
expect(await favoritos.esFavorito(custom.uuid), isTrue);
setUp(() {
SharedPreferences.setMockInitialValues({});
});
testWidgets(
'PantallaInicio permite reintentar manualmente tras fallo inicial agotado',
(tester) async {
final radio = FakeServicioRadio(
erroresPopularesPorLlamada: [Exception('sin red')],
popularesPorLlamada: [
const [],
[emisoraDemo(uuid: 'api-1', nombre: 'API Uno')],
],
tendenciasPorLlamada: [
const [],
[emisoraDemo(uuid: 'trend-1', nombre: 'Trend Uno')],
],
);
final estado = EstadoRadio(
audio: FakeServicioAudio(),
favoritos: FakeServicioFavoritos(),
radio: radio,
servicioEcualizador: FakeServicioEcualizador(),
resolverArchivoCustom: _archivoCustomVacio,
iniciarAutomaticamente: false,
);
await estado.inicializar();
'PantallaInicio muestra custom, reproducir usa EstadoRadio y favorito usa flujo existente',
(tester) async {
final audio = FakeServicioAudio();
final favoritos = FakeServicioFavoritos();
final radio = FakeServicioRadio();
final custom = emisoraDemo(uuid: 'custom-1', nombre: 'Custom Uno');
final archivo = await _crearArchivoCustom([custom]);
final estado = EstadoRadio(
audio: audio,
favoritos: favoritos,
radio: radio,
servicioEcualizador: FakeServicioEcualizador(),
servicioGrabacion: FakeServicioGrabacionRadio(),
resolverArchivoCustom: () async => archivo,
iniciarAutomaticamente: false,
);
addTearDown(estado.dispose);
await tester.runAsync(estado.inicializar);
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: const MaterialApp(
home: Scaffold(body: PantallaInicio()),
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: _testApp(const PantallaInicio()),
),
),
);
await tester.pumpAndSettle();
);
await _pumpStableFrame(tester);
expect(find.text('Sin conexión a la API de radio'), findsOneWidget);
expect(find.text('Reintentar'), findsOneWidget);
expect(find.text('Custom Uno'), findsOneWidget);
await tester.tap(find.text('Reintentar'));
await tester.pumpAndSettle();
await tester.ensureVisible(find.text('Custom Uno'));
await _pumpStableFrame(tester);
await tester.tap(find.text('Custom Uno'));
await _pumpStableFrame(tester);
expect(
audio.emisorasReproducidas.map((e) => e.uuid),
contains('custom-1'),
);
expect(radio.ultimoUuidClick, 'custom-1');
expect(radio.obtenerPopularesCalls, 2);
expect(find.text('Sin conexión a la API de radio'), findsNothing);
expect(find.text('API Uno'), findsOneWidget);
});
final tarjetaCustom = find.ancestor(
of: find.text('Custom Uno'),
matching: find.byType(TarjetaEmisora),
);
final botonFavorito =
find
.descendant(of: tarjetaCustom, matching: find.byType(InkWell))
.last;
expect(botonFavorito, findsOneWidget);
testWidgets('PantallaFavoritos muestra custom favorito tras recarga',
(tester) async {
await tester.ensureVisible(botonFavorito);
await _pumpStableFrame(tester);
await tester.tap(botonFavorito);
await _pumpStableFrame(tester);
expect(favoritos.toggleCalls, 1);
expect(await favoritos.esFavorito(custom.uuid), isTrue);
},
);
testWidgets(
'PantallaInicio permite reintentar manualmente tras fallo inicial agotado',
(tester) async {
final radio = FakeServicioRadio(
erroresPopularesPorLlamada: [Exception('sin red')],
popularesPorLlamada: [
const [],
[emisoraDemo(uuid: 'api-1', nombre: 'API Uno')],
],
tendenciasPorLlamada: [
const [],
[emisoraDemo(uuid: 'trend-1', nombre: 'Trend Uno')],
],
);
final estado = EstadoRadio(
audio: FakeServicioAudio(),
favoritos: FakeServicioFavoritos(),
radio: radio,
servicioEcualizador: FakeServicioEcualizador(),
servicioGrabacion: FakeServicioGrabacionRadio(),
resolverArchivoCustom: _archivoCustomVacio,
iniciarAutomaticamente: false,
);
addTearDown(estado.dispose);
await tester.runAsync(estado.inicializar);
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: _testApp(const PantallaInicio()),
),
);
await _pumpStableFrame(tester);
expect(find.text('Sin conexión a la API de radio'), findsOneWidget);
expect(find.text('Reintentar'), findsOneWidget);
await tester.ensureVisible(find.text('Reintentar'));
await _pumpStableFrame(tester);
await tester.tap(find.text('Reintentar'));
await _pumpStableFrame(tester);
expect(radio.obtenerPopularesCalls, 2);
expect(find.text('Sin conexión a la API de radio'), findsNothing);
expect(find.text('API Uno'), findsOneWidget);
},
);
testWidgets('PantallaFavoritos muestra custom favorito tras recarga', (
tester,
) async {
final favoritos = FakeServicioFavoritos();
final custom = emisoraDemo(uuid: 'custom-1', nombre: 'Custom Uno');
final archivo = await _crearArchivoCustom([custom]);
@@ -120,54 +140,84 @@ void main() {
populares: [emisoraDemo(uuid: 'api-1', nombre: 'API Uno')],
),
servicioEcualizador: FakeServicioEcualizador(),
servicioGrabacion: FakeServicioGrabacionRadio(),
resolverArchivoCustom: () async => archivo,
iniciarAutomaticamente: false,
);
await estado.inicializar();
addTearDown(estado.dispose);
await tester.runAsync(estado.inicializar);
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: const MaterialApp(
home: Scaffold(body: PantallaInicio()),
),
child: _testApp(const PantallaInicio()),
),
);
await tester.pumpAndSettle();
await _pumpStableFrame(tester);
await tester.ensureVisible(find.text('Custom Uno'));
await _pumpStableFrame(tester);
final tarjetaCustom = find.ancestor(
of: find.text('Custom Uno'),
matching: find.byType(Card),
);
final botonFavorito = find.descendant(
of: tarjetaCustom.first,
matching: find.byIcon(Icons.favorite_outline_rounded),
matching: find.byType(TarjetaEmisora),
);
final botonFavorito =
find.descendant(of: tarjetaCustom, matching: find.byType(InkWell)).last;
expect(botonFavorito, findsOneWidget);
await tester.ensureVisible(botonFavorito);
await _pumpStableFrame(tester);
await tester.tap(botonFavorito);
await tester.pumpAndSettle();
await _pumpStableFrame(tester);
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: const MaterialApp(
home: Scaffold(body: PantallaFavoritos()),
),
child: _testApp(const PantallaFavoritos()),
),
);
await tester.pumpAndSettle();
await _pumpStableFrame(tester);
expect(await favoritos.esFavorito(custom.uuid), isTrue);
expect(find.text('Custom Uno'), findsOneWidget);
});
}
Widget _testApp(Widget body) {
return MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold(body: body),
);
}
class FakeServicioGrabacionRadio extends ServicioGrabacionRadio {
final _controller = StreamController<EstadoGrabacionRadio>.broadcast();
@override
EstadoGrabacionRadio get estado => const EstadoGrabacionRadio.inactiva();
@override
Stream<EstadoGrabacionRadio> get estadoStream => _controller.stream;
@override
Future<void> inicializar() async {}
@override
Future<void> dispose() => _controller.close();
}
Future<void> _pumpStableFrame(WidgetTester tester) async {
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));
}
Future<File> _crearArchivoCustom(List<dynamic> emisoras) async {
final dir = await Directory.systemTemp.createTemp('pluriwave-test-');
final archivo = File('${dir.path}/emisoras_custom.json');
await archivo.writeAsString(jsonEncode(emisoras.map((e) => e.toMap()).toList()));
return archivo;
final nombre =
emisoras.isEmpty
? 'emisoras_custom_vacio.json'
: 'emisoras_custom_uno.json';
return File('${Directory.current.path}/test/fixtures/$nombre');
}
Future<File> _archivoCustomVacio() async => _crearArchivoCustom(const []);
+2 -2
View File
@@ -63,8 +63,8 @@ void main() {
retryDelay: Duration.zero,
);
expect(
() => servicio.obtenerPopulares(limit: 1),
await expectLater(
servicio.obtenerPopulares(limit: 1),
throwsA(isA<Exception>()),
);
expect(intentos, 2);
+6 -8
View File
@@ -8,8 +8,8 @@ import 'package:pluriwave/widgets/pluri_wave_scaffold.dart';
void main() {
test('PluriWaveTokens.dark mantiene valores base esperados', () {
expect(PluriWaveTokens.dark.deepViolet, const Color(0xFF24123D));
expect(PluriWaveTokens.dark.radiusMd, 16);
expect(PluriWaveTokens.dark.deepViolet, const Color(0xFF07121A));
expect(PluriWaveTokens.dark.radiusMd, 22);
expect(PluriWaveTokens.dark.spacingMd, 16);
});
@@ -30,16 +30,14 @@ void main() {
expect(find.bySemanticsLabel('Buscar emisoras'), findsOneWidget);
});
testWidgets('PluriGlassSurface y PluriWaveScaffold se instancian', (tester) async {
testWidgets('PluriGlassSurface y PluriWaveScaffold se instancian', (
tester,
) async {
await tester.pumpWidget(
MaterialApp(
theme: PluriWaveTheme.dark(),
home: const PluriWaveScaffold(
body: Center(
child: PluriGlassSurface(
child: Text('ok'),
),
),
body: Center(child: PluriGlassSurface(child: Text('ok'))),
),
),
);