merge(main): étapes d'exercice jamais affichées en séance (ticket #72)
Fusionne feature/bugfix-72-program-steps-snapshot — pub get OK, dart format appliqué (aucun changement), analyze propre (mêmes infos préexistantes), 135/135 tests verts (dont 2 nouveaux tests de non-régression exerçant le vrai flux UI ProgramFormScreen), build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
issueRef: "#71"
|
issueRef: "#71"
|
||||||
version: 3
|
version: 4
|
||||||
updatedBy: {"kind":"user"}
|
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||||
updatedAt: 1784490850142
|
updatedAt: 1784526657344
|
||||||
---
|
---
|
||||||
|
|||||||
@ -2,15 +2,15 @@
|
|||||||
id: "b7216520-cf5b-4e65-aeed-1839980d1fe6"
|
id: "b7216520-cf5b-4e65-aeed-1839980d1fe6"
|
||||||
number: 71
|
number: 71
|
||||||
title: "[Bug] selection de score par défaut à 0"
|
title: "[Bug] selection de score par défaut à 0"
|
||||||
status: "open"
|
status: "qa"
|
||||||
priority: "critical"
|
priority: "critical"
|
||||||
sprint: null
|
sprint: null
|
||||||
links: []
|
links: []
|
||||||
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
||||||
createdBy: {"kind":"user"}
|
createdBy: {"kind":"user"}
|
||||||
updatedBy: {"kind":"user"}
|
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||||
createdAt: 1784490730564
|
createdAt: 1784490730564
|
||||||
updatedAt: 1784490850142
|
updatedAt: 1784526657344
|
||||||
version: 3
|
version: 4
|
||||||
---
|
---
|
||||||
Je dois pouvoir mettre un score par défaut à 0 lors de la création d'un exercice. Actuellement quand je met un score à 0, l'application me retourne une StateException car il y avisiblement un check qui se fait un le score > 0
|
Je dois pouvoir mettre un score par défaut à 0 lors de la création d'un exercice. Actuellement quand je met un score à 0, l'application me retourne une StateException car il y avisiblement un check qui se fait un le score > 0
|
||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
issueRef: "#74"
|
issueRef: "#74"
|
||||||
version: 3
|
version: 4
|
||||||
updatedBy: {"kind":"user"}
|
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||||
updatedAt: 1784493327494
|
updatedAt: 1784526657602
|
||||||
---
|
---
|
||||||
|
|||||||
@ -2,16 +2,16 @@
|
|||||||
id: "c18d9e4a-f6b4-4f6e-bfb5-b77c958f5534"
|
id: "c18d9e4a-f6b4-4f6e-bfb5-b77c958f5534"
|
||||||
number: 74
|
number: 74
|
||||||
title: "[Bug] Serveur non lancable"
|
title: "[Bug] Serveur non lancable"
|
||||||
status: "open"
|
status: "qa"
|
||||||
priority: "critical"
|
priority: "critical"
|
||||||
sprint: null
|
sprint: null
|
||||||
links: []
|
links: []
|
||||||
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
||||||
createdBy: {"kind":"user"}
|
createdBy: {"kind":"user"}
|
||||||
updatedBy: {"kind":"user"}
|
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||||
createdAt: 1784493302766
|
createdAt: 1784493302766
|
||||||
updatedAt: 1784493327494
|
updatedAt: 1784526657602
|
||||||
version: 3
|
version: 4
|
||||||
---
|
---
|
||||||
J'ai cette erreur quand j'essaie de build le serveur:
|
J'ai cette erreur quand j'essaie de build le serveur:
|
||||||
❯ docker compose up --build
|
❯ docker compose up --build
|
||||||
|
|||||||
@ -799,13 +799,13 @@
|
|||||||
"issueRef": "#71",
|
"issueRef": "#71",
|
||||||
"path": "71",
|
"path": "71",
|
||||||
"title": "[Bug] selection de score par défaut à 0",
|
"title": "[Bug] selection de score par défaut à 0",
|
||||||
"status": "open",
|
"status": "qa",
|
||||||
"priority": "critical",
|
"priority": "critical",
|
||||||
"sprint": null,
|
"sprint": null,
|
||||||
"assignedAgentIds": [
|
"assignedAgentIds": [
|
||||||
"57695b92-24d0-4876-837c-76116e70a6ae"
|
"57695b92-24d0-4876-837c-76116e70a6ae"
|
||||||
],
|
],
|
||||||
"updatedAt": 1784490850142
|
"updatedAt": 1784526657344
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"issueRef": "#72",
|
"issueRef": "#72",
|
||||||
@ -835,13 +835,13 @@
|
|||||||
"issueRef": "#74",
|
"issueRef": "#74",
|
||||||
"path": "74",
|
"path": "74",
|
||||||
"title": "[Bug] Serveur non lancable",
|
"title": "[Bug] Serveur non lancable",
|
||||||
"status": "open",
|
"status": "qa",
|
||||||
"priority": "critical",
|
"priority": "critical",
|
||||||
"sprint": null,
|
"sprint": null,
|
||||||
"assignedAgentIds": [
|
"assignedAgentIds": [
|
||||||
"57695b92-24d0-4876-837c-76116e70a6ae"
|
"57695b92-24d0-4876-837c-76116e70a6ae"
|
||||||
],
|
],
|
||||||
"updatedAt": 1784493327494
|
"updatedAt": 1784526657602
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -915,6 +915,7 @@ final class _ProgramExerciseDraft {
|
|||||||
this.exerciseImageMediaIdSnapshot,
|
this.exerciseImageMediaIdSnapshot,
|
||||||
this.exerciseImageMediaIdsSnapshot = const [],
|
this.exerciseImageMediaIdsSnapshot = const [],
|
||||||
this.exerciseVideoMediaIdSnapshot,
|
this.exerciseVideoMediaIdSnapshot,
|
||||||
|
this.exerciseStepsSnapshot = const [],
|
||||||
this.exerciseArchivedSnapshot = false,
|
this.exerciseArchivedSnapshot = false,
|
||||||
required this.availableTimeSnapshot,
|
required this.availableTimeSnapshot,
|
||||||
required this.availableRepsSnapshot,
|
required this.availableRepsSnapshot,
|
||||||
@ -944,6 +945,7 @@ final class _ProgramExerciseDraft {
|
|||||||
exercise.iconMediaId ?? exercise.imageMediaId,
|
exercise.iconMediaId ?? exercise.imageMediaId,
|
||||||
exerciseImageMediaIdsSnapshot: exercise.imageMediaIds,
|
exerciseImageMediaIdsSnapshot: exercise.imageMediaIds,
|
||||||
exerciseVideoMediaIdSnapshot: exercise.videoMediaId,
|
exerciseVideoMediaIdSnapshot: exercise.videoMediaId,
|
||||||
|
exerciseStepsSnapshot: exercise.steps,
|
||||||
exerciseArchivedSnapshot: exercise.archivedAt != null,
|
exerciseArchivedSnapshot: exercise.archivedAt != null,
|
||||||
availableTimeSnapshot: exercise.hasTimeMeasure,
|
availableTimeSnapshot: exercise.hasTimeMeasure,
|
||||||
availableRepsSnapshot: exercise.hasRepsMeasure,
|
availableRepsSnapshot: exercise.hasRepsMeasure,
|
||||||
@ -975,6 +977,7 @@ final class _ProgramExerciseDraft {
|
|||||||
exerciseImageMediaIdSnapshot: exercise.exerciseImageMediaIdSnapshot,
|
exerciseImageMediaIdSnapshot: exercise.exerciseImageMediaIdSnapshot,
|
||||||
exerciseImageMediaIdsSnapshot: exercise.exerciseImageMediaIdsSnapshot,
|
exerciseImageMediaIdsSnapshot: exercise.exerciseImageMediaIdsSnapshot,
|
||||||
exerciseVideoMediaIdSnapshot: exercise.exerciseVideoMediaIdSnapshot,
|
exerciseVideoMediaIdSnapshot: exercise.exerciseVideoMediaIdSnapshot,
|
||||||
|
exerciseStepsSnapshot: exercise.exerciseStepsSnapshot,
|
||||||
exerciseArchivedSnapshot: exercise.exerciseArchivedSnapshot,
|
exerciseArchivedSnapshot: exercise.exerciseArchivedSnapshot,
|
||||||
availableTimeSnapshot: exercise.availableTimeSnapshot,
|
availableTimeSnapshot: exercise.availableTimeSnapshot,
|
||||||
availableRepsSnapshot: exercise.availableRepsSnapshot,
|
availableRepsSnapshot: exercise.availableRepsSnapshot,
|
||||||
@ -1004,6 +1007,7 @@ final class _ProgramExerciseDraft {
|
|||||||
final String? exerciseImageMediaIdSnapshot;
|
final String? exerciseImageMediaIdSnapshot;
|
||||||
final List<String> exerciseImageMediaIdsSnapshot;
|
final List<String> exerciseImageMediaIdsSnapshot;
|
||||||
final String? exerciseVideoMediaIdSnapshot;
|
final String? exerciseVideoMediaIdSnapshot;
|
||||||
|
final List<ExerciseStep> exerciseStepsSnapshot;
|
||||||
final bool exerciseArchivedSnapshot;
|
final bool exerciseArchivedSnapshot;
|
||||||
final bool availableTimeSnapshot;
|
final bool availableTimeSnapshot;
|
||||||
final bool availableRepsSnapshot;
|
final bool availableRepsSnapshot;
|
||||||
@ -1029,6 +1033,7 @@ final class _ProgramExerciseDraft {
|
|||||||
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
||||||
exerciseImageMediaIdsSnapshot: exerciseImageMediaIdsSnapshot,
|
exerciseImageMediaIdsSnapshot: exerciseImageMediaIdsSnapshot,
|
||||||
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
||||||
|
exerciseStepsSnapshot: exerciseStepsSnapshot,
|
||||||
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
||||||
availableTimeSnapshot: availableTimeSnapshot,
|
availableTimeSnapshot: availableTimeSnapshot,
|
||||||
availableRepsSnapshot: availableRepsSnapshot,
|
availableRepsSnapshot: availableRepsSnapshot,
|
||||||
@ -1057,6 +1062,7 @@ final class _ProgramExerciseDraft {
|
|||||||
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
||||||
exerciseImageMediaIdsSnapshot: exerciseImageMediaIdsSnapshot,
|
exerciseImageMediaIdsSnapshot: exerciseImageMediaIdsSnapshot,
|
||||||
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
||||||
|
exerciseStepsSnapshot: exerciseStepsSnapshot,
|
||||||
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
||||||
availableTimeSnapshot: availableTimeSnapshot,
|
availableTimeSnapshot: availableTimeSnapshot,
|
||||||
availableRepsSnapshot: availableRepsSnapshot,
|
availableRepsSnapshot: availableRepsSnapshot,
|
||||||
|
|||||||
@ -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 l’objectif de chrono pour un score en mode chrono', (
|
testWidgets('affiche l’objectif de chrono pour un score en mode chrono', (
|
||||||
tester,
|
tester,
|
||||||
) async {
|
) 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 {
|
final class _FakeClock implements Clock {
|
||||||
const _FakeClock(this.value);
|
const _FakeClock(this.value);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user