Audit 8.5 (t4:381): the "REPETIR" eyebrow above the weekday circles -- never rendered before. New ARB key alarmRepeatSectionLabel. Audit 8.6 (t4:392-401): the station picker, volume, fade-in and vacation toggle now share ONE bordered card with sangred divider lines -- were four separately-spaced widgets. Restyled _CampoSelectorEmisora as a flat nav row (icon/bold label/muted value/chevron, matching FilaAjuste's own convention) so it fits inside the card instead of drawing its own outlined InputDecoration chrome -- used by BOTH the primary and fallback (Advanced) station fields. Snooze duration, "use current station", and the Advanced section stay OUTSIDE the card, exactly where they were (audit 8.8's own documented deliberate extras). A transparent Material sits inside the card's coloured box so the fade-in ListTile and the vacation SwitchListTile still paint their ink/background correctly (the same class of constraint already documented for PluriGlassSurface elsewhere in this codebase). Audit 8.7 (t4:396): the volume row is now a compact 112px track with a live percentage label -- was a bare full-width Slider with no visible value. New ArtB key alarmVolumeLabel. Item 8.8 (extra name/type-selector/snooze-selector/"use current station"/Advanced fields) stays exactly as documented: deliberate, required by the hard constraint that the editor keep its date field, fallback-station picker and sound dropdown.
571 lines
18 KiB
Dart
571 lines
18 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:pluriwave/widgets/editor_hora_inline.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';
|
|
|
|
class _Entorno {
|
|
_Entorno({required this.estadoAlarmas});
|
|
|
|
final EstadoAlarmas estadoAlarmas;
|
|
}
|
|
|
|
class _EntornoDosAlarmas {
|
|
_EntornoDosAlarmas({required this.estadoAlarmas, required this.android});
|
|
|
|
final EstadoAlarmas estadoAlarmas;
|
|
final FakePuertoAlarmasAndroid android;
|
|
}
|
|
|
|
/// WU8 fixture: two DAILY alarms ('r1' 07:00, 'r2' 08:00) under a FIXED clock
|
|
/// (06:00, before both) so 'r1' is deterministically the featured/soonest
|
|
/// alarm regardless of the wall-clock time the suite happens to run at.
|
|
Future<_EntornoDosAlarmas> _montarDosAlarmas(WidgetTester tester) 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, 1, 6, 0)),
|
|
android: android,
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(estadoAlarmas.dispose);
|
|
addTearDown(android.dispose);
|
|
await estadoAlarmas.guardarAlarma(
|
|
const AlarmaMusical(
|
|
id: 'r1',
|
|
nombre: 'Primera',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
),
|
|
);
|
|
await estadoAlarmas.guardarAlarma(
|
|
const AlarmaMusical(
|
|
id: 'r2',
|
|
nombre: 'Segunda',
|
|
hora: 8,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
),
|
|
);
|
|
|
|
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.pumpAndSettle();
|
|
|
|
return _EntornoDosAlarmas(estadoAlarmas: estadoAlarmas, android: android);
|
|
}
|
|
|
|
Future<_Entorno> _abrirEditor(WidgetTester tester) async {
|
|
tester.view.physicalSize = const Size(1440, 3200);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
addTearDown(tester.view.resetPhysicalSize);
|
|
addTearDown(tester.view.resetDevicePixelRatio);
|
|
|
|
final favoritos = FakeServicioFavoritos();
|
|
await favoritos.agregar(emisoraDemo(uuid: 'alfa', nombre: 'Alfa FM'));
|
|
await favoritos.agregar(emisoraDemo(uuid: 'beta', nombre: 'Beta FM'));
|
|
final radio = EstadoRadio(
|
|
audio: FakeServicioAudio(),
|
|
favoritos: favoritos,
|
|
radio: FakeServicioRadio(),
|
|
servicioEcualizador: FakeServicioEcualizador(),
|
|
servicioGrabacion: FakeServicioGrabacionRadioInactiva(),
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(radio.dispose);
|
|
await radio.cargarFavoritos();
|
|
|
|
final android = FakePuertoAlarmasAndroid();
|
|
final estadoAlarmas = EstadoAlarmas(
|
|
servicio: ServicioAlarmas(reloj: DateTime.now),
|
|
android: android,
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(estadoAlarmas.dispose);
|
|
addTearDown(android.dispose);
|
|
await estadoAlarmas.guardarAlarma(
|
|
const AlarmaMusical(
|
|
id: 'ed1',
|
|
nombre: 'Semanal',
|
|
hora: 7,
|
|
minuto: 30,
|
|
tipoProgramacion: TipoProgramacionAlarma.diasSemana,
|
|
diasSemana: [DateTime.monday],
|
|
),
|
|
);
|
|
|
|
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.pumpAndSettle();
|
|
|
|
// WU8: the always-visible "Edit" button is gone — tapping the simplified
|
|
// card itself now opens the editor.
|
|
final tarjeta = find.byKey(const ValueKey('tarjeta-alarma-ed1'));
|
|
await tester.ensureVisible(tarjeta);
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(tarjeta);
|
|
await tester.pumpAndSettle();
|
|
return _Entorno(estadoAlarmas: estadoAlarmas);
|
|
}
|
|
|
|
String _textoPreview(WidgetTester tester) {
|
|
final texto = tester.widget<Text>(
|
|
find.descendant(
|
|
of: find.byKey(const ValueKey('next-trigger-preview')),
|
|
matching: find.byType(Text),
|
|
),
|
|
);
|
|
return texto.data ?? '';
|
|
}
|
|
|
|
void main() {
|
|
final l10n = lookupAppLocalizations(const Locale('es'));
|
|
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
});
|
|
|
|
testWidgets(
|
|
'muestra la proxima ejecucion y la actualiza al cambiar la recurrencia '
|
|
'(S2-R8)',
|
|
(tester) async {
|
|
await _abrirEditor(tester);
|
|
|
|
expect(
|
|
find.byKey(const ValueKey('next-trigger-preview')),
|
|
findsOneWidget,
|
|
);
|
|
final antes = _textoPreview(tester);
|
|
expect(antes, isNot(l10n.alarmNoNextExecution));
|
|
|
|
// Lunes -> Martes: la fecha calculada SIEMPRE cambia, sea cual sea hoy.
|
|
await tester.tap(find.text(l10n.weekdayShortTuesday));
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.text(l10n.weekdayShortMonday));
|
|
await tester.pumpAndSettle();
|
|
|
|
final despues = _textoPreview(tester);
|
|
expect(despues, isNot(l10n.alarmNoNextExecution));
|
|
expect(despues, isNot(antes));
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'el selector de emisora abre un bottom sheet con buscador (S2-R9)',
|
|
(tester) async {
|
|
await _abrirEditor(tester);
|
|
|
|
await tester.ensureVisible(
|
|
find.byKey(const ValueKey('alarm-station-field')),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.byKey(const ValueKey('alarm-station-field')));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(SearchBar), findsOneWidget);
|
|
final lista = find.byType(ListView).last;
|
|
expect(
|
|
find.descendant(of: lista, matching: find.text('Alfa FM')),
|
|
findsOneWidget,
|
|
);
|
|
expect(
|
|
find.descendant(of: lista, matching: find.text('Beta FM')),
|
|
findsOneWidget,
|
|
);
|
|
|
|
await tester.enterText(find.byType(TextField).last, 'beta');
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.descendant(of: lista, matching: find.text('Alfa FM')),
|
|
findsNothing,
|
|
);
|
|
expect(
|
|
find.descendant(of: lista, matching: find.text('Beta FM')),
|
|
findsOneWidget,
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'tambien existe un selector para la emisora de respaldo (S2-R9)',
|
|
(tester) async {
|
|
await _abrirEditor(tester);
|
|
|
|
// WU10 correction: the fallback-station field moved into the
|
|
// collapsed "Advanced" section (native-alarms delta — Alarm Editor
|
|
// Preserves Date, Fallback Station, and Sound Fields). It must be
|
|
// expanded first — a collapsed `ExpansionTile` does not build its
|
|
// children, so `find.byKey` would otherwise find nothing.
|
|
await tester.ensureVisible(find.text(l10n.alarmAdvancedSectionTitle));
|
|
await tester.tap(find.text(l10n.alarmAdvancedSectionTitle));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.ensureVisible(
|
|
find.byKey(const ValueKey('alarm-fallback-station-field')),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(
|
|
find.byKey(const ValueKey('alarm-fallback-station-field')),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(SearchBar), findsOneWidget);
|
|
},
|
|
);
|
|
|
|
testWidgets('permite configurar la duracion del snooze (S2-R10)', (
|
|
tester,
|
|
) async {
|
|
final entorno = await _abrirEditor(tester);
|
|
|
|
await tester.ensureVisible(find.text(l10n.alarmSnoozeDurationTitle));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byType(SegmentedButton<int>), findsWidgets);
|
|
|
|
await tester.tap(find.text(l10n.alarmSnoozeOptionLabel(10)));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.ensureVisible(find.text(l10n.saveAlarmAction));
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.text(l10n.saveAlarmAction));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(entorno.estadoAlarmas.alarmas.single.snoozeMinutos, 10);
|
|
});
|
|
|
|
testWidgets('el slider de volumen permite bajar hasta 0.0 (S2-R11)', (
|
|
tester,
|
|
) async {
|
|
await _abrirEditor(tester);
|
|
|
|
final sliders = tester.widgetList<Slider>(find.byType(Slider));
|
|
final volumen = sliders.firstWhere((slider) => slider.max == 1.0);
|
|
expect(volumen.min, 0.0);
|
|
});
|
|
|
|
group('visual fidelity (audit 8.1/8.3/8.4, item 20)', () {
|
|
testWidgets(
|
|
'8.1: the sheet is an opaque, bottom-anchored surface (radius on the '
|
|
'top corners only) with a grab handle',
|
|
(tester) async {
|
|
await _abrirEditor(tester);
|
|
|
|
final surface = tester.widget<DecoratedBox>(
|
|
find.byKey(const ValueKey('alarm-editor-sheet-surface')),
|
|
);
|
|
final decoracion = surface.decoration as BoxDecoration;
|
|
expect(
|
|
decoracion.color,
|
|
const Color(0xFF0D1B24),
|
|
reason: 't4 line 373: background:#0D1B24 (opaque, not glass)',
|
|
);
|
|
expect(
|
|
decoracion.borderRadius,
|
|
const BorderRadius.vertical(top: Radius.circular(30)),
|
|
reason:
|
|
't4 line 373: border-radius:30px 30px 0 0 — top corners '
|
|
'only, unlike a floating dialog',
|
|
);
|
|
|
|
final asa = tester.widget<Container>(
|
|
find.byKey(const ValueKey('alarm-editor-grab-handle')),
|
|
);
|
|
expect(asa.constraints?.maxWidth, 44);
|
|
expect(asa.constraints?.maxHeight, 4);
|
|
final asaDecoracion = asa.decoration as BoxDecoration;
|
|
expect(
|
|
asaDecoracion.borderRadius,
|
|
BorderRadius.circular(2),
|
|
reason: 't4 line 374: width:44px;height:4px;border-radius:2px',
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets('8.3: the hour editor sits inside a framed card', (
|
|
tester,
|
|
) async {
|
|
await _abrirEditor(tester);
|
|
|
|
final tarjeta = tester.widget<DecoratedBox>(
|
|
find.ancestor(
|
|
of: find.byType(EditorHoraInline),
|
|
matching: find.byKey(const ValueKey('alarm-editor-hour-card')),
|
|
),
|
|
);
|
|
final decoracion = tarjeta.decoration as BoxDecoration;
|
|
expect(
|
|
decoracion.borderRadius,
|
|
BorderRadius.circular(22),
|
|
reason: 't4 line 378: border-radius:22px',
|
|
);
|
|
expect(
|
|
decoracion.color,
|
|
const Color(0xFF102532),
|
|
reason: 't4 line 378: background:#102532 (== listSurface token)',
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'8.4: the weekday selector is exactly 7 circular buttons, not chips',
|
|
(tester) async {
|
|
await _abrirEditor(tester);
|
|
|
|
final fila = find.byKey(const ValueKey('alarm-weekday-circles'));
|
|
expect(fila, findsOneWidget);
|
|
expect(
|
|
find.descendant(of: fila, matching: find.byType(FilterChip)),
|
|
findsNothing,
|
|
reason: 't4 lines 383-390: 7 circles, not FilterChips',
|
|
);
|
|
final circulos = find.descendant(
|
|
of: fila,
|
|
matching: find.byWidgetPredicate(
|
|
(w) => w is Material && w.shape is CircleBorder,
|
|
),
|
|
);
|
|
expect(circulos, findsNWidgets(7));
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'capacity guard: the date field, the fallback-station picker and the '
|
|
'sound dropdown all stay reachable behind Advanced',
|
|
(tester) async {
|
|
await _abrirEditor(tester);
|
|
|
|
await tester.ensureVisible(find.text(l10n.alarmAdvancedSectionTitle));
|
|
await tester.tap(find.text(l10n.alarmAdvancedSectionTitle));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text(l10n.dateField), findsOneWidget);
|
|
expect(
|
|
find.byKey(const ValueKey('alarm-fallback-station-field')),
|
|
findsOneWidget,
|
|
);
|
|
expect(
|
|
find.byType(DropdownButtonFormField<SonidoInternoAlarma>),
|
|
findsOneWidget,
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets('8.5: the "REPETIR" eyebrow sits above the weekday circles', (
|
|
tester,
|
|
) async {
|
|
await _abrirEditor(tester);
|
|
|
|
expect(find.text(l10n.alarmRepeatSectionLabel), findsOneWidget);
|
|
final eyebrowY =
|
|
tester.getBottomLeft(find.text(l10n.alarmRepeatSectionLabel)).dy;
|
|
final circlesY =
|
|
tester
|
|
.getTopLeft(find.byKey(const ValueKey('alarm-weekday-circles')))
|
|
.dy;
|
|
expect(eyebrowY <= circlesY, isTrue);
|
|
});
|
|
|
|
testWidgets(
|
|
'8.6: the station field, volume, fade-in and vacation toggle share '
|
|
'ONE grouped card',
|
|
(tester) async {
|
|
await _abrirEditor(tester);
|
|
|
|
final tarjeta = find.byKey(const ValueKey('alarm-editor-grouped-card'));
|
|
expect(tarjeta, findsOneWidget);
|
|
expect(
|
|
find.descendant(
|
|
of: tarjeta,
|
|
matching: find.byKey(const ValueKey('alarm-station-field')),
|
|
),
|
|
findsOneWidget,
|
|
);
|
|
expect(
|
|
find.descendant(of: tarjeta, matching: find.byType(Slider)),
|
|
findsNWidgets(2),
|
|
reason: 'volume + fade-in sliders both live inside the card',
|
|
);
|
|
expect(
|
|
find.descendant(of: tarjeta, matching: find.byType(SwitchListTile)),
|
|
findsOneWidget,
|
|
reason: 'the vacation toggle lives inside the card',
|
|
);
|
|
// Snooze duration is a deliberate EXTRA (audit 8.8), kept OUTSIDE
|
|
// the prototype-mandated 4-row card.
|
|
expect(
|
|
find.descendant(
|
|
of: tarjeta,
|
|
matching: find.text(l10n.alarmSnoozeDurationTitle),
|
|
),
|
|
findsNothing,
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets('8.7: the volume row shows a live percentage label next to a '
|
|
'compact track', (tester) async {
|
|
await _abrirEditor(tester);
|
|
|
|
expect(find.text(l10n.alarmVolumeLabel), findsOneWidget);
|
|
expect(find.text('85%'), findsOneWidget);
|
|
});
|
|
});
|
|
|
|
group('WU8 — tarjeta de alarma simplificada', () {
|
|
testWidgets(
|
|
'tocar la tarjeta abre el editor precargado con los datos de esa '
|
|
'alarma',
|
|
(tester) async {
|
|
await _montarDosAlarmas(tester);
|
|
|
|
await tester.tap(find.byKey(const ValueKey('tarjeta-alarma-r2')));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text(l10n.editAlarmTitle), findsOneWidget);
|
|
expect(find.text('Segunda'), findsOneWidget);
|
|
},
|
|
);
|
|
|
|
testWidgets('deslizar la tarjeta pide confirmacion; cancelar la conserva y '
|
|
'confirmar la elimina', (tester) async {
|
|
final entorno = await _montarDosAlarmas(tester);
|
|
|
|
// Cancelar: la tarjeta se conserva.
|
|
await tester.drag(
|
|
find.byKey(const ValueKey('tarjeta-alarma-r2')),
|
|
const Offset(-600, 0),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
expect(find.text(l10n.alarmDeleteConfirmTitle), findsOneWidget);
|
|
await tester.tap(find.text(l10n.cancelAction));
|
|
await tester.pumpAndSettle();
|
|
expect(entorno.estadoAlarmas.alarmas.length, 2);
|
|
expect(find.byKey(const ValueKey('tarjeta-alarma-r2')), findsOneWidget);
|
|
|
|
// Confirmar: la tarjeta se elimina.
|
|
await tester.drag(
|
|
find.byKey(const ValueKey('tarjeta-alarma-r2')),
|
|
const Offset(-600, 0),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
expect(find.text(l10n.alarmDeleteConfirmTitle), findsOneWidget);
|
|
await tester.tap(find.text(l10n.deleteAction));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(entorno.estadoAlarmas.alarmas.length, 1);
|
|
expect(find.byKey(const ValueKey('tarjeta-alarma-r2')), findsNothing);
|
|
});
|
|
|
|
testWidgets(
|
|
'el banner destacado permite saltar la alarma mas proxima (accion '
|
|
'"Saltar")',
|
|
(tester) async {
|
|
final entorno = await _montarDosAlarmas(tester);
|
|
final proximaAntes = entorno.estadoAlarmas.proximaAlarma;
|
|
expect(proximaAntes?.id, 'r1');
|
|
|
|
await tester.tap(find.byKey(const ValueKey('hero-skip-next')));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(SnackBar), findsOneWidget);
|
|
final r1 = entorno.estadoAlarmas.alarmas.firstWhere(
|
|
(a) => a.id == 'r1',
|
|
);
|
|
expect(r1.proximaEjecucion, isNot(proximaAntes!.proximaEjecucion));
|
|
},
|
|
);
|
|
|
|
testWidgets('la fila resumen de vacaciones empuja una pantalla al tocarla '
|
|
'(destino provisorio; WU9 lo reemplaza)', (tester) async {
|
|
await _montarDosAlarmas(tester);
|
|
|
|
await tester.tap(find.byKey(const ValueKey('vacaciones-resumen')));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
|
});
|
|
|
|
testWidgets(
|
|
'guardia de capacidad: editar (tap), saltar (hero) y eliminar (swipe) '
|
|
'siguen siendo alcanzables tras la simplificacion de la tarjeta',
|
|
(tester) async {
|
|
final entorno = await _montarDosAlarmas(tester);
|
|
|
|
// 1) Saltar, desde el banner destacado.
|
|
await tester.tap(find.byKey(const ValueKey('hero-skip-next')));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byType(SnackBar), findsOneWidget);
|
|
|
|
// 2) Editar, tocando la tarjeta.
|
|
await tester.tap(find.byKey(const ValueKey('tarjeta-alarma-r2')));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text(l10n.editAlarmTitle), findsOneWidget);
|
|
await tester.tap(find.byIcon(Icons.close_rounded));
|
|
await tester.pumpAndSettle();
|
|
|
|
// 3) Eliminar, deslizando y confirmando.
|
|
await tester.drag(
|
|
find.byKey(const ValueKey('tarjeta-alarma-r2')),
|
|
const Offset(-600, 0),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.text(l10n.deleteAction));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(entorno.estadoAlarmas.alarmas.length, 1);
|
|
},
|
|
);
|
|
});
|
|
}
|