refactor(ajustes): split remaining Settings sections into pushed screens
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
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/ajustes/pantalla_ajustes_backup.dart';
|
||||
import 'package:pluriwave/widgets/pluri_push_scaffold.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../helpers/fakes.dart';
|
||||
import '../../helpers/fakes_alarmas.dart';
|
||||
|
||||
/// 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.
|
||||
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);
|
||||
}
|
||||
|
||||
/// WU3b task 3b.1: the APLICACIÓN detail screen for "Copia de seguridad"
|
||||
/// renders inside a [PluriPushScaffold] and its moved controls (export /
|
||||
/// import rows) are still present and reachable exactly as they were inside
|
||||
/// the old `_SeccionBackup`.
|
||||
///
|
||||
/// Both rows call into native plugins (`share_plus`, `file_picker`) that
|
||||
/// this suite does not mock, so this file verifies reachability (title and
|
||||
/// both row labels present, with a live `onTap`) rather than tapping through
|
||||
/// the native share/pick flow — the same conservative choice this batch
|
||||
/// makes for any moved control that would otherwise depend on an unmocked
|
||||
/// platform channel.
|
||||
void main() {
|
||||
setUp(() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
});
|
||||
|
||||
Future<File> archivoCustomVacio() async => File(
|
||||
'${Directory.current.path}/test/fixtures/emisoras_custom_vacio.json',
|
||||
);
|
||||
|
||||
Future<EstadoRadio> crearEstado() async {
|
||||
return EstadoRadio(
|
||||
audio: FakeServicioAudio(),
|
||||
favoritos: FakeServicioFavoritos(),
|
||||
radio: FakeServicioRadio(),
|
||||
servicioEcualizador: FakeServicioEcualizador(),
|
||||
servicioGrabacion: FakeServicioGrabacionRadioInactiva(),
|
||||
resolverArchivoCustom: archivoCustomVacio,
|
||||
iniciarAutomaticamente: false,
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildScreen(EstadoRadio estado) {
|
||||
return ChangeNotifierProvider<EstadoRadio>.value(
|
||||
value: estado,
|
||||
child: MaterialApp(
|
||||
locale: const Locale('en'),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: const PantallaAjustesBackup(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('renders inside a PluriPushScaffold titled "Backup"', (
|
||||
tester,
|
||||
) async {
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
||||
expect(find.text('Backup'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'moved controls still reachable: export and import rows both present',
|
||||
(tester) async {
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
final exportTile = find.widgetWithText(ListTile, 'Export configuration');
|
||||
final importTile = find.widgetWithText(ListTile, 'Import configuration');
|
||||
expect(exportTile, findsOneWidget);
|
||||
expect(importTile, findsOneWidget);
|
||||
expect(tester.widget<ListTile>(exportTile).onTap, isNotNull);
|
||||
expect(tester.widget<ListTile>(importTile).onTap, isNotNull);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pluriwave/estado/estado_grabacion.dart';
|
||||
import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
||||
import 'package:pluriwave/pantallas/ajustes/pantalla_ajustes_grabaciones.dart';
|
||||
import 'package:pluriwave/widgets/pluri_push_scaffold.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../helpers/fakes_alarmas.dart';
|
||||
|
||||
/// 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.
|
||||
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);
|
||||
}
|
||||
|
||||
/// WU3b task 3b.1: the GRABACIONES Y MÚSICA detail screen for "Grabaciones"
|
||||
/// renders inside a [PluriPushScaffold] and its moved controls still respond
|
||||
/// exactly as they did inside the old `_SeccionGrabaciones`.
|
||||
///
|
||||
/// This file deliberately does NOT interact with "Maximum recording size"
|
||||
/// (`_editarTamanoMaximo`): that control has a pre-existing, out-of-scope
|
||||
/// controller-dispose race (documented in `pantalla_ajustes_grabaciones.dart`
|
||||
/// and, for the analogous `_editarGrupo` case, in
|
||||
/// `pantalla_ajustes_grupos_favoritos_test.dart`) that this move does not
|
||||
/// fix. "Restore default path" is exercised instead — a real, moved
|
||||
/// capability with no such hazard.
|
||||
void main() {
|
||||
setUp(() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
});
|
||||
|
||||
Widget buildScreen(EstadoGrabacion estado) {
|
||||
return ListenableProvider<EstadoGrabacion>.value(
|
||||
value: estado,
|
||||
child: MaterialApp(
|
||||
locale: const Locale('en'),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: const PantallaAjustesGrabaciones(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('renders inside a PluriPushScaffold titled "Recordings"', (
|
||||
tester,
|
||||
) async {
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = EstadoGrabacion(
|
||||
servicio: FakeServicioGrabacionRadioInactiva(),
|
||||
);
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
||||
expect(find.text('Recordings'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('moved control still responds: restore default path clears the '
|
||||
'configured directory', (tester) async {
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = EstadoGrabacion(
|
||||
servicio: FakeServicioGrabacionRadioInactiva(),
|
||||
);
|
||||
addTearDown(estado.dispose);
|
||||
await estado.cambiarDirectorio('/tmp/custom-recordings');
|
||||
expect(estado.directorioConfigurado, '/tmp/custom-recordings');
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
await tester.tap(find.byIcon(Icons.restore_rounded));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(estado.directorioConfigurado, isNull);
|
||||
expect(
|
||||
find.text('The default internal folder will be used'),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// SnackBar's own dismiss Timer is not frame-scheduled — let it resolve
|
||||
// before teardown (WU3a batch discovery) instead of leaving a pending
|
||||
// Timer behind.
|
||||
await tester.pump(const Duration(seconds: 5));
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pluriwave/estado/estado_idioma.dart';
|
||||
import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
||||
import 'package:pluriwave/pantallas/ajustes/pantalla_ajustes_idioma.dart';
|
||||
import 'package:pluriwave/widgets/pluri_push_scaffold.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// WU3b task 3b.1: the APLICACIÓN detail screen for "Idioma" renders inside
|
||||
/// a [PluriPushScaffold] and its moved control (the language dropdown)
|
||||
/// still responds exactly as it did inside the old `_SeccionIdioma`.
|
||||
void main() {
|
||||
setUp(() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
});
|
||||
|
||||
Widget buildScreen(EstadoIdioma estado) {
|
||||
return ChangeNotifierProvider<EstadoIdioma>.value(
|
||||
value: estado,
|
||||
child: MaterialApp(
|
||||
locale: const Locale('en'),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: const PantallaAjustesIdioma(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('renders inside a PluriPushScaffold titled "Language"', (
|
||||
tester,
|
||||
) async {
|
||||
final estado = EstadoIdioma(
|
||||
sharedPreferences: await SharedPreferences.getInstance(),
|
||||
);
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
||||
// "Language" legitimately renders twice here too (pre-existing,
|
||||
// unmodified by this move): the pushed screen's title AND the
|
||||
// dropdown's own floating label share the same l10n string.
|
||||
expect(find.text('Language'), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('moved control still responds: selecting a language persists '
|
||||
'it', (tester) async {
|
||||
final estado = EstadoIdioma(
|
||||
sharedPreferences: await SharedPreferences.getInstance(),
|
||||
);
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byType(DropdownButtonFormField<String>));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Español').last);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(estado.localeSeleccionado, const Locale('es'));
|
||||
expect(find.text('Language updated: Español'), findsOneWidget);
|
||||
|
||||
// SnackBar's own dismiss Timer is not frame-scheduled — let it resolve
|
||||
// before teardown (WU3a batch discovery) instead of leaving a pending
|
||||
// Timer behind.
|
||||
await tester.pump(const Duration(seconds: 5));
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
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/ajustes/pantalla_ajustes_info.dart';
|
||||
import 'package:pluriwave/widgets/pluri_push_scaffold.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../helpers/fakes.dart';
|
||||
import '../../helpers/fakes_alarmas.dart';
|
||||
|
||||
/// 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.
|
||||
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);
|
||||
}
|
||||
|
||||
/// WU3b task 3b.1: the APLICACIÓN detail screen for "Info" renders inside a
|
||||
/// [PluriPushScaffold] and its moved controls (saved-favorites count, help
|
||||
/// row) still respond exactly as they did inside the old `_SeccionInfo`.
|
||||
/// Unlike the other four WU3b screens, this one never had its own header
|
||||
/// icon+title row to strip (see `pantalla_ajustes_info.dart`'s doc comment).
|
||||
void main() {
|
||||
setUp(() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
});
|
||||
|
||||
Future<File> archivoCustomVacio() async => File(
|
||||
'${Directory.current.path}/test/fixtures/emisoras_custom_vacio.json',
|
||||
);
|
||||
|
||||
Future<EstadoRadio> crearEstado() async {
|
||||
return EstadoRadio(
|
||||
audio: FakeServicioAudio(),
|
||||
favoritos: FakeServicioFavoritos(),
|
||||
radio: FakeServicioRadio(),
|
||||
servicioEcualizador: FakeServicioEcualizador(),
|
||||
servicioGrabacion: FakeServicioGrabacionRadioInactiva(),
|
||||
resolverArchivoCustom: archivoCustomVacio,
|
||||
iniciarAutomaticamente: false,
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildScreen(EstadoRadio estado) {
|
||||
return ChangeNotifierProvider<EstadoRadio>.value(
|
||||
value: estado,
|
||||
child: MaterialApp(
|
||||
locale: const Locale('en'),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: const PantallaAjustesInfo(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('renders inside a PluriPushScaffold titled "Info"', (
|
||||
tester,
|
||||
) async {
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
||||
expect(find.text('Info'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'moved control still responds: saved favorites count reflects the '
|
||||
'favorites list',
|
||||
(tester) async {
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(find.text('Saved favorites'), findsOneWidget);
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('Help and tutorial'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
||||
import 'package:pluriwave/pantallas/ajustes/pantalla_ajustes_musica_local.dart';
|
||||
import 'package:pluriwave/widgets/pluri_push_scaffold.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// WU3b task 3b.1: the GRABACIONES Y MÚSICA detail screen for "Música local"
|
||||
/// renders inside a [PluriPushScaffold] and its moved controls (the
|
||||
/// android-auto-local-music-paging Phase 7 friendly-folder-name projection)
|
||||
/// still respond exactly as they did inside the old `_SeccionMusicaLocal`.
|
||||
///
|
||||
/// The two Phase 7 scenarios below are relocated verbatim from
|
||||
/// `pantalla_ajustes_test.dart`'s "_SeccionMusicaLocal — friendly folder
|
||||
/// name (Phase 7)" group, now targeting the isolated screen directly instead
|
||||
/// of scrolling to find it inside the whole Settings root.
|
||||
void main() {
|
||||
Widget buildScreen() {
|
||||
return const MaterialApp(
|
||||
locale: Locale('en'),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: PantallaAjustesMusicaLocal(),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('renders inside a PluriPushScaffold titled "Local music (Android '
|
||||
'Auto)"', (tester) async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
await tester.pumpWidget(buildScreen());
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
||||
expect(find.text('Local music (Android Auto)'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'7.1-A: carpeta configurada muestra el nombre amigable derivado de la '
|
||||
'URI, nunca la URI cruda',
|
||||
(tester) async {
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'musica_local_uri':
|
||||
'content://com.android.externalstorage.documents/tree/'
|
||||
'primary%3AMusic%2FMyFolder',
|
||||
});
|
||||
|
||||
await tester.pumpWidget(buildScreen());
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(find.text('MyFolder'), findsOneWidget);
|
||||
expect(find.textContaining('content://'), findsNothing);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('7.1-B: sin carpeta configurada mantiene el mensaje '
|
||||
'localMusicFolderNotConfigured', (tester) async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
await tester.pumpWidget(buildScreen());
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(find.text('No folder selected'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
@@ -88,15 +88,12 @@ void main() {
|
||||
|
||||
// ── WU3a: AUDIO + EMISORAS become grouped nav rows ─────────────────────────
|
||||
//
|
||||
// Design ADR-3: the root now carries zero inline controls for the 7
|
||||
// sections WU3a moved (Ecualizador, Salida de audio, Temporizador de sueño,
|
||||
// Grupos de favoritos, Emisora preferida, Emisoras personalizadas, Orden de
|
||||
// listas) — each is reached through a FilaAjuste row instead. The other 5
|
||||
// sections (Grabaciones, Música local, Idioma, Backup, Info) still render
|
||||
// inline here: WU3b decomposes GRABACIONES Y MÚSICA / APLICACIÓN the same
|
||||
// way, so the root is not yet under 400 lines nor fully "zero inline
|
||||
// controls" — that end state is WU3b's completion, not WU3a's (see the
|
||||
// apply-progress note on this discrepancy in tasks.md 3a.1/3a.8).
|
||||
// Design ADR-3: the root carries zero inline controls for the 7 sections
|
||||
// WU3a moved (Ecualizador, Salida de audio, Temporizador de sueño, Grupos
|
||||
// de favoritos, Emisora preferida, Emisoras personalizadas, Orden de
|
||||
// listas) — each is reached through a FilaAjuste row instead. WU3b (below)
|
||||
// completes the remaining 5 sections, so the root is now exactly 4
|
||||
// GrupoAjustes cards, under 400 lines.
|
||||
group('WU3a — AUDIO and EMISORAS groups', () {
|
||||
testWidgets('AUDIO group renders exactly 3 nav rows, no inline controls', (
|
||||
tester,
|
||||
@@ -190,9 +187,66 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
// ── Sections not yet converted (WU3b's job) stay reachable ─────────────────
|
||||
group('Sections pending WU3b remain inline and reachable', () {
|
||||
testWidgets('Grabaciones, Idioma, Backup and Info still render', (
|
||||
// ── WU3b: GRABACIONES Y MÚSICA + APLICACIÓN become grouped nav rows ───────
|
||||
//
|
||||
// Design ADR-3: the root now carries zero inline controls for the final 5
|
||||
// sections (Grabaciones, Música local, Idioma, Backup, Info) — each is
|
||||
// reached through a FilaAjuste row, matching WU3a's AUDIO/EMISORAS
|
||||
// treatment. All 12 sections are decomposed now; the root is exactly 4
|
||||
// GrupoAjustes cards and crosses under 400 lines (tasks.md 3b.5). The two
|
||||
// "Phase 7" friendly-folder-name scenarios that used to live in this file
|
||||
// are relocated verbatim to `ajustes/pantalla_ajustes_musica_local_test.dart`,
|
||||
// now targeting the isolated pushed screen directly.
|
||||
group('WU3b — GRABACIONES Y MÚSICA and APLICACIÓN groups', () {
|
||||
testWidgets(
|
||||
'RECORDINGS & MUSIC group renders exactly 2 nav rows, no inline '
|
||||
'controls',
|
||||
(tester) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildAjustes(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.text('RECORDINGS & MUSIC'), findsOneWidget);
|
||||
expect(find.text('Recordings'), findsOneWidget);
|
||||
expect(find.text('Local music (Android Auto)'), findsOneWidget);
|
||||
|
||||
// Zero inline controls: the folder-path row, path action buttons and
|
||||
// max-size row are gone from the root now.
|
||||
expect(find.text('Change path'), findsNothing);
|
||||
expect(find.text('Maximum recording size'), findsNothing);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'APPLICATION group renders exactly 3 nav rows, no inline controls',
|
||||
(tester) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildAjustes(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.text('APPLICATION'), findsOneWidget);
|
||||
// "Language" now renders exactly once at the root (the FilaAjuste
|
||||
// row only) — the old inline dropdown's duplicate label is gone.
|
||||
expect(find.text('Language'), findsOneWidget);
|
||||
expect(find.text('Backup'), findsOneWidget);
|
||||
expect(find.text('Info'), findsOneWidget);
|
||||
|
||||
// Zero inline controls: the language dropdown and export/import rows
|
||||
// are gone from the root now.
|
||||
expect(find.byType(DropdownButtonFormField<String>), findsNothing);
|
||||
expect(find.text('Export configuration'), findsNothing);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('tapping the Grabaciones row pushes its detail screen', (
|
||||
tester,
|
||||
) async {
|
||||
setLargeSurface(tester);
|
||||
@@ -203,50 +257,16 @@ void main() {
|
||||
await tester.pumpWidget(buildAjustes(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.text('Recordings'), findsOneWidget);
|
||||
// "Language" legitimately renders twice (pre-existing, unmodified by
|
||||
// WU3a): the section header AND the dropdown's own label share the
|
||||
// same l10n string.
|
||||
expect(find.text('Language'), findsWidgets);
|
||||
expect(find.text('Backup'), findsOneWidget);
|
||||
expect(find.text('Help and tutorial'), findsOneWidget);
|
||||
await tester.tap(find.text('Recordings'));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
||||
expect(find.text('Change path'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
// ── android-auto-local-music-paging Phase 7: friendly folder name ────────
|
||||
group('_SeccionMusicaLocal — friendly folder name (Phase 7)', () {
|
||||
testWidgets(
|
||||
'7.1-A: carpeta configurada muestra el nombre amigable derivado de '
|
||||
'la URI, nunca la URI cruda',
|
||||
(tester) async {
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'musica_local_uri':
|
||||
'content://com.android.externalstorage.documents/tree/'
|
||||
'primary%3AMusic%2FMyFolder',
|
||||
});
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildAjustes(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
await tester.scrollUntilVisible(
|
||||
find.text('Local music folder'),
|
||||
300,
|
||||
scrollable: find.byType(Scrollable).first,
|
||||
);
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.text('MyFolder'), findsOneWidget);
|
||||
expect(find.textContaining('content://'), findsNothing);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('7.1-B: sin carpeta configurada mantiene el mensaje '
|
||||
'localMusicFolderNotConfigured', (tester) async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
testWidgets('tapping the Info row pushes its detail screen', (
|
||||
tester,
|
||||
) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
@@ -255,14 +275,28 @@ void main() {
|
||||
await tester.pumpWidget(buildAjustes(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
await tester.scrollUntilVisible(
|
||||
find.text('Local music folder'),
|
||||
300,
|
||||
scrollable: find.byType(Scrollable).first,
|
||||
);
|
||||
await tester.tap(find.text('Info'));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.text('No folder selected'), findsOneWidget);
|
||||
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
||||
expect(find.text('Help and tutorial'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('root now contains exactly 4 GrupoAjustes cards', (
|
||||
tester,
|
||||
) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildAjustes(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.text('AUDIO'), findsOneWidget);
|
||||
expect(find.text('STATIONS'), findsOneWidget);
|
||||
expect(find.text('RECORDINGS & MUSIC'), findsOneWidget);
|
||||
expect(find.text('APPLICATION'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user