import 'package:clock/clock.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zenxercise/src/history/session_repository.dart'; import 'package:zenxercise/src/modes/mode.dart'; import 'package:zenxercise/src/plans/models/plan.dart'; import 'package:zenxercise/src/plans/plan_repository.dart'; import 'package:zenxercise/src/player/session_runner.dart'; import '../support/test_app.dart'; import '../support/test_database.dart'; /// Noon, so the Grace Hour never shifts what counts as today. final noon = DateTime(2026, 10, 29, 12); void main() { late PlanRepository plans; late SessionRepository sessions; late FakeNotifier notifier; setUp(() { final db = testDatabase(); addTearDown(db.close); plans = PlanRepository(db); sessions = SessionRepository(db); notifier = FakeNotifier(); }); Plan plan(Mode mode) => Plan( id: 'p1', mode: mode, name: 'Legs', items: const [WorkoutStep(exerciseId: 'Barbell_Squat', sets: 1, target: WorkoutTarget.reps(10))], createdAt: DateTime(2026), updatedAt: DateTime(2026), ); Future finished(WidgetTester tester, Mode mode, DateTime end, {bool complete = true}) async => tester.runAsync( () => sessions.save( SessionResult( plan: plan(mode), startedAt: end.subtract(const Duration(minutes: 20)), endedAt: end, duration: const Duration(minutes: 20), complete: complete, setLogs: const [SetLog(exerciseId: 'Barbell_Squat', set: 1, reps: 10)], ), ), ); /// Lets drift's streams deliver (they need real time), then settles. Future settle(WidgetTester tester) async { await tester.runAsync(() => Future.delayed(const Duration(milliseconds: 50))); await tester.pumpAndSettle(); } testWidgets('the flame counts finished days across both Modes and opens the Streak panel', (tester) async { await withClock(Clock.fixed(noon), () async { await finished(tester, Mode.workout, noon.subtract(const Duration(days: 1))); await finished(tester, Mode.stretching, noon); usePhoneScreen(tester); await pumpApp(tester, plans: plans, sessions: sessions, notifier: notifier); await settle(tester); expect(find.descendant(of: find.byTooltip('Streak'), matching: find.text('2')), findsOneWidget); await tester.tap(find.byTooltip('Streak')); await settle(tester); expect(find.text('2-day streak'), findsOneWidget); expect(find.text('Today is done.'), findsOneWidget); expect(find.text('Freezes: 0 of 2'), findsOneWidget); }); }); testWidgets('History lists the Mode\'s Sessions by day and reopens a summary', (tester) async { await withClock(Clock.fixed(noon), () async { await finished(tester, Mode.workout, noon, complete: false); await finished(tester, Mode.stretching, noon); usePhoneScreen(tester); await pumpApp(tester, plans: plans, sessions: sessions, prefs: {'mode': 'workout'}); await tester.tap(find.byTooltip('History')); await settle(tester); expect(find.text('Today'), findsOneWidget); expect(find.text('Legs'), findsOneWidget); expect(find.text('Ended early'), findsOneWidget); await tester.tap(find.text('Legs')); await settle(tester); expect(find.text('Set 1 ยท 10 reps'), findsOneWidget); }); }); testWidgets('switching a Reminder off in Settings drops its notifications', (tester) async { await withClock(Clock.fixed(noon), () async { usePhoneScreen(tester); await pumpApp(tester, plans: plans, sessions: sessions, notifier: notifier); await tester.tap(find.byTooltip('Settings')); await tester.pumpAndSettle(); expect(find.text('19:00'), findsOneWidget); expect(find.text('22:30'), findsOneWidget); await tester.tap(find.byType(Switch).at(1)); await settle(tester); expect(notifier.permissionRequests, 0); expect(notifier.scheduled, isNotEmpty); expect(notifier.scheduled.every((r) => r.$1.hour == 19), isTrue); }); }); }