import 'package:flutter_test/flutter_test.dart'; import 'package:zenxercise/src/modes/mode.dart'; import 'package:zenxercise/src/plans/models/plan.dart'; Plan _plan(Mode mode, List items) => Plan( id: 'p1', mode: mode, name: 'Push day', items: items, createdAt: DateTime(2026, 10, 1), updatedAt: DateTime(2026, 10, 1), ); WorkoutStep _w(String id, {int sets = 3, WorkoutTarget target = const WorkoutTarget.reps(10), double? kg}) => WorkoutStep(exerciseId: id, sets: sets, target: target, weightKg: kg); void main() { group('adding Exercises', () { test('a Workout Plan adds a Workout Step of 3 × 10 reps without weight', () { final plan = _plan(Mode.workout, const []).withExerciseAdded('Bench_Press'); expect(plan.items.single, _w('Bench_Press')); }); test('a Stretching Plan adds a single 30 s hold on one side', () { final plan = _plan(Mode.stretching, const []).withExerciseAdded('Calf_Stretch'); expect( plan.items.single, const StretchingStep(exerciseId: 'Calf_Stretch', holdSeconds: 30, bothSides: false, rounds: 1), ); }); }); test('moving follows the list convention of an index past the removed item', () { final plan = _plan(Mode.workout, [_w('A'), _w('B'), _w('C')]); expect(plan.withItemMoved(0, 3).items, [_w('B'), _w('C'), _w('A')]); expect(plan.withItemMoved(2, 0).items, [_w('C'), _w('A'), _w('B')]); expect(plan.withItemRemoved(1).items, [_w('A'), _w('C')]); }); group('Supersets', () { test('a Step grouped with the next keeps each target and takes the first set count', () { final plan = _plan(Mode.workout, [ _w('A', sets: 4, kg: 20), _w('B', sets: 2, target: const WorkoutTarget.seconds(45)), _w('C'), ]).withSupersetWithNext(0); expect(plan.items, [ const Superset( sets: 4, restBetween: false, members: [ SupersetMember(exerciseId: 'A', target: WorkoutTarget.reps(10), weightKg: 20), SupersetMember(exerciseId: 'B', target: WorkoutTarget.seconds(45)), ], ), _w('C'), ]); expect(plan.exerciseCount, 3); }); test('grouping again with the next item grows the Superset, also when the next is a Superset', () { final grown = _plan(Mode.workout, [_w('A'), _w('B'), _w('C')]).withSupersetWithNext(0).withSupersetWithNext(0); expect((grown.items.single as Superset).members.map((m) => m.exerciseId), ['A', 'B', 'C']); final merged = _plan(Mode.workout, [_w('A'), _w('B'), _w('C')]).withSupersetWithNext(1).withSupersetWithNext(0); expect((merged.items.single as Superset).members.map((m) => m.exerciseId), ['A', 'B', 'C']); }); test('only an item with a next item can be grouped', () { final plan = _plan(Mode.workout, [_w('A'), _w('B')]); expect(plan.canSupersetWithNext(0), isTrue); expect(plan.canSupersetWithNext(1), isFalse); expect(_plan(Mode.stretching, const []).withExerciseAdded('X').canSupersetWithNext(0), isFalse); }); test('a member taken out lands right after its Superset with the Superset set count', () { final plan = _plan(Mode.workout, [ _w('A'), _w('B'), _w('C'), _w('D'), ]).withSupersetWithNext(0).withSupersetWithNext(0).withMemberRemovedFromSuperset(0, 0); expect((plan.items[0] as Superset).members.map((m) => m.exerciseId), ['B', 'C']); expect(plan.items[1], _w('A')); expect(plan.items[2], _w('D')); }); test('a Superset left with one member turns back into a Step', () { final plan = _plan(Mode.workout, [ _w('A', sets: 5), _w('B'), ]).withSupersetWithNext(0).withMemberRemovedFromSuperset(0, 1); expect(plan.items, [_w('A', sets: 5), _w('B', sets: 5)]); }); test('ungrouping gives every member its own Step with the Superset set count', () { final plan = _plan(Mode.workout, [_w('A', sets: 4), _w('B')]).withSupersetWithNext(0).withUngrouped(0); expect(plan.items, [_w('A', sets: 4), _w('B', sets: 4)]); }); }); test('a Stretching Plan estimates its length from holds, sides, rounds and Transitions', () { final plan = _plan(Mode.stretching, const [ StretchingStep(exerciseId: 'A', holdSeconds: 30, bothSides: true, rounds: 2), StretchingStep(exerciseId: 'B', holdSeconds: 60, bothSides: false, rounds: 1), ]); // Holds: 4 × 30 s + 60 s = 180 s; 5 holds have 4 Transitions of 5 s between them. expect(plan.estimatedDuration, const Duration(seconds: 200)); expect(_plan(Mode.workout, [_w('A')]).estimatedDuration, isNull); }); test('items survive a JSON round trip', () { final items = [ _w('A', kg: 12.5), const StretchingStep(exerciseId: 'B', holdSeconds: 45, bothSides: true, rounds: 3), const Superset( sets: 3, restBetween: true, members: [ SupersetMember(exerciseId: 'C', target: WorkoutTarget.seconds(30)), SupersetMember(exerciseId: 'D', target: WorkoutTarget.reps(8), weightKg: 40), ], ), ]; expect(PlanItem.listFromJson(PlanItem.listToJson(items)), items); }); }