fix(favoritos,grabaciones): replace glass cards with flat rows
Item 23 / audit 4.3, 12.4 (t4:226-232, 616-619): Favoritos and Grabaciones rows were full glass cards / ListTiles with two stacked buttons and no artwork slot. Replace with flat, background-less rows via a new shared FilaEmisoraPlana widget (square art, name+meta, a circular play affordance) plus a bespoke Grabaciones row (44x12 placeholder art -- recordings carry no per-station favicon, so this is a themed fallback, not invented artwork). Favoritos keeps "Move to list" / "Remove from favorites" behind an overflow menu (same underlying methods, unchanged) instead of two always-visible buttons, since dropping either would be a functional regression the prototype's own row doesn't have to solve for. Also 12.1 (t4:610): the Grabaciones header action is folder_open, not a generic gear.
This commit is contained in:
@@ -6,7 +6,9 @@ import 'package:pluriwave/estado/estado_radio.dart';
|
||||
import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
||||
import 'package:pluriwave/pantallas/ajustes/pantalla_ajustes_grupos_favoritos.dart';
|
||||
import 'package:pluriwave/pantallas/pantalla_favoritos.dart';
|
||||
import 'package:pluriwave/widgets/fila_emisora_plana.dart';
|
||||
import 'package:pluriwave/widgets/pluri_push_scaffold.dart';
|
||||
import 'package:pluriwave/widgets/tarjeta_emisora.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@@ -381,6 +383,90 @@ void main() {
|
||||
|
||||
expect(find.widgetWithText(TextFormField, 'Name *'), findsOneWidget);
|
||||
});
|
||||
|
||||
// Item 23 / audit 4.3 (t4:226-232): flat, background-less rows with a
|
||||
// square thumbnail and a circular play affordance, replacing the full
|
||||
// glass TarjetaEmisora card + two stacked filledTonal buttons.
|
||||
group('Item 23 -- flat rows (audit 4.3)', () {
|
||||
testWidgets(
|
||||
'each row is a flat FilaEmisoraPlana, not the full glass card',
|
||||
(tester) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstadoConFavoritos();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.byType(FilaEmisoraPlana), findsNWidgets(3));
|
||||
expect(
|
||||
find.byType(TarjetaEmisora),
|
||||
findsNothing,
|
||||
reason: 'audit 4.3 replaces the full glass card with a flat row',
|
||||
);
|
||||
expect(
|
||||
find.byIcon(Icons.drag_indicator_rounded),
|
||||
findsNWidgets(3),
|
||||
reason: 't4:227 drag_indicator, not drag_handle',
|
||||
);
|
||||
expect(find.byIcon(Icons.drag_handle_rounded), findsNothing);
|
||||
expect(
|
||||
find.byType(BotonReproducirCircular),
|
||||
findsNWidgets(3),
|
||||
reason: 't4:230 a circular play affordance per row',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'the overflow menu offers exactly "Move to list" and "Remove from '
|
||||
'favorites"',
|
||||
(tester) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstadoConFavoritos();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.more_vert_rounded).first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Move to list'), findsOneWidget);
|
||||
expect(find.text('Remove from favorites'), findsOneWidget);
|
||||
expect(find.byType(PopupMenuItem<String>), findsNWidgets(2));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'"Remove from favorites" still calls the same removal path as before',
|
||||
(tester) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstadoConFavoritos();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
// Invoke the callback directly rather than opening the overlay and
|
||||
// tapping its rendered item by screen position: leaving that modal
|
||||
// route open while the tap it triggers removes and unmounts the
|
||||
// very row that anchors it is a real hang hazard, confirmed while
|
||||
// developing this test (multi-minute stall, same class of risk as
|
||||
// the project's other documented `pumpAndSettle` traps).
|
||||
final boton = tester.widget<PopupMenuButton<String>>(
|
||||
find.byType(PopupMenuButton<String>).first,
|
||||
);
|
||||
boton.onSelected!('remove');
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.text('Station A'), findsNothing);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void setLargeSurface(WidgetTester tester) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
||||
import 'package:pluriwave/modelos/archivo_grabacion.dart';
|
||||
import 'package:pluriwave/pantallas/pantalla_grabaciones.dart';
|
||||
import 'package:pluriwave/servicios/servicio_grabacion_radio.dart';
|
||||
import 'package:pluriwave/widgets/pluri_glass_surface.dart';
|
||||
import 'package:pluriwave/widgets/pluri_push_scaffold.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@@ -137,7 +138,9 @@ void main() {
|
||||
);
|
||||
await pumpStable(tester);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.settings_outlined));
|
||||
// Audit 12.1 (t4:610): the header action icon is `folder_open`, not
|
||||
// a generic gear.
|
||||
await tester.tap(find.byIcon(Icons.folder_open_rounded));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.byType(PluriPushScaffold), findsNWidgets(2));
|
||||
@@ -194,6 +197,51 @@ void main() {
|
||||
expect(find.byIcon(Icons.play_circle_fill_rounded), findsNWidgets(3));
|
||||
});
|
||||
|
||||
// Item 23 / audit 12.4 (t4:616-619): flat, background-less rows with a
|
||||
// 44x44/radius-12 thumbnail placeholder -- replacing the PluriGlassSurface
|
||||
// + ListTile card, which had no artwork slot at all.
|
||||
testWidgets(
|
||||
'rows are flat -- no ListTile, no per-row PluriGlassSurface -- with a '
|
||||
'44x44/radius-12 thumbnail placeholder',
|
||||
(tester) async {
|
||||
final estado = EstadoGrabacion(
|
||||
servicio: _FakeServicioGrabacionConArchivos([
|
||||
fijaA,
|
||||
fijaB,
|
||||
], maxBytesFijo: 200 * 1024 * 1024),
|
||||
);
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildScreen(
|
||||
estado: estado,
|
||||
reproductor: _ReproductorGrabacionesFake(const {}),
|
||||
),
|
||||
);
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(
|
||||
find.byType(ListTile),
|
||||
findsNothing,
|
||||
reason: 'audit 12.4 replaces the ListTile row with a flat Row',
|
||||
);
|
||||
expect(
|
||||
find.byType(PluriGlassSurface),
|
||||
findsOneWidget,
|
||||
reason: 'only the storage card keeps a surface -- rows do not',
|
||||
);
|
||||
|
||||
final miniaturas = find.byKey(const ValueKey('fila-grabacion-arte'));
|
||||
expect(miniaturas, findsNWidgets(2));
|
||||
expect(tester.getSize(miniaturas.first), const Size(44, 44));
|
||||
final clip = tester.widget<ClipRRect>(miniaturas.first);
|
||||
expect(
|
||||
(clip.borderRadius as BorderRadius).topLeft,
|
||||
const Radius.circular(12),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('15.2-B: empty folder renders an empty state, not an error', (
|
||||
tester,
|
||||
) async {
|
||||
|
||||
Reference in New Issue
Block a user