Stage 4: guided Player for both Modes and the Session summary
- buildSegments lays a Plan out as holds with Transitions (get ready, switch sides, next round, next exercise) or sets with count-up Rests; Supersets alternate members and rest per round unless asked otherwise - SessionRunner drives segments against an injected clock: auto-advance with a chime, pause, skip, back, rep sets confirmed with logged reps and kg, timed sets started on demand, early end - Player screen: countdown ring, accent pulse in the last 3 s, Flip Animation, next-up cards, Rest Screen, mute toggle, screen kept on - Sessions table (schema v2 migration) keeps a Plan snapshot and Set Logs; nothing is saved when no hold or set was done - Summary with time, exercises or sets logged, and every Set Log - Synthesised quiet cue sounds (tools/sounds), status bar per Mode Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bbdca95541
commit
971ff64117
28 files changed
+3465
-84
No files matched your search
@@ -0,0 +1,58 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:zenxercise/src/database/database.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/player/session_runner.dart';
|
||||
|
||||
import '../support/test_database.dart';
|
||||
|
||||
void main() {
|
||||
late AppDatabase db;
|
||||
late SessionRepository sessions;
|
||||
|
||||
setUp(() {
|
||||
db = testDatabase();
|
||||
sessions = SessionRepository(db);
|
||||
});
|
||||
tearDown(() => db.close());
|
||||
|
||||
Plan plan(Mode mode) => Plan(
|
||||
id: 'p1',
|
||||
mode: mode,
|
||||
name: 'Legs',
|
||||
items: const [WorkoutStep(exerciseId: 'Squat', sets: 1, target: WorkoutTarget.reps(10))],
|
||||
createdAt: DateTime(2026),
|
||||
updatedAt: DateTime(2026),
|
||||
);
|
||||
|
||||
SessionResult result(Mode mode, DateTime end, {bool complete = true}) => SessionResult(
|
||||
plan: plan(mode),
|
||||
startedAt: end.subtract(const Duration(minutes: 30)),
|
||||
endedAt: end,
|
||||
duration: const Duration(minutes: 28),
|
||||
complete: complete,
|
||||
setLogs: const [SetLog(exerciseId: 'Squat', set: 1, reps: 8, weightKg: 60)],
|
||||
);
|
||||
|
||||
test('a saved Session keeps its Plan snapshot, logs and outcome', () async {
|
||||
final saved = await sessions.save(result(Mode.workout, DateTime(2026, 10, 29, 19), complete: false));
|
||||
|
||||
expect(saved.planName, 'Legs');
|
||||
expect(saved.items.single, isA<WorkoutStep>());
|
||||
expect(saved.duration, const Duration(minutes: 28));
|
||||
expect(saved.complete, isFalse);
|
||||
expect(saved.setLogs.single.reps, 8);
|
||||
expect(saved.setLogs.single.weightKg, 60);
|
||||
});
|
||||
|
||||
test('Sessions are listed per Mode, the latest first', () async {
|
||||
await sessions.save(result(Mode.workout, DateTime(2026, 10, 28)));
|
||||
await sessions.save(result(Mode.workout, DateTime(2026, 10, 29)));
|
||||
await sessions.save(result(Mode.stretching, DateTime(2026, 10, 30)));
|
||||
|
||||
final workout = await sessions.watchSessions(Mode.workout).first;
|
||||
|
||||
expect(workout.map((s) => s.endedAt.day), [29, 28]);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user