Stage 3: Plan editor with Steps, Supersets and reordering
- Plan model with immutable edits: add with Mode defaults, move, remove, Superset with next, take a member out, ungroup; Stretching length estimate from holds, sides, rounds and Transitions - drift database (plans table, items as JSON, ADR 0005) and PlanRepository; Plans list per Mode with rename/duplicate/delete - Editor: drag handles, step editor sheet (sets, reps or time, optional kg; hold, both sides, rounds; Superset sets and rest between), pause between holds for Stretching Plans - Exercise picker reusing the Library browser; Add to plan from the Exercise detail page - Theme: Mode-styled snackbar, FAB, menus, segmented buttons, switches Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
786dd52e30
commit
bbdca95541
28 files changed
+3654
-11
No files matched your search
@@ -0,0 +1,64 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:zenxercise/src/database/database.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 '../support/test_database.dart';
|
||||
|
||||
void main() {
|
||||
late AppDatabase db;
|
||||
late PlanRepository plans;
|
||||
var now = DateTime(2026, 10, 1, 8);
|
||||
|
||||
setUp(() {
|
||||
db = testDatabase();
|
||||
now = DateTime(2026, 10, 1, 8);
|
||||
plans = PlanRepository(db, clock: () => now);
|
||||
});
|
||||
tearDown(() => db.close());
|
||||
|
||||
test('a created Plan is empty, belongs to its Mode and is listed newest first', () async {
|
||||
final morning = await plans.create(Mode.stretching, 'Morning');
|
||||
now = now.add(const Duration(hours: 1));
|
||||
final evening = await plans.create(Mode.stretching, 'Evening');
|
||||
await plans.create(Mode.workout, 'Legs');
|
||||
|
||||
final listed = await plans.watchPlans(Mode.stretching).first;
|
||||
|
||||
expect(listed.map((p) => p.name), ['Evening', 'Morning']);
|
||||
expect(morning.items, isEmpty);
|
||||
expect(evening.transitionSeconds, Plan.defaultTransitionSeconds);
|
||||
});
|
||||
|
||||
test('saving keeps the items and stamps the change time', () async {
|
||||
final plan = await plans.create(Mode.workout, 'Push');
|
||||
now = DateTime(2026, 10, 2);
|
||||
await plans.save(plan.withExerciseAdded('Bench').withExerciseAdded('Dips').withSupersetWithNext(0));
|
||||
|
||||
final stored = (await plans.get(plan.id))!;
|
||||
|
||||
expect(stored.items.single, isA<Superset>());
|
||||
expect(stored.updatedAt, DateTime(2026, 10, 2));
|
||||
expect(stored.createdAt, plan.createdAt);
|
||||
});
|
||||
|
||||
test('a duplicate copies the items under a new id and name', () async {
|
||||
final plan = await plans.create(Mode.workout, 'Push');
|
||||
await plans.save(plan.withExerciseAdded('Bench'));
|
||||
|
||||
final copy = await plans.duplicate(plan.id, 'Push (copy)');
|
||||
|
||||
expect(copy.id, isNot(plan.id));
|
||||
expect(copy.items, (await plans.get(plan.id))!.items);
|
||||
expect((await plans.watchPlans(Mode.workout).first), hasLength(2));
|
||||
});
|
||||
|
||||
test('a deleted Plan is gone', () async {
|
||||
final plan = await plans.create(Mode.workout, 'Push');
|
||||
|
||||
await plans.delete(plan.id);
|
||||
|
||||
expect(await plans.get(plan.id), isNull);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user