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/views/player_view.dart'; import '../support/test_app.dart'; import '../support/test_database.dart'; void main() { late PlanRepository plans; late SessionRepository sessions; late FakePlayerEffects effects; setUp(() { final db = testDatabase(); addTearDown(db.close); plans = PlanRepository(db); sessions = SessionRepository(db); effects = FakePlayerEffects(); }); Future startPlan(WidgetTester tester, Mode mode, List items) async { final plan = await plans.create(mode, 'Routine'); await plans.save(plan.copyWith(items: items)); usePhoneScreen(tester); await pumpApp(tester, plans: plans, sessions: sessions, effects: effects, prefs: {'mode': mode.name}); await tester.tap(find.byTooltip('Start')); await tester.pump(); await tester.pump(); } /// Drift's streams need real time to deliver, outside the test's fake clock. Future> sessionsOf(WidgetTester tester, Mode mode) async => (await tester.runAsync(() => sessions.watchSessions(mode).first))!; /// The Player's ticker never lets frames settle, so it is pumped for a fixed time. Future settle(WidgetTester tester) async { for (var i = 0; i < 8; i++) { await tester.pump(const Duration(milliseconds: 100)); } } Future wait(WidgetTester tester, int seconds) async { for (var i = 0; i < seconds * 5; i++) { await tester.pump(const Duration(milliseconds: 200)); } } testWidgets('a Stretching Session counts down, chimes, keeps the screen on and ends on its summary', (tester) async { await startPlan(tester, Mode.stretching, const [ StretchingStep(exerciseId: 'Calf_Stretch', holdSeconds: 10, bothSides: false, rounds: 1), ]); expect(find.text('Get ready'), findsOneWidget); expect(effects.screenOn, isTrue); // After the get-ready and the 0.7 s crossfade into the hold. await wait(tester, 5); await settle(tester); expect(find.text('Calf Stretch'), findsOneWidget); expect(find.text('Get ready'), findsNothing); await wait(tester, 10); await settle(tester); expect(find.text('Session complete'), findsOneWidget); expect(effects.chimes, [Mode.stretching, Mode.stretching]); expect(effects.screenOn, isFalse); expect((await sessionsOf(tester, Mode.stretching)).single.complete, isTrue); }); testWidgets('a Workout set is confirmed with its logged reps, then rested and repeated', (tester) async { await startPlan(tester, Mode.workout, const [ WorkoutStep(exerciseId: 'Barbell_Squat', sets: 2, target: WorkoutTarget.reps(10), weightKg: 60), ]); expect(find.text('Set 1 of 2'), findsOneWidget); expect(find.text('10 reps · 60 kg'), findsOneWidget); await tester.tap(find.text('Set done')); await settle(tester); await tester.tap(find.byTooltip('Reps −')); await tester.pump(); await tester.tap(find.text('Save set')); await settle(tester); expect(find.text('Rest'), findsOneWidget); await wait(tester, 3); expect(find.text('0:03'), findsOneWidget); await tester.tap(find.text('Start next set')); await settle(tester); await tester.tap(find.text('Set done')); await settle(tester); await tester.tap(find.text('Save set')); await settle(tester); expect(find.text('Session complete'), findsOneWidget); expect(find.text('Set 1 · 9 reps · 60 kg'), findsOneWidget); expect(find.text('Set 2 · 10 reps · 60 kg'), findsOneWidget); }); testWidgets('ending early after a set saves an unfinished Session', (tester) async { await startPlan(tester, Mode.workout, const [ WorkoutStep(exerciseId: 'Barbell_Squat', sets: 3, target: WorkoutTarget.reps(10)), ]); await tester.tap(find.text('Set done')); await settle(tester); await tester.tap(find.text('Save set')); await settle(tester); await tester.tap(find.byTooltip('End session')); await settle(tester); await tester.tap(find.text('End session').last); await settle(tester); expect(find.text('Ended early'), findsOneWidget); expect((await sessionsOf(tester, Mode.workout)).single.complete, isFalse); }); testWidgets('skipping through everything leaves no Session behind', (tester) async { await startPlan(tester, Mode.workout, const [ WorkoutStep(exerciseId: 'Barbell_Squat', sets: 1, target: WorkoutTarget.reps(10)), ]); await tester.tap(find.byTooltip('Skip')); await settle(tester); expect(find.byType(PlayerView), findsNothing); expect(await sessionsOf(tester, Mode.workout), isEmpty); }); testWidgets('ending before anything is done leaves no Session behind', (tester) async { await startPlan(tester, Mode.workout, const [ WorkoutStep(exerciseId: 'Barbell_Squat', sets: 3, target: WorkoutTarget.reps(10)), ]); await tester.tap(find.byTooltip('End session')); await settle(tester); await tester.tap(find.text('End session').last); await settle(tester); expect(find.byType(PlayerView), findsNothing); expect(await sessionsOf(tester, Mode.workout), isEmpty); }); }