feat(exécution): chrono du score chronométré pendant la séance (ticket #27)
Ajoute le chrono du score chronométré (fondation domain/config des tickets #25/#26) sur workout_execution_screen.dart, avec les use cases associés. flutter analyze propre, 75/75 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -241,6 +241,160 @@ void main() {
|
||||
expect(find.text('Image 2'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('le chrono score démarre et s’arrête avec la bonne durée', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.binding.setSurfaceSize(const Size(400, 1400));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
|
||||
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(
|
||||
scoreInputMode: ScoreInputMode.stopwatch,
|
||||
),
|
||||
);
|
||||
repository.session = session;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: WorkoutExecutionScreen(
|
||||
initialSession: session,
|
||||
activeUseCases: _activeUseCases(repository, clock),
|
||||
closeUseCase: _closeUseCase(repository, clock),
|
||||
historyUseCases: _historyUseCases(clock),
|
||||
workoutTemplateUseCases: _workoutTemplateUseCases(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('Chrono score'), findsOneWidget);
|
||||
expect(find.text('00:00.0'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Démarrer'));
|
||||
await tester.pump();
|
||||
clock.value = DateTime.utc(2026, 7, 17, 12, 0, 1, 200);
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(find.text('00:01.2'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Arrêter'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('00:01.2'), findsOneWidget);
|
||||
expect(
|
||||
repository.scoreStopwatchStates.single.status,
|
||||
ActiveScoreStopwatchStatus.stopped,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('terminer sans chrono démarré affiche une confirmation', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.binding.setSurfaceSize(const Size(400, 1400));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
|
||||
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(
|
||||
scoreInputMode: ScoreInputMode.stopwatch,
|
||||
),
|
||||
);
|
||||
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.tap(find.text('Terminer la série'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Aucun temps chronométré'), findsOneWidget);
|
||||
expect(find.text("Tu n'as pas démarré le chrono score."), findsOneWidget);
|
||||
expect(find.text('Démarrer le chrono'), findsOneWidget);
|
||||
expect(find.text('Terminer sans chrono'), findsOneWidget);
|
||||
expect(repository.results, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('terminer avec chrono en cours l’arrête et enregistre', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.binding.setSurfaceSize(const Size(400, 1400));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
|
||||
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,
|
||||
scoreInputMode: ScoreInputMode.stopwatch,
|
||||
),
|
||||
);
|
||||
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.tap(find.text('Démarrer'));
|
||||
await tester.pump();
|
||||
clock.value = DateTime.utc(2026, 7, 17, 12, 0, 2, 500);
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
await tester.tap(find.text('Terminer la série'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(repository.results.single.actualScoreTimeMs, 2500);
|
||||
expect(
|
||||
repository.scoreStopwatchStates.single.status,
|
||||
ActiveScoreStopwatchStatus.stopped,
|
||||
);
|
||||
expect(repository.session?.currentSetIndex, 1);
|
||||
});
|
||||
|
||||
testWidgets('un repos actif est repris avec le temps restant recalculé', (
|
||||
tester,
|
||||
) async {
|
||||
@ -850,7 +1004,15 @@ final class _FakeActiveSessionRepository implements ActiveSessionRepository {
|
||||
required int exerciseIndex,
|
||||
required int setIndex,
|
||||
required DateTime deletedAt,
|
||||
}) async {}
|
||||
}) async {
|
||||
scoreStopwatchStates.removeWhere(
|
||||
(state) =>
|
||||
state.activeWorkoutSessionId == sessionId &&
|
||||
state.programIndex == programIndex &&
|
||||
state.exerciseIndex == exerciseIndex &&
|
||||
state.setIndex == setIndex,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSetResult(ActiveSetResult result) async {
|
||||
|
||||
Reference in New Issue
Block a user