feat(exercice): galerie de 5 images par exercice (ticket #35)
Étend le modèle Drift (tables.dart, app_database.dart/.g.dart), les entités/use cases/repositories et exercise_library_screen.dart pour supporter jusqu'à 5 images par exercice au lieu d'une seule. flutter analyze propre, 63/63 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -132,23 +132,90 @@ void main() {
|
||||
home: ExerciseFormScreen(
|
||||
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
||||
mediaUseCases: _mediaUseCases(exerciseRepository, storage: storage),
|
||||
mediaPicker: const _FakeMediaPicker(
|
||||
imagePath: '/tmp/imports/photo.png',
|
||||
),
|
||||
mediaPicker: _FakeMediaPicker(imagePath: '/tmp/imports/photo.png'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Choisir une image'));
|
||||
await tester.tap(find.text('Ajouter 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);
|
||||
expect(find.text('Image ajoutée.'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('ajouter des images bloque la sixième image', (tester) async {
|
||||
final exerciseRepository = _FakeExerciseRepository();
|
||||
final storage = _FakeMediaStorage();
|
||||
|
||||
await tester.binding.setSurfaceSize(const Size(400, 1200));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: ExerciseFormScreen(
|
||||
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
||||
mediaUseCases: _mediaUseCases(exerciseRepository, storage: storage),
|
||||
mediaPicker: _FakeMediaPicker(
|
||||
imagePaths: const [
|
||||
'/tmp/imports/photo-1.png',
|
||||
'/tmp/imports/photo-2.png',
|
||||
'/tmp/imports/photo-3.png',
|
||||
'/tmp/imports/photo-4.png',
|
||||
'/tmp/imports/photo-5.png',
|
||||
'/tmp/imports/photo-6.png',
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
for (var i = 0; i < 5; i++) {
|
||||
await tester.tap(find.text('Ajouter une image'));
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
expect(storage.imports, hasLength(5));
|
||||
expect(find.text('photo-5.png'), findsOneWidget);
|
||||
expect(find.text('Maximum 5 images par exercice.'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Ajouter une image'));
|
||||
await tester.pump();
|
||||
|
||||
expect(storage.imports, hasLength(5));
|
||||
expect(find.text('photo-6.png'), findsNothing);
|
||||
expect(find.text('Maximum 5 images par exercice.'), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('supprimer une image la retire de la galerie', (tester) async {
|
||||
final exerciseRepository = _FakeExerciseRepository();
|
||||
final storage = _FakeMediaStorage();
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: ExerciseFormScreen(
|
||||
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
||||
mediaUseCases: _mediaUseCases(exerciseRepository, storage: storage),
|
||||
mediaPicker: _FakeMediaPicker(imagePath: '/tmp/imports/photo.png'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Ajouter une image'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('photo.png'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byTooltip('Supprimer l’image'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('photo.png'), findsNothing);
|
||||
expect(find.text('Aucune image sélectionnée'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'supprimer un exercice demande confirmation puis le retire de la liste',
|
||||
(tester) async {
|
||||
@ -244,17 +311,31 @@ MediaUseCases _mediaUseCases(
|
||||
}
|
||||
|
||||
final class _FakeMediaPicker implements MediaSourcePicker {
|
||||
const _FakeMediaPicker({this.imagePath});
|
||||
_FakeMediaPicker({this.imagePath, this.imagePaths = const []});
|
||||
|
||||
final String? imagePath;
|
||||
final List<String> imagePaths;
|
||||
var _nextImagePath = 0;
|
||||
|
||||
@override
|
||||
Future<String?> pickPath(MediaKind kind) async {
|
||||
return switch (kind) {
|
||||
MediaKind.image => imagePath,
|
||||
MediaKind.image => _pickImagePath(),
|
||||
MediaKind.video => null,
|
||||
};
|
||||
}
|
||||
|
||||
String? _pickImagePath() {
|
||||
if (imagePaths.isEmpty) {
|
||||
return imagePath;
|
||||
}
|
||||
if (_nextImagePath >= imagePaths.length) {
|
||||
return null;
|
||||
}
|
||||
final path = imagePaths[_nextImagePath];
|
||||
_nextImagePath += 1;
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
EntityMetadata _metadata(String id) {
|
||||
|
||||
Reference in New Issue
Block a user