The alarms list showed a generic "Días" label for a diasSemana alarm instead of its actual configured days. Render the real recurrence (e.g. "Lun, Mié, Vie") by reusing the SAME per-day abbreviation the editor's own day-picker circles already use -- no new formatting scheme, no new ARB keys for the days themselves. Also surface fade/volume/vacation-pause state on the card, each only when it is a genuinely useful deviation from the common case: a fade badge when fadeInSegundos > 0 (reusing the existing alarmFadeInLabel key), a volume percentage when it differs from the 85% default, and a vacation-paused badge when the alarm is both configured to pause and a vacation range is currently active (mirrors the exact predicate ServicioProgramacionAlarmas already uses). One compact line, not a badge per field. Fixes a text-collision regression in pantalla_alarmas_editor_test.dart: opening the editor for an alarm whose own day now renders on its card (e.g. "Lun") made a bare find.text(weekday) ambiguous against the editor's day-picker circle with the same label -- scoped that finder to the BottomSheet subtree.
331 lines
9.2 KiB
Dart
331 lines
9.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:pluriwave/estado/estado_alarmas.dart';
|
|
import 'package:pluriwave/estado/estado_radio.dart';
|
|
import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
|
import 'package:pluriwave/modelos/alarma_musical.dart';
|
|
import 'package:pluriwave/pantallas/pantalla_alarmas.dart';
|
|
import 'package:pluriwave/servicios/servicio_alarmas.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../helpers/fakes.dart';
|
|
import '../helpers/fakes_alarmas.dart';
|
|
|
|
/// Item 5: the alarm list must show which days a `diasSemana` alarm
|
|
/// actually fires on (e.g. "Lun, Mié, Vie"), not the generic "Días" label,
|
|
/// plus surface fade/volume/vacation-pause state when they are genuinely
|
|
/// informative -- without cluttering the row.
|
|
void main() {
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
});
|
|
|
|
Future<(EstadoRadio, EstadoAlarmas)> montar(
|
|
WidgetTester tester, {
|
|
required AlarmaMusical alarma,
|
|
List<RangoVacaciones> vacaciones = const [],
|
|
}) async {
|
|
tester.view.physicalSize = const Size(1440, 3200);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
addTearDown(tester.view.resetPhysicalSize);
|
|
addTearDown(tester.view.resetDevicePixelRatio);
|
|
|
|
final radio = EstadoRadio(
|
|
audio: FakeServicioAudio(),
|
|
favoritos: FakeServicioFavoritos(),
|
|
radio: FakeServicioRadio(),
|
|
servicioEcualizador: FakeServicioEcualizador(),
|
|
servicioGrabacion: FakeServicioGrabacionRadioInactiva(),
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(radio.dispose);
|
|
|
|
final android = FakePuertoAlarmasAndroid();
|
|
final estadoAlarmas = EstadoAlarmas(
|
|
servicio: ServicioAlarmas(reloj: () => DateTime(2026, 6, 11, 6, 0)),
|
|
android: android,
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(estadoAlarmas.dispose);
|
|
addTearDown(android.dispose);
|
|
|
|
await estadoAlarmas.guardarAlarma(alarma);
|
|
if (vacaciones.isNotEmpty) {
|
|
await estadoAlarmas.guardarVacaciones(vacaciones);
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
MultiProvider(
|
|
providers: [
|
|
ChangeNotifierProvider<EstadoRadio>.value(value: radio),
|
|
ChangeNotifierProvider<EstadoAlarmas>.value(value: estadoAlarmas),
|
|
],
|
|
child: MaterialApp(
|
|
locale: const Locale('es'),
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
home: const Scaffold(body: PantallaAlarmas()),
|
|
),
|
|
),
|
|
);
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 100));
|
|
|
|
return (radio, estadoAlarmas);
|
|
}
|
|
|
|
testWidgets(
|
|
'diasSemana alarm shows the ACTUAL configured days (Lun, Mié, Vie), '
|
|
'not the generic "Días" label',
|
|
(tester) async {
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-dias',
|
|
nombre: 'Entre semana',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diasSemana,
|
|
diasSemana: [DateTime.monday, DateTime.wednesday, DateTime.friday],
|
|
),
|
|
);
|
|
|
|
expect(find.text('Lun, Mié, Vie'), findsOneWidget);
|
|
expect(find.text('Días'), findsNothing);
|
|
},
|
|
);
|
|
|
|
testWidgets('daily alarm still shows "Diaria" (unaffected)', (
|
|
tester,
|
|
) async {
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-diaria',
|
|
nombre: 'Todos los días',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
),
|
|
);
|
|
|
|
expect(find.text('Diaria'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('one-time alarm still shows "Una vez" (unaffected)', (
|
|
tester,
|
|
) async {
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-unica',
|
|
nombre: 'Una sola vez',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.unica,
|
|
diasSemana: [],
|
|
fechaUnica: null,
|
|
),
|
|
);
|
|
|
|
expect(find.text('Una vez'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets(
|
|
'a diasSemana alarm with an (invalid/legacy) empty diasSemana falls '
|
|
'back to the generic label instead of showing nothing',
|
|
(tester) async {
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-dias-vacio',
|
|
nombre: 'Corrupta',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diasSemana,
|
|
diasSemana: [],
|
|
),
|
|
);
|
|
|
|
expect(find.text('Días'), findsOneWidget);
|
|
},
|
|
);
|
|
|
|
testWidgets('a configured fade-in shows a compact "Fade-in Ns" detail', (
|
|
tester,
|
|
) async {
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-fade',
|
|
nombre: 'Con fade',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
fadeInSegundos: 8,
|
|
),
|
|
);
|
|
|
|
expect(find.textContaining('Fade-in 8s'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('no fade-in (0s, the default) shows no fade detail', (
|
|
tester,
|
|
) async {
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-sin-fade',
|
|
nombre: 'Sin fade',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
fadeInSegundos: 0,
|
|
),
|
|
);
|
|
|
|
expect(find.textContaining('Fade-in'), findsNothing);
|
|
});
|
|
|
|
testWidgets(
|
|
'a non-default volume shows a compact percentage detail',
|
|
(tester) async {
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-vol',
|
|
nombre: 'Volumen bajo',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
volumen: 0.5,
|
|
),
|
|
);
|
|
|
|
expect(find.textContaining('50%'), findsOneWidget);
|
|
},
|
|
);
|
|
|
|
testWidgets('the default volume (85%) shows no volume detail', (
|
|
tester,
|
|
) async {
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-vol-default',
|
|
nombre: 'Volumen default',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
),
|
|
);
|
|
|
|
expect(find.textContaining('85%'), findsNothing);
|
|
});
|
|
|
|
testWidgets(
|
|
'an alarm paused by a CURRENTLY active vacation range shows a '
|
|
'vacation-paused detail',
|
|
(tester) async {
|
|
final l10n = lookupAppLocalizations(const Locale('es'));
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-vacaciones',
|
|
nombre: 'Pausada',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
sonarEnVacaciones: false,
|
|
),
|
|
vacaciones: [
|
|
// Wide, real-wall-clock-safe range (rangoVacacionesActivo()
|
|
// defaults to the REAL DateTime.now(), not this file's injected
|
|
// `reloj`) -- deliberately spans many years so the test stays
|
|
// valid regardless of exactly when it runs.
|
|
RangoVacaciones(
|
|
id: 'v1',
|
|
nombre: 'Verano',
|
|
inicio: DateTime(2020, 1, 1),
|
|
fin: DateTime(2030, 12, 31),
|
|
),
|
|
],
|
|
);
|
|
|
|
expect(
|
|
find.textContaining(l10n.alarmCardVacationPausedBadge),
|
|
findsOneWidget,
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'an alarm that DOES sound during vacations shows NO vacation-paused '
|
|
'detail even with an active range',
|
|
(tester) async {
|
|
final l10n = lookupAppLocalizations(const Locale('es'));
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-suena-vacaciones',
|
|
nombre: 'Suena igual',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
sonarEnVacaciones: true,
|
|
),
|
|
vacaciones: [
|
|
// Wide, real-wall-clock-safe range (rangoVacacionesActivo()
|
|
// defaults to the REAL DateTime.now(), not this file's injected
|
|
// `reloj`) -- deliberately spans many years so the test stays
|
|
// valid regardless of exactly when it runs.
|
|
RangoVacaciones(
|
|
id: 'v1',
|
|
nombre: 'Verano',
|
|
inicio: DateTime(2020, 1, 1),
|
|
fin: DateTime(2030, 12, 31),
|
|
),
|
|
],
|
|
);
|
|
|
|
expect(
|
|
find.textContaining(l10n.alarmCardVacationPausedBadge),
|
|
findsNothing,
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'sonarEnVacaciones:false with NO currently-active vacation range shows '
|
|
'no vacation-paused detail (nothing to be paused BY right now)',
|
|
(tester) async {
|
|
final l10n = lookupAppLocalizations(const Locale('es'));
|
|
await montar(
|
|
tester,
|
|
alarma: const AlarmaMusical(
|
|
id: 'a-sin-rango-activo',
|
|
nombre: 'Sin vacaciones activas',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
sonarEnVacaciones: false,
|
|
),
|
|
);
|
|
|
|
expect(
|
|
find.textContaining(l10n.alarmCardVacationPausedBadge),
|
|
findsNothing,
|
|
);
|
|
},
|
|
);
|
|
}
|