Issue 3 (feedback-pruebas): t4:427 wraps the POSPONER eyebrow, the snooze tiles and the Stop button in a single flex column with a uniform gap:12 -- this screen carried a 10/14 pair instead, matching neither the prototype nor each other. The dismiss-guard test (protected, untouched) only asserts behaviour via find.text/find.byType, so this pure value change is safe against it -- re-verified empty diff after this commit.
208 lines
7.1 KiB
Dart
208 lines
7.1 KiB
Dart
import 'dart:async';
|
|
|
|
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/formato_fechas.dart';
|
|
import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
|
import 'package:pluriwave/modelos/alarma_musical.dart';
|
|
import 'package:pluriwave/modelos/emisora.dart';
|
|
import 'package:pluriwave/pantallas/pantalla_alarma_sonando.dart';
|
|
import 'package:pluriwave/servicios/servicio_alarmas.dart';
|
|
import 'package:pluriwave/servicios/servicio_audio.dart';
|
|
import 'package:pluriwave/tema/pluriwave_theme.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../helpers/fakes.dart';
|
|
import '../helpers/fakes_alarmas.dart';
|
|
|
|
/// Closing batch (items 9.4, 9.10, audit id 2521) -- the ringing screen.
|
|
/// Mirrors `pantalla_alarma_sonando_tier4_test.dart`'s own harness (kept as
|
|
/// a local copy, not a shared import, matching this file's established
|
|
/// precedent of small helper duplication over cross-test-file coupling).
|
|
Future<void> _montarPantalla(WidgetTester tester) async {
|
|
tester.view.physicalSize = const Size(1440, 3200);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
addTearDown(tester.view.resetPhysicalSize);
|
|
addTearDown(tester.view.resetDevicePixelRatio);
|
|
|
|
final audio = FakeServicioAudio();
|
|
audio.emitirEstado(EstadoReproduccion.reproduciendo);
|
|
final radio = EstadoRadio(
|
|
audio: audio,
|
|
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, 7, 0)),
|
|
android: android,
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(estadoAlarmas.dispose);
|
|
addTearDown(android.dispose);
|
|
await estadoAlarmas.guardarAlarma(
|
|
const AlarmaMusical(
|
|
id: 'tier5-1',
|
|
nombre: 'Despertar',
|
|
hora: 7,
|
|
minuto: 30,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
snoozeMinutos: 10,
|
|
emisora: Emisora(
|
|
uuid: 'e1',
|
|
nombre: 'Radio Uno',
|
|
url: 'https://radio.example/stream',
|
|
),
|
|
),
|
|
);
|
|
|
|
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,
|
|
theme: PluriWaveTheme.dark(),
|
|
home: const SizedBox.shrink(),
|
|
),
|
|
),
|
|
);
|
|
final navigator = tester.state<NavigatorState>(find.byType(Navigator));
|
|
unawaited(
|
|
navigator.push(
|
|
MaterialPageRoute<void>(
|
|
builder:
|
|
(_) => PantallaAlarmaSonando(alarma: estadoAlarmas.alarmas.single),
|
|
fullscreenDialog: true,
|
|
),
|
|
),
|
|
);
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
}
|
|
|
|
void main() {
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
});
|
|
|
|
testWidgets('visual fidelity (audit 9.4): the date line renders between the '
|
|
'schedule pill and the hero time (t4:419)', (tester) async {
|
|
await _montarPantalla(tester);
|
|
|
|
final localeTag =
|
|
Localizations.localeOf(
|
|
tester.element(find.byType(PantallaAlarmaSonando)),
|
|
).toString();
|
|
final esperado = fechaLargaConDiaSemana(localeTag, DateTime.now());
|
|
|
|
expect(find.text(esperado), findsOneWidget);
|
|
|
|
// Order: pill above the date line, date line above the hero time.
|
|
final pillY =
|
|
tester
|
|
.getBottomLeft(find.byKey(const ValueKey('ringing-schedule-pill')))
|
|
.dy;
|
|
final dateY = tester.getTopLeft(find.text(esperado)).dy;
|
|
final timeY =
|
|
tester.getTopLeft(find.byKey(const ValueKey('ringing-hero-time'))).dy;
|
|
expect(pillY <= dateY, isTrue);
|
|
expect(dateY <= timeY, isTrue);
|
|
|
|
// Regression guard: pumpAndSettle must still complete (purely
|
|
// additive static text, no new animation).
|
|
await tester.pumpAndSettle();
|
|
});
|
|
|
|
testWidgets(
|
|
'visual fidelity (audit 9.10): the highlighted snooze tile carries a '
|
|
'"usual" qualifier ADDITIVELY -- the original flat label the dismiss '
|
|
'guard finds by text stays fully intact',
|
|
(tester) async {
|
|
await _montarPantalla(tester);
|
|
|
|
final l10n = AppLocalizations.of(
|
|
tester.element(find.byType(PantallaAlarmaSonando)),
|
|
);
|
|
|
|
// The ORIGINAL flat label must still resolve to exactly one Text --
|
|
// this IS the dismiss-guard's own finder contract.
|
|
expect(find.text(l10n.alarmSnoozeOptionLabel(10)), findsOneWidget);
|
|
// The additive qualifier renders too, but only on the destacado tile.
|
|
expect(find.text(l10n.alarmSnoozeUsualLabel), findsOneWidget);
|
|
expect(find.text(l10n.alarmSnoozeOptionLabel(3)), findsOneWidget);
|
|
expect(find.text(l10n.alarmSnoozeOptionLabel(5)), findsOneWidget);
|
|
|
|
// Regression guard: the exact hazard 9.9/9.10 share -- pumpAndSettle
|
|
// must still complete, and the flat label must remain the SAME
|
|
// widget the dismiss guard taps (still inside a FilledButton).
|
|
expect(
|
|
find.ancestor(
|
|
of: find.text(l10n.alarmSnoozeOptionLabel(10)),
|
|
matching: find.byType(FilledButton),
|
|
),
|
|
findsOneWidget,
|
|
);
|
|
await tester.pumpAndSettle();
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'Issue 3 (feedback-pruebas): the gap above the snooze tiles matches the '
|
|
'gap below them (t4:427 draws a uniform gap:12 flex column) -- the '
|
|
'previous 10/14 pair matched neither the prototype nor each other',
|
|
(tester) async {
|
|
await _montarPantalla(tester);
|
|
final l10n = AppLocalizations.of(
|
|
tester.element(find.byType(PantallaAlarmaSonando)),
|
|
);
|
|
|
|
final eyebrowBottom =
|
|
tester
|
|
.getBottomLeft(
|
|
find
|
|
.ancestor(
|
|
of: find.byIcon(Icons.snooze_rounded),
|
|
matching: find.byType(Row),
|
|
)
|
|
.first,
|
|
)
|
|
.dy;
|
|
final tileDestacado = find.ancestor(
|
|
of: find.text(l10n.alarmSnoozeOptionLabel(10)),
|
|
matching: find.byType(FilledButton),
|
|
);
|
|
final tilesTop = tester.getTopLeft(tileDestacado).dy;
|
|
final tilesBottom = tester.getBottomLeft(tileDestacado).dy;
|
|
final stopButtonTop =
|
|
tester
|
|
.getTopLeft(find.byKey(const ValueKey('ringing-stop-button')))
|
|
.dy;
|
|
|
|
expect(
|
|
tilesTop - eyebrowBottom,
|
|
12,
|
|
reason: 't4:427: gap:12 above the snooze tiles',
|
|
);
|
|
expect(
|
|
stopButtonTop - tilesBottom,
|
|
12,
|
|
reason: 't4:427: gap:12 below the snooze tiles, same as above',
|
|
);
|
|
},
|
|
);
|
|
}
|