EstadoAlarmas, EstadoGrabacion and EstadoRadio defaulted `esPremium` to `() => true`, so any construction site that forgot to wire entitlement compiled fine and silently ran ungated — failing OPEN to premium and disabling the paywall with no test able to catch it. The parameter is now required with no default. Production wiring in app.dart was already correct and is unchanged; the 184 pre-existing test call sites now pass `() => true` explicitly, which is exactly the old implicit default, so every assertion is untouched. EstadoRadio has no gate of its own but constructs EstadoGrabacion, so it inherits the same contract. The one test that existed to pin the old default is renamed to describe what it still covers (the premium path through iniciar() with no duracion); its assertions are unchanged.
333 lines
9.3 KiB
Dart
333 lines
9.3 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(
|
|
esPremium: () => true,
|
|
audio: FakeServicioAudio(),
|
|
favoritos: FakeServicioFavoritos(),
|
|
radio: FakeServicioRadio(),
|
|
servicioEcualizador: FakeServicioEcualizador(),
|
|
servicioGrabacion: FakeServicioGrabacionRadioInactiva(),
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(radio.dispose);
|
|
|
|
final android = FakePuertoAlarmasAndroid();
|
|
final estadoAlarmas = EstadoAlarmas(
|
|
esPremium: () => true,
|
|
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,
|
|
);
|
|
},
|
|
);
|
|
}
|