fix(media): sélecteur de média pour les exercices (ticket #15)

Corrige/ajoute le sélecteur de média sur exercise_library_screen.dart
suite aux retours de QA. flutter analyze propre (1 warning mineur
"paramètre inutilisé" dans un test, sans impact), 27/27 tests verts,
build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 22:22:59 +02:00
parent ee51b3941f
commit cb5cfc48ff
4 changed files with 271 additions and 46 deletions

View File

@ -65,6 +65,34 @@ void main() {
expect(find.text('Répétitions'), findsOneWidget);
expect(find.text('Score (kg)'), findsOneWidget);
});
testWidgets(
'sélectionner une image déclenche limport et confirme le fichier',
(tester) async {
final exerciseRepository = _FakeExerciseRepository();
final storage = _FakeMediaStorage();
await tester.pumpWidget(
MaterialApp(
home: ExerciseFormScreen(
exerciseUseCases: _exerciseUseCases(exerciseRepository),
mediaUseCases: _mediaUseCases(exerciseRepository, storage: storage),
mediaPicker: const _FakeMediaPicker(
imagePath: '/tmp/imports/photo.png',
),
),
),
);
await tester.tap(find.text('Choisir une image'));
await tester.pump();
expect(storage.imports.single.sourcePath, '/tmp/imports/photo.png');
expect(storage.imports.single.kind, MediaKind.image);
expect(find.text('photo.png'), findsOneWidget);
expect(find.text('Image importée.'), findsOneWidget);
},
);
}
ExerciseUseCases _exerciseUseCases(_FakeExerciseRepository repository) {
@ -76,17 +104,35 @@ ExerciseUseCases _exerciseUseCases(_FakeExerciseRepository repository) {
);
}
MediaUseCases _mediaUseCases(_FakeExerciseRepository exerciseRepository) {
MediaUseCases _mediaUseCases(
_FakeExerciseRepository exerciseRepository, {
_FakeMediaStorage? storage,
}) {
return MediaUseCases(
mediaRepository: _FakeMediaAssetRepository(),
exerciseRepository: exerciseRepository,
storage: _FakeMediaStorage(),
storage: storage ?? _FakeMediaStorage(),
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
ids: _FakeIds(),
originDeviceId: 'device-1',
);
}
final class _FakeMediaPicker implements MediaSourcePicker {
const _FakeMediaPicker({this.imagePath, this.videoPath});
final String? imagePath;
final String? videoPath;
@override
Future<String?> pickPath(MediaKind kind) async {
return switch (kind) {
MediaKind.image => imagePath,
MediaKind.video => videoPath,
};
}
}
EntityMetadata _metadata(String id) {
return EntityMetadata(
id: id,
@ -167,6 +213,8 @@ final class _FakeMediaAssetRepository implements MediaAssetRepository {
}
final class _FakeMediaStorage implements LocalMediaStorage {
final imports = <_MediaImportCall>[];
@override
Future<void> deleteByLocalUri(String localUri) async {}
@ -176,6 +224,13 @@ final class _FakeMediaStorage implements LocalMediaStorage {
required MediaKind kind,
required String stableFileName,
}) async {
imports.add(
_MediaImportCall(
sourcePath: sourcePath,
kind: kind,
stableFileName: stableFileName,
),
);
return StoredMediaFile(
localUri: 'file:///tmp/$stableFileName',
mimeType: kind == MediaKind.image ? 'image/png' : 'video/mp4',
@ -186,3 +241,15 @@ final class _FakeMediaStorage implements LocalMediaStorage {
@override
Future<Set<String>> listManagedLocalUris() async => const {};
}
final class _MediaImportCall {
const _MediaImportCall({
required this.sourcePath,
required this.kind,
required this.stableFileName,
});
final String sourcePath;
final MediaKind kind;
final String stableFileName;
}