Replace the glass-card container with a full-bleed blurred-art background, giant heroTime display, 3 fixed snooze tiles (3/5/10 min, one highlighted), and a full-width stop pill. The status label now also renders a static "Subiendo volumen" line - no seconds counter - when the alarm has a configured fade-in, per resolution 4: the native-to-Flutter progress channel a live counter would need is deliberately absent from this architecture. The dismiss guard and force-stop retry banner are untouched: the banner is byte-identical to its pre-restyle form, only repositioned, and the guard test's own diff against main stays empty. size:exception: 530 changed lines (440+/90-) against the 200-300 forecast - lib/ alone is 302 lines, at the edge of the band; the two touched test files account for the rest. Not split further: this is one cohesive restyle to the single screen in this branch where an inconsistent intermediate state is least acceptable.
181 lines
5.5 KiB
Dart
181 lines
5.5 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_animate/flutter_animate.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/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:pluriwave/widgets/pluri_glass_surface.dart';
|
|
import 'package:pluriwave/widgets/pluri_wave_scaffold.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../helpers/fakes.dart';
|
|
import '../helpers/fakes_alarmas.dart';
|
|
|
|
Future<void> _montarPantalla(
|
|
WidgetTester tester, {
|
|
bool disableAnimations = false,
|
|
}) 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: 'scaffold1',
|
|
nombre: 'Despertar',
|
|
hora: 7,
|
|
minuto: 30,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
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,
|
|
builder:
|
|
(context, child) => MediaQuery(
|
|
data: MediaQuery.of(
|
|
context,
|
|
).copyWith(disableAnimations: disableAnimations),
|
|
child: child!,
|
|
),
|
|
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.pumpAndSettle();
|
|
}
|
|
|
|
void main() {
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
});
|
|
|
|
testWidgets(
|
|
'usa PluriWaveScaffold sin colores hardcodeados y anima la entrada '
|
|
'(S2-R7)',
|
|
(tester) async {
|
|
await _montarPantalla(tester);
|
|
|
|
expect(find.byType(PluriWaveScaffold), findsOneWidget);
|
|
for (final scaffold in tester.widgetList<Scaffold>(
|
|
find.byType(Scaffold),
|
|
)) {
|
|
expect(
|
|
scaffold.backgroundColor,
|
|
isNot(const Color(0xFF061722)),
|
|
reason: 'el Scaffold crudo con color hardcodeado debe desaparecer',
|
|
);
|
|
}
|
|
expect(
|
|
find.byType(Animate),
|
|
findsWidgets,
|
|
reason: 'la entrada debe animarse cuando las animaciones estan activas',
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'omite la animacion de entrada con disableAnimations=true (S5-R3)',
|
|
(tester) async {
|
|
await _montarPantalla(tester, disableAnimations: true);
|
|
|
|
expect(find.byType(PluriWaveScaffold), findsOneWidget);
|
|
expect(
|
|
find.byType(Animate),
|
|
findsNothing,
|
|
reason: 'reduced motion debe omitir la animacion de entrada',
|
|
);
|
|
final l10n = lookupAppLocalizations(const Locale('es'));
|
|
expect(find.text(l10n.stopAlarmAction), findsOneWidget);
|
|
},
|
|
);
|
|
|
|
group('WU11 — restyle a pantalla completa', () {
|
|
testWidgets(
|
|
'el tiempo gigante usa PluriWaveTypography.heroTime envuelto en '
|
|
'FittedBox(scaleDown)',
|
|
(tester) async {
|
|
await _montarPantalla(tester);
|
|
|
|
final heroFinder = find.byKey(const ValueKey('ringing-hero-time'));
|
|
expect(heroFinder, findsOneWidget);
|
|
final texto = tester.widget<Text>(heroFinder);
|
|
final contexto = tester.element(heroFinder);
|
|
expect(texto.style, contexto.pluriType.heroTime);
|
|
expect(
|
|
find.ancestor(of: heroFinder, matching: find.byType(FittedBox)),
|
|
findsOneWidget,
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'el fondo es arte difuminado a pantalla completa; el contenedor '
|
|
'glass-card anterior desaparece',
|
|
(tester) async {
|
|
await _montarPantalla(tester);
|
|
|
|
expect(
|
|
find.byKey(const ValueKey('ringing-background-art')),
|
|
findsOneWidget,
|
|
);
|
|
expect(find.byType(PluriGlassSurface), findsNothing);
|
|
},
|
|
);
|
|
});
|
|
}
|