fix(programme): étapes d'exercice jamais affichées en séance (ticket #72)

_ProgramExerciseDraft dans program_screen.dart (état interne du
formulaire de composition de programme) n'avait jamais de champ
exerciseStepsSnapshot depuis sa création, avant le chantier "Exercice
à plusieurs étapes" (#54). Les étapes d'un exercice étaient donc
systématiquement perdues à chaque composition/édition de programme
via l'écran réel, peu importe l'ordre des opérations — d'où l'absence
totale d'étapes affichées en séance. Ajoute le champ manquant et 2
tests de non-régression exerçant le vrai flux UI ProgramFormScreen.
flutter pub get OK, dart format appliqué (aucun changement), analyze
propre (mêmes infos préexistantes), 135/135 tests verts, build APK
debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 08:10:19 +02:00
parent a28080e2a7
commit adf56ec9e0
2 changed files with 163 additions and 0 deletions

View File

@ -164,6 +164,148 @@ void main() {
},
);
testWidgets(
'ajouter un exercice avec étapes conserve le snapshot des étapes',
(tester) async {
final steps = [
_step(
id: 'step-1',
position: 0,
name: 'Course',
type: ExerciseStepType.time,
),
_step(
id: 'step-2',
position: 1,
name: 'Tirs',
type: ExerciseStepType.reps,
),
];
final programRepository = _FakeProgramRepository();
final exerciseRepository = _FakeExerciseRepository()
..exercises.add(
Exercise(
metadata: _metadata('exercise-steps'),
name: 'Circuit tirs',
hasTimeMeasure: false,
hasRepsMeasure: true,
hasScoreMeasure: false,
defaultTargetReps: 2,
steps: steps,
),
);
await tester.pumpWidget(
MaterialApp(
home: ProgramFormScreen(
programUseCases: _programUseCases(
programRepository,
exerciseRepository,
),
exerciseUseCases: _exerciseUseCases(exerciseRepository),
),
),
);
await tester.enterText(
find.widgetWithText(TextFormField, 'Nom'),
'Programme étapes',
);
await tester.tap(find.text('Ajouter un exercice'));
await tester.pumpAndSettle();
await tester.tap(find.text('Circuit tirs'));
await tester.pumpAndSettle();
await tester.tap(find.text('Enregistrer'));
await tester.pumpAndSettle();
final savedSteps = programRepository
.programs
.single
.exercises
.single
.exerciseStepsSnapshot;
expect(savedSteps, hasLength(2));
expect(savedSteps.map((step) => step.name), ['Course', 'Tirs']);
expect(savedSteps.map((step) => step.type), [
ExerciseStepType.time,
ExerciseStepType.reps,
]);
},
);
testWidgets(
'réenregistrer un programme existant conserve les étapes du snapshot',
(tester) async {
final steps = [
_step(
id: 'step-1',
position: 0,
name: 'Course',
type: ExerciseStepType.time,
),
_step(
id: 'step-2',
position: 1,
name: 'Tirs',
type: ExerciseStepType.reps,
),
];
final programRepository = _FakeProgramRepository()
..programs.add(
Program(
metadata: _metadata('program-steps'),
name: 'Programme étapes',
defaultRestSeconds: 45,
exercises: [
ProgramExercise(
metadata: _metadata('program-exercise-steps'),
programId: 'program-steps',
sourceExerciseId: 'exercise-steps',
position: 0,
exerciseNameSnapshot: 'Circuit tirs',
exerciseStepsSnapshot: steps,
availableTimeSnapshot: false,
availableRepsSnapshot: true,
availableScoreSnapshot: false,
setsCount: 3,
timeEnabled: false,
repsEnabled: true,
scoreEnabled: false,
targetReps: 2,
),
],
),
);
final exerciseRepository = _FakeExerciseRepository();
await tester.pumpWidget(
MaterialApp(
home: ProgramFormScreen(
programUseCases: _programUseCases(
programRepository,
exerciseRepository,
),
exerciseUseCases: _exerciseUseCases(exerciseRepository),
program: programRepository.programs.single,
),
),
);
await tester.tap(find.text('Enregistrer'));
await tester.pumpAndSettle();
final savedSteps = programRepository
.programs
.single
.exercises
.single
.exerciseStepsSnapshot;
expect(savedSteps, hasLength(2));
expect(savedSteps.map((step) => step.name), ['Course', 'Tirs']);
expect(savedSteps.map((step) => step.position), [0, 1]);
},
);
testWidgets('affiche lobjectif de chrono pour un score en mode chrono', (
tester,
) async {
@ -510,6 +652,21 @@ EntityMetadata _metadata(String id) {
);
}
ExerciseStep _step({
required String id,
required int position,
required String name,
required ExerciseStepType type,
}) {
return ExerciseStep(
id: id,
position: position,
name: name,
type: type,
defaultTargetValue: type == ExerciseStepType.time ? 30 : 5,
);
}
final class _FakeClock implements Clock {
const _FakeClock(this.value);