fix(execution): reprise en temps réel de la séance en cours (ticket #14)
Corrige la reprise de séance sur workout_execution_screen.dart suite aux retours de QA. flutter analyze propre, 26/26 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -115,6 +115,108 @@ void main() {
|
||||
|
||||
expect(find.text('Pas de repos'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('un repos actif est repris avec le temps restant recalculé', (
|
||||
tester,
|
||||
) async {
|
||||
final clock = _FakeClock(DateTime.utc(2026, 7, 17, 12));
|
||||
final repository = _FakeActiveSessionRepository();
|
||||
final session = ActiveWorkoutSession(
|
||||
metadata: _metadata('session-1'),
|
||||
sourceWorkoutTemplateId: 'template-1',
|
||||
status: ActiveWorkoutStatus.running,
|
||||
startedAt: DateTime.utc(2026, 7, 17, 12),
|
||||
lastPersistedAt: DateTime.utc(2026, 7, 17, 12),
|
||||
elapsedActiveMs: 0,
|
||||
currentProgramIndex: 0,
|
||||
currentExerciseIndex: 0,
|
||||
currentSetIndex: 0,
|
||||
resolvedTemplateSnapshotJson: _sessionSnapshot(
|
||||
setsCount: 2,
|
||||
restSeconds: 60,
|
||||
),
|
||||
);
|
||||
repository.session = session;
|
||||
repository.restStates.add(
|
||||
ActiveRestState(
|
||||
metadata: _metadata('rest-1'),
|
||||
activeWorkoutSessionId: 'session-1',
|
||||
afterProgramIndex: 0,
|
||||
afterExerciseIndex: 0,
|
||||
afterSetIndex: 0,
|
||||
plannedRestSeconds: 60,
|
||||
adjustedRestSeconds: 60,
|
||||
startedAt: DateTime.now().toUtc().subtract(const Duration(seconds: 15)),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: WorkoutExecutionScreen(
|
||||
initialSession: session,
|
||||
activeUseCases: _activeUseCases(repository, clock),
|
||||
closeUseCase: _closeUseCase(repository, clock),
|
||||
historyUseCases: _historyUseCases(clock),
|
||||
workoutTemplateUseCases: _workoutTemplateUseCases(),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Repos avant la prochaine série'), findsOneWidget);
|
||||
expect(find.text('00:45'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('+15 s'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('01:00'), findsOneWidget);
|
||||
expect(repository.restStates.single.adjustedRestSeconds, 75);
|
||||
|
||||
await tester.tap(find.text('Ignorer le repos'));
|
||||
await tester.pump();
|
||||
|
||||
expect(repository.restStates.single.skippedAt, isNotNull);
|
||||
});
|
||||
|
||||
testWidgets('la mesure temps enregistre une durée réelle de série', (
|
||||
tester,
|
||||
) async {
|
||||
final clock = _FakeClock(DateTime.utc(2026, 7, 17, 12));
|
||||
final repository = _FakeActiveSessionRepository();
|
||||
final session = ActiveWorkoutSession(
|
||||
metadata: _metadata('session-1'),
|
||||
sourceWorkoutTemplateId: 'template-1',
|
||||
status: ActiveWorkoutStatus.running,
|
||||
startedAt: DateTime.utc(2026, 7, 17, 12),
|
||||
lastPersistedAt: DateTime.utc(2026, 7, 17, 12),
|
||||
elapsedActiveMs: 0,
|
||||
currentProgramIndex: 0,
|
||||
currentExerciseIndex: 0,
|
||||
currentSetIndex: 0,
|
||||
resolvedTemplateSnapshotJson: _sessionSnapshot(
|
||||
setsCount: 2,
|
||||
restSeconds: 0,
|
||||
),
|
||||
);
|
||||
repository.session = session;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: WorkoutExecutionScreen(
|
||||
initialSession: session,
|
||||
activeUseCases: _activeUseCases(repository, clock),
|
||||
closeUseCase: _closeUseCase(repository, clock),
|
||||
historyUseCases: _historyUseCases(clock),
|
||||
workoutTemplateUseCases: _workoutTemplateUseCases(),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump(const Duration(seconds: 2));
|
||||
await tester.tap(find.text('Terminer la série'));
|
||||
await tester.pump();
|
||||
|
||||
expect(repository.results.single.actualTimeMs, greaterThan(0));
|
||||
});
|
||||
}
|
||||
|
||||
ActiveWorkoutSessionUseCases _activeUseCases(
|
||||
@ -221,6 +323,7 @@ final class _FakeIds implements IdGenerator {
|
||||
final class _FakeActiveSessionRepository implements ActiveSessionRepository {
|
||||
ActiveWorkoutSession? session;
|
||||
final results = <ActiveSetResult>[];
|
||||
final restStates = <ActiveRestState>[];
|
||||
|
||||
@override
|
||||
Future<ActiveWorkoutSession?> findById(String id) async {
|
||||
@ -231,11 +334,20 @@ final class _FakeActiveSessionRepository implements ActiveSessionRepository {
|
||||
Future<ActiveWorkoutSession?> findOpen() async => session;
|
||||
|
||||
@override
|
||||
Future<ActiveRestState?> findRestStateById(String id) async => null;
|
||||
Future<ActiveRestState?> findRestStateById(String id) async {
|
||||
for (final restState in restStates) {
|
||||
if (restState.metadata.id == id) {
|
||||
return restState;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ActiveRestState>> listRestStates(String sessionId) async {
|
||||
return const [];
|
||||
return restStates
|
||||
.where((restState) => restState.activeWorkoutSessionId == sessionId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -249,7 +361,16 @@ final class _FakeActiveSessionRepository implements ActiveSessionRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveRestState(ActiveRestState restState) async {}
|
||||
Future<void> saveRestState(ActiveRestState restState) async {
|
||||
final index = restStates.indexWhere(
|
||||
(saved) => saved.metadata.id == restState.metadata.id,
|
||||
);
|
||||
if (index == -1) {
|
||||
restStates.add(restState);
|
||||
} else {
|
||||
restStates[index] = restState;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSetResult(ActiveSetResult result) async {
|
||||
|
||||
Reference in New Issue
Block a user