Files
pluriwave/test/pantallas/ajustes/widgets/fila_ajuste_test.dart
T
FreeTLab b5b9829faa fix(ajustes): group eyebrow outside the card, per-row accent icons
Audit 10.2 (t4:511): GrupoAjustes's eyebrow now sits OUTSIDE the
PluriGlassSurface, at title-tier (20px) padding -- it used to share
the card's own 16px content padding.

Audit 10.5 (t4:514/526): FilaAjuste gains an optional iconColor.
Wired on the two rows the prototype actually accents: AUDIO's
Ecualizador (brand cyan) and STATIONS' Grupos de favoritos (warmCoral).

Item 10.10 (two extra AUDIO rows -- "Calidad de streaming" and
"Reproduccion sin interrupciones") is NOT implemented: neither concept
has any backing state or service anywhere in this app today. Building
them for real means inventing two new persisted settings, and a
genuine streaming-quality preference would plausibly need to touch
the protected servicio_audio.dart to actually affect playback --
out of scope for a visual-fidelity pass. Decorative rows that open
nothing would be dead UI, which is worse than leaving the gap
disclosed.
2026-07-30 16:05:28 +02:00

204 lines
5.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pluriwave/pantallas/ajustes/widgets/fila_ajuste.dart';
import 'package:pluriwave/tema/pluriwave_theme.dart';
import 'package:pluriwave/widgets/pluri_glass_surface.dart';
/// S8 (Tier 1 visual fidelity): the prototype puts a trailing "current
/// value" on nearly every settings row, 13px `rgba(242,247,250,.55)` (t4
/// lines 512-539, 625 — "Voz clara", "Alta", "3 guardados", "Alfabético",
/// "7 · 84 MB", "Español", "Hoy, 08:12", "200 MB"). `FilaAjuste` used to
/// accept only `icon`/`titulo`/`onTap` — no value slot at all.
/// Pre-existing project constraint (see `pantalla_ajustes_test.dart`):
/// `PluriGlassSurface` paints a background over `ListTile`'s ink layer,
/// which Flutter flags as a warning-level assertion, not a correctness bug.
/// Only needed by the `GrupoAjustes` test below — the bare `FilaAjuste`
/// tests above never wrap a `ListTile` inside that surface.
void _suppressListTileInkAssertion() {
final original = FlutterError.onError;
FlutterError.onError = (details) {
if (details.exceptionAsString().contains(
'ListTile background color or ink splashes may be invisible',
)) {
return;
}
original?.call(details);
};
addTearDown(() => FlutterError.onError = original);
}
void main() {
Widget host(Widget child) {
return MaterialApp(
theme: PluriWaveTheme.dark(),
home: Scaffold(body: child),
);
}
testWidgets('renders the trailing value before the chevron when provided', (
tester,
) async {
await tester.pumpWidget(
host(
FilaAjuste(
icon: Icons.language_rounded,
titulo: 'Language',
valor: 'English',
onTap: () {},
),
),
);
expect(find.text('English'), findsOneWidget);
expect(find.byIcon(Icons.chevron_right_rounded), findsOneWidget);
});
testWidgets('renders no trailing value text when valor is omitted '
'(unchanged pre-S8 behaviour)', (tester) async {
await tester.pumpWidget(
host(
FilaAjuste(
icon: Icons.info_outline_rounded,
titulo: 'Info',
onTap: () {},
),
),
);
expect(find.text('Info'), findsOneWidget);
expect(find.byIcon(Icons.chevron_right_rounded), findsOneWidget);
});
testWidgets(
'visual fidelity (audit 10.6/10.8/10.9): leading icon is 21px, the '
'title is 14px, and the chevron is 19px at 40% opacity (t4 lines '
'514-515)',
(tester) async {
await tester.pumpWidget(
host(
FilaAjuste(
icon: Icons.equalizer_rounded,
titulo: 'Ecualizador base',
onTap: () {},
),
),
);
final icon = tester.widget<Icon>(find.byIcon(Icons.equalizer_rounded));
expect(icon.size, 21);
final titulo = tester.widget<Text>(find.text('Ecualizador base'));
expect(titulo.style?.fontSize, 14);
final chevron = tester.widget<Icon>(
find.byIcon(Icons.chevron_right_rounded),
);
expect(chevron.size, 19);
expect(chevron.color, const Color(0xFFF2F7FA).withValues(alpha: 0.4));
},
);
testWidgets(
'visual fidelity (audit S10): GrupoAjustes insets its row divider by '
'47px, not full-bleed (t4 line 516)',
(tester) async {
_suppressListTileInkAssertion();
await tester.pumpWidget(
host(
GrupoAjustes(
titulo: 'AUDIO',
filas: [
FilaAjuste(
icon: Icons.equalizer_rounded,
titulo: 'Uno',
onTap: () {},
),
FilaAjuste(icon: Icons.hd_rounded, titulo: 'Dos', onTap: () {}),
],
),
),
);
final divider = tester.widget<Divider>(find.byType(Divider));
expect(divider.indent, 47);
},
);
testWidgets('visual fidelity (audit 10.5): an explicit iconColor tints the '
'leading icon (t4 lines 514/516/522 -- equalizer/hd/folder accents)', (
tester,
) async {
await tester.pumpWidget(
host(
FilaAjuste(
icon: Icons.equalizer_rounded,
titulo: 'Ecualizador base',
iconColor: const Color(0xFF21D4D9),
onTap: () {},
),
),
);
final icon = tester.widget<Icon>(find.byIcon(Icons.equalizer_rounded));
expect(icon.color, const Color(0xFF21D4D9));
});
testWidgets(
'visual fidelity (audit 10.5): omitting iconColor keeps the default '
'(unchanged pre-10.5 behaviour)',
(tester) async {
await tester.pumpWidget(
host(
FilaAjuste(
icon: Icons.language_rounded,
titulo: 'Idioma',
onTap: () {},
),
),
);
final icon = tester.widget<Icon>(find.byIcon(Icons.language_rounded));
expect(icon.color, isNull);
},
);
testWidgets(
'visual fidelity (audit 10.2): the group eyebrow sits OUTSIDE the '
'card, at title-tier (20px) padding -- not inside the '
'PluriGlassSurface (t4 line 511)',
(tester) async {
_suppressListTileInkAssertion();
await tester.pumpWidget(
host(
GrupoAjustes(
titulo: 'AUDIO',
filas: [
FilaAjuste(
icon: Icons.equalizer_rounded,
titulo: 'Uno',
onTap: () {},
),
],
),
),
);
expect(
find.ancestor(
of: find.text('AUDIO'),
matching: find.byType(PluriGlassSurface),
),
findsNothing,
reason: 'the eyebrow must not be a descendant of the card',
);
final eyebrowLeft = tester.getTopLeft(find.text('AUDIO')).dx;
expect(
eyebrowLeft,
20,
reason: 'S5 title tier -- PluriLayout.titleHorizontal',
);
},
);
}