feat(exercice): affichage des médias pendant l'exécution (ticket #36)

Expose la galerie d'images d'exercice (backend du ticket #35) pendant
l'exécution de séance sur workout_execution_screen.dart et
program_screen.dart, avec les ajustements domain/use cases/repositories
nécessaires. flutter analyze propre, 67/67 tests verts, build APK
debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 14:16:31 +02:00
parent 7385c96517
commit ff6b79b66e
10 changed files with 654 additions and 1 deletions

View File

@ -116,6 +116,131 @@ void main() {
expect(find.text('Pas de repos'), findsOneWidget);
});
testWidgets('le bouton médias est absent sans média', (tester) async {
final clock = _FakeClock(DateTime.utc(2026, 7, 17, 12));
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(),
);
await tester.pumpWidget(
MaterialApp(
home: WorkoutExecutionScreen(
initialSession: session,
activeUseCases: _activeUseCases(
_FakeActiveSessionRepository(),
clock,
),
closeUseCase: _closeUseCase(_FakeActiveSessionRepository(), clock),
historyUseCases: _historyUseCases(clock),
workoutTemplateUseCases: _workoutTemplateUseCases(),
),
),
);
expect(find.text('Voir médias'), findsNothing);
expect(find.byTooltip('Voir les médias de lexercice'), findsNothing);
});
testWidgets('le bouton médias ouvre la sheet si médias présents', (
tester,
) async {
final clock = _FakeClock(DateTime.utc(2026, 7, 17, 12));
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(
imageMediaIds: const ['image-1'],
videoMediaId: 'video-1',
),
);
await tester.pumpWidget(
MaterialApp(
home: WorkoutExecutionScreen(
initialSession: session,
activeUseCases: _activeUseCases(
_FakeActiveSessionRepository(),
clock,
),
closeUseCase: _closeUseCase(_FakeActiveSessionRepository(), clock),
historyUseCases: _historyUseCases(clock),
workoutTemplateUseCases: _workoutTemplateUseCases(),
),
),
);
expect(find.byTooltip('Voir les médias de lexercice'), findsOneWidget);
await tester.tap(find.text('Voir médias'));
await tester.pumpAndSettle();
expect(find.text('Médias de lexercice'), findsOneWidget);
expect(find.text('Images'), findsOneWidget);
expect(find.text('Vidéo'), findsOneWidget);
expect(find.text('1/1'), findsOneWidget);
expect(find.text('Image 1'), findsOneWidget);
});
testWidgets('la galerie médias navigue entre les images', (tester) async {
final clock = _FakeClock(DateTime.utc(2026, 7, 17, 12));
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(
imageMediaIds: const ['image-1', 'image-2', 'image-3'],
),
);
await tester.pumpWidget(
MaterialApp(
home: WorkoutExecutionScreen(
initialSession: session,
activeUseCases: _activeUseCases(
_FakeActiveSessionRepository(),
clock,
),
closeUseCase: _closeUseCase(_FakeActiveSessionRepository(), clock),
historyUseCases: _historyUseCases(clock),
workoutTemplateUseCases: _workoutTemplateUseCases(),
),
),
);
await tester.tap(find.text('Voir médias'));
await tester.pumpAndSettle();
expect(find.text('1/3'), findsOneWidget);
await tester.drag(find.byType(PageView), const Offset(-400, 0));
await tester.pumpAndSettle();
expect(find.text('2/3'), findsOneWidget);
expect(find.text('Image 2'), findsOneWidget);
});
testWidgets('un repos actif est repris avec le temps restant recalculé', (
tester,
) async {
@ -559,6 +684,8 @@ String _sessionSnapshot({
double targetScore = 80,
String scoreUnit = 'kg',
ScoreInputMode scoreInputMode = ScoreInputMode.manual,
List<String> imageMediaIds = const [],
String? videoMediaId,
}) {
return jsonEncode({
'name': 'Séance jambes',
@ -582,6 +709,10 @@ String _sessionSnapshot({
'scoreLabelSnapshot': 'Score',
'scoreUnitSnapshot': scoreUnit,
'restSecondsOverride': restSeconds,
'exerciseImageMediaIdsSnapshot': imageMediaIds,
'imageMediaIdsSnapshot': imageMediaIds,
'exerciseVideoMediaIdSnapshot': videoMediaId,
'videoMediaIdSnapshot': videoMediaId,
},
],
}),