feat(ui): édition ponctuelle des séries en séance (ticket #23)
Expose l'édition ponctuelle des séries (backend du ticket #21) sur workout_execution_screen.dart, sans impacter le modèle de séance. flutter analyze propre, 33/33 tests verts, build APK debug validé. Clôture le lot navigation libre / programme simplifié (tickets #20 à #23). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -280,6 +280,123 @@ void main() {
|
||||
|
||||
expect(find.text('Plan de séance'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'modifier une série passée la termine sans changer la position courante',
|
||||
(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: 2,
|
||||
resolvedTemplateSnapshotJson: _sessionSnapshot(
|
||||
setsCount: 4,
|
||||
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.tap(find.byTooltip('Voir le plan'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.widgetWithText(ListTile, 'Série 2'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Modifier la série'), findsOneWidget);
|
||||
|
||||
await tester.enterText(_textFieldWithLabel('Répétitions'), '12');
|
||||
await tester.ensureVisible(find.text('Enregistrer le résultat'));
|
||||
await tester.tap(find.text('Enregistrer le résultat'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(repository.session?.currentSetIndex, 2);
|
||||
expect(repository.results.single.status, SetResultStatus.completed);
|
||||
expect(repository.results.single.actualReps, 12);
|
||||
expect(find.text('Série mise à jour'), findsWidgets);
|
||||
expect(find.text('12 reps'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byTooltip('Fermer'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.text('Programme 1/1 · Exercice 1/1 · Série 3/4'),
|
||||
findsOneWidget,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'toucher la série courante dans le plan revient à l’écran principal',
|
||||
(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: 1,
|
||||
resolvedTemplateSnapshotJson: _sessionSnapshot(
|
||||
setsCount: 3,
|
||||
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.tap(find.byTooltip('Voir le plan'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('En cours'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Plan de séance'), findsNothing);
|
||||
expect(find.text('Modifier la série'), findsNothing);
|
||||
expect(
|
||||
find.text('Programme 1/1 · Exercice 1/1 · Série 2/3'),
|
||||
findsOneWidget,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Finder _textFieldWithLabel(String label) {
|
||||
return find.byWidgetPredicate(
|
||||
(widget) => widget is TextField && widget.decoration?.labelText == label,
|
||||
description: 'TextField with label "$label"',
|
||||
);
|
||||
}
|
||||
|
||||
ActiveWorkoutSessionUseCases _activeUseCases(
|
||||
@ -437,7 +554,18 @@ final class _FakeActiveSessionRepository implements ActiveSessionRepository {
|
||||
|
||||
@override
|
||||
Future<void> saveSetResult(ActiveSetResult result) async {
|
||||
results.add(result);
|
||||
final index = results.indexWhere(
|
||||
(saved) =>
|
||||
saved.activeWorkoutSessionId == result.activeWorkoutSessionId &&
|
||||
saved.programIndex == result.programIndex &&
|
||||
saved.exerciseIndex == result.exerciseIndex &&
|
||||
saved.setIndex == result.setIndex,
|
||||
);
|
||||
if (index == -1) {
|
||||
results.add(result);
|
||||
} else {
|
||||
results[index] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user