feat(ui): réglages auto-enchaînement exercice, programme et séance (ticket #77)

Ajoute les réglages d'auto-enchaînement des chronos d'étapes sur
exercise_library_screen.dart, program_screen.dart et
workout_template_screen.dart, s'appuyant sur la résolution du ticket
#76. Propagation du nouveau champ vérifiée exhaustivement sur les 5
points d'impact de _ProgramExerciseDraft (constructeur, fromExercise,
fromProgramExercise, copy(), toConfig()) — pas de répétition du bug
#72. flutter pub get OK, dart format appliqué (7 fichiers reformatés),
analyze propre (mêmes infos préexistantes), 152/152 tests verts, build
APK debug validé. Développés dans le même worktree partagé par
DevBackend et DevFrontend ; feature/step-chaining-77-ui-settings
restée vide, jamais divergée.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 13:57:01 +02:00
parent ea1e240c87
commit 4b355152d5
6 changed files with 505 additions and 1 deletions

View File

@ -44,6 +44,117 @@ void main() {
},
);
testWidgets(
'personnaliser lenchaînement automatique transmet loverride séance',
(tester) async {
final program = WorkoutTemplateProgramDraft(
clientKey: 'program-1',
programNameSnapshot: 'Circuit',
defaultRestSecondsSnapshot: 60,
programSnapshotJson: '{}',
exercises: [
WorkoutTemplateExerciseDraft(
snapshotExerciseId: 'exercise-1',
exerciseNameSnapshot: 'Navettes',
setsCount: 3,
timeEnabled: true,
repsEnabled: false,
scoreEnabled: false,
exerciseStepsSnapshot: [
_step(
id: 'step-1',
position: 0,
name: 'Course',
type: ExerciseStepType.time,
),
_step(
id: 'step-2',
position: 1,
name: 'Retour',
type: ExerciseStepType.time,
),
],
autoStartNextTimedStepSnapshot: true,
targetTimeSeconds: 30,
),
],
);
await tester.binding.setSurfaceSize(const Size(400, 1400));
addTearDown(() => tester.binding.setSurfaceSize(null));
await tester.pumpWidget(
MaterialApp(home: WorkoutTemplateProgramDetailScreen(program: program)),
);
await tester.tap(
find.widgetWithText(
SwitchListTile,
'Enchaîner automatiquement les chronos consécutifs',
),
);
await tester.pump();
expect(
find.textContaining('Personnalisé pour cette séance'),
findsOneWidget,
);
final override = program.toOverrides().single;
expect(override.autoStartNextTimedStepOverride, isFalse);
},
);
testWidgets(
'revenir au réglage du programme remet loverride séance à null',
(tester) async {
final program = WorkoutTemplateProgramDraft(
clientKey: 'program-1',
programNameSnapshot: 'Circuit',
defaultRestSecondsSnapshot: 60,
programSnapshotJson: '{}',
exercises: [
WorkoutTemplateExerciseDraft(
snapshotExerciseId: 'exercise-1',
exerciseNameSnapshot: 'Navettes',
setsCount: 3,
timeEnabled: true,
repsEnabled: false,
scoreEnabled: false,
exerciseStepsSnapshot: [
_step(
id: 'step-1',
position: 0,
name: 'Course',
type: ExerciseStepType.time,
),
],
autoStartNextTimedStepSnapshot: false,
autoStartNextTimedStepOverride: true,
targetTimeSeconds: 30,
),
],
);
await tester.binding.setSurfaceSize(const Size(400, 1400));
addTearDown(() => tester.binding.setSurfaceSize(null));
await tester.pumpWidget(
MaterialApp(home: WorkoutTemplateProgramDetailScreen(program: program)),
);
expect(
find.textContaining('Personnalisé pour cette séance'),
findsOneWidget,
);
await tester.tap(find.text('Revenir au réglage du programme'));
await tester.pump();
expect(find.textContaining('Réglage du programme'), findsOneWidget);
expect(program.toOverrides(), isEmpty);
},
);
testWidgets(
'lancer une séance alors quune autre est en cours affiche un message',
(tester) async {
@ -308,6 +419,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);