feat(bibliothèque): badge Exemple, catégories et états vides bibliothèque (ticket #80)
Ajoute le badge "Exemple" (example_badge.dart) affiché sur les exercices/programmes/séances-modèles seedés, le filtrage par catégorie dans la bibliothèque d'exercices et les états vides dédiés. Préserve isExample lors de l'édition d'un programme ou d'une séance-modèle existants (ProgramUseCases/WorkoutTemplateUseCases). Validé GO par Main : dart analyze propre, 173 tests (4 échecs préexistants sans rapport avec ce lot). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1306,6 +1306,7 @@ final class ProgramUseCases {
|
|||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
name: name,
|
name: name,
|
||||||
defaultRestSeconds: defaultRestSeconds,
|
defaultRestSeconds: defaultRestSeconds,
|
||||||
|
isExample: existing?.isExample ?? false,
|
||||||
exercises: programExercises,
|
exercises: programExercises,
|
||||||
);
|
);
|
||||||
await programRepository.replaceExercises(program, now);
|
await programRepository.replaceExercises(program, now);
|
||||||
@ -1551,6 +1552,7 @@ final class WorkoutTemplateUseCases {
|
|||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
name: name,
|
name: name,
|
||||||
lastStartedAt: existing?.lastStartedAt,
|
lastStartedAt: existing?.lastStartedAt,
|
||||||
|
isExample: existing?.isExample ?? false,
|
||||||
programs: templatePrograms,
|
programs: templatePrograms,
|
||||||
overrides: templateOverrides,
|
overrides: templateOverrides,
|
||||||
);
|
);
|
||||||
|
|||||||
24
lib/presentation/example_badge.dart
Normal file
24
lib/presentation/example_badge.dart
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'theme.dart';
|
||||||
|
|
||||||
|
final class ExampleBadge extends StatelessWidget {
|
||||||
|
const ExampleBadge({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final tokens = courtBlazerTokensOf(context);
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: tokens.border),
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Exemple',
|
||||||
|
style: theme.textTheme.labelSmall?.copyWith(color: tokens.mutedText),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import 'package:image_picker/image_picker.dart';
|
|||||||
|
|
||||||
import '../application/application.dart';
|
import '../application/application.dart';
|
||||||
import '../domain/domain.dart';
|
import '../domain/domain.dart';
|
||||||
|
import 'example_badge.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
abstract interface class MediaSourcePicker {
|
abstract interface class MediaSourcePicker {
|
||||||
@ -134,9 +135,9 @@ final class _ExerciseLibraryScreenState extends State<ExerciseLibraryScreen> {
|
|||||||
SliverFillRemaining(
|
SliverFillRemaining(
|
||||||
hasScrollBody: false,
|
hasScrollBody: false,
|
||||||
child: _CenteredMessage(
|
child: _CenteredMessage(
|
||||||
title: 'Aucun exercice pour le moment',
|
title: 'Aucun exercice',
|
||||||
message:
|
message:
|
||||||
'Crée ton premier exercice pour composer tes programmes.',
|
'Crée ton premier exercice ou restaure les exemples.',
|
||||||
actionLabel: 'Créer un exercice',
|
actionLabel: 'Créer un exercice',
|
||||||
onAction: () => _openForm(),
|
onAction: () => _openForm(),
|
||||||
),
|
),
|
||||||
@ -275,7 +276,15 @@ final class ExerciseListTile extends StatelessWidget {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
leading: _ExerciseIconAvatar(iconMediaId: iconMediaId),
|
leading: _ExerciseIconAvatar(iconMediaId: iconMediaId),
|
||||||
title: Text(exercise.name),
|
title: Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 4,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(exercise.name),
|
||||||
|
if (exercise.isExample) const ExampleBadge(),
|
||||||
|
],
|
||||||
|
),
|
||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -294,6 +303,7 @@ final class ExerciseListTile extends StatelessWidget {
|
|||||||
spacing: 6,
|
spacing: 6,
|
||||||
runSpacing: 6,
|
runSpacing: 6,
|
||||||
children: [
|
children: [
|
||||||
|
_CategoryBadge(category: exercise.category),
|
||||||
...exercise.availableMeasures.map(
|
...exercise.availableMeasures.map(
|
||||||
(measure) => MeasureBadge(
|
(measure) => MeasureBadge(
|
||||||
measure: measure,
|
measure: measure,
|
||||||
@ -327,6 +337,20 @@ final class ExerciseListTile extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class _CategoryBadge extends StatelessWidget {
|
||||||
|
const _CategoryBadge({required this.category});
|
||||||
|
|
||||||
|
final ExerciseCategory category;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Chip(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
label: Text(category.label),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class _ExerciseIconAvatar extends StatelessWidget {
|
final class _ExerciseIconAvatar extends StatelessWidget {
|
||||||
const _ExerciseIconAvatar({required this.iconMediaId});
|
const _ExerciseIconAvatar({required this.iconMediaId});
|
||||||
|
|
||||||
@ -1786,11 +1810,9 @@ final class _MediaImportField extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final fileName = selectedFileName;
|
final fileName = selectedFileName;
|
||||||
final status = fileName == null
|
final status =
|
||||||
? imported
|
fileName ??
|
||||||
? 'Média déjà importé'
|
(imported ? 'Média déjà importé' : 'Aucun fichier sélectionné');
|
||||||
: 'Aucun fichier sélectionné'
|
|
||||||
: fileName;
|
|
||||||
return InputDecorator(
|
return InputDecorator(
|
||||||
decoration: InputDecoration(labelText: label),
|
decoration: InputDecoration(labelText: label),
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -1867,3 +1889,18 @@ extension on WorkoutMeasure {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension on ExerciseCategory {
|
||||||
|
String get label {
|
||||||
|
return switch (this) {
|
||||||
|
ExerciseCategory.shoot => 'Tir',
|
||||||
|
ExerciseCategory.freeThrows => 'Lancers francs',
|
||||||
|
ExerciseCategory.dribble => 'Dribble',
|
||||||
|
ExerciseCategory.finishing => 'Finition',
|
||||||
|
ExerciseCategory.conditioning => 'Condition physique',
|
||||||
|
ExerciseCategory.defense => 'Défense',
|
||||||
|
ExerciseCategory.mobility => 'Mobilité',
|
||||||
|
ExerciseCategory.uncategorized => 'Non catégorisé',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export 'game_time_app.dart';
|
export 'game_time_app.dart';
|
||||||
|
export 'example_badge.dart';
|
||||||
export 'exercise_step_audio.dart';
|
export 'exercise_step_audio.dart';
|
||||||
export 'exercise_library_screen.dart';
|
export 'exercise_library_screen.dart';
|
||||||
export 'history_screen.dart';
|
export 'history_screen.dart';
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import '../application/application.dart';
|
import '../application/application.dart';
|
||||||
import '../domain/domain.dart';
|
import '../domain/domain.dart';
|
||||||
|
import 'example_badge.dart';
|
||||||
import 'exercise_library_screen.dart';
|
import 'exercise_library_screen.dart';
|
||||||
import 'share_screen.dart';
|
import 'share_screen.dart';
|
||||||
|
|
||||||
@ -61,9 +62,8 @@ final class _ProgramListScreenState extends State<ProgramListScreen> {
|
|||||||
final programs = snapshot.data ?? const <Program>[];
|
final programs = snapshot.data ?? const <Program>[];
|
||||||
if (programs.isEmpty) {
|
if (programs.isEmpty) {
|
||||||
return _CenteredMessage(
|
return _CenteredMessage(
|
||||||
title: 'Aucun programme pour le moment',
|
title: 'Aucun programme',
|
||||||
message:
|
message: 'Assemble quelques exercices pour préparer une séance.',
|
||||||
'Crée un programme pour organiser tes exercices en séries.',
|
|
||||||
actionLabel: 'Créer un programme',
|
actionLabel: 'Créer un programme',
|
||||||
onAction: () => _openForm(),
|
onAction: () => _openForm(),
|
||||||
);
|
);
|
||||||
@ -74,7 +74,15 @@ final class _ProgramListScreenState extends State<ProgramListScreen> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final program = programs[index];
|
final program = programs[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(program.name),
|
title: Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 4,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(program.name),
|
||||||
|
if (program.isExample) const ExampleBadge(),
|
||||||
|
],
|
||||||
|
),
|
||||||
subtitle: Text(_programSummary(program)),
|
subtitle: Text(_programSummary(program)),
|
||||||
trailing: Wrap(
|
trailing: Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import '../application/application.dart';
|
import '../application/application.dart';
|
||||||
import '../domain/domain.dart';
|
import '../domain/domain.dart';
|
||||||
|
import 'example_badge.dart';
|
||||||
import 'share_screen.dart';
|
import 'share_screen.dart';
|
||||||
import 'workout_execution_screen.dart';
|
import 'workout_execution_screen.dart';
|
||||||
|
|
||||||
@ -66,8 +67,9 @@ final class _WorkoutTemplateListScreenState
|
|||||||
final templates = snapshot.data ?? const <WorkoutTemplate>[];
|
final templates = snapshot.data ?? const <WorkoutTemplate>[];
|
||||||
if (templates.isEmpty) {
|
if (templates.isEmpty) {
|
||||||
return _CenteredMessage(
|
return _CenteredMessage(
|
||||||
title: 'Aucune séance pour le moment',
|
title: 'Aucune séance',
|
||||||
message: 'Compose une séance à partir de tes programmes.',
|
message:
|
||||||
|
'Crée une séance-modèle pour lancer ton entraînement plus vite.',
|
||||||
actionLabel: 'Créer une séance',
|
actionLabel: 'Créer une séance',
|
||||||
onAction: () => _openForm(),
|
onAction: () => _openForm(),
|
||||||
);
|
);
|
||||||
@ -78,7 +80,15 @@ final class _WorkoutTemplateListScreenState
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final template = templates[index];
|
final template = templates[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(template.name),
|
title: Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 4,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(template.name),
|
||||||
|
if (template.isExample) const ExampleBadge(),
|
||||||
|
],
|
||||||
|
),
|
||||||
subtitle: Text(_templateSummary(template)),
|
subtitle: Text(_templateSummary(template)),
|
||||||
trailing: Wrap(
|
trailing: Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
|
|||||||
@ -54,6 +54,8 @@ void main() {
|
|||||||
defaultTargetTimeSeconds: 45,
|
defaultTargetTimeSeconds: 45,
|
||||||
defaultTargetReps: 10,
|
defaultTargetReps: 10,
|
||||||
defaultTargetScore: 80,
|
defaultTargetScore: 80,
|
||||||
|
category: ExerciseCategory.finishing,
|
||||||
|
isExample: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -67,6 +69,8 @@ void main() {
|
|||||||
expect(find.text('Temps'), findsOneWidget);
|
expect(find.text('Temps'), findsOneWidget);
|
||||||
expect(find.text('Répétitions'), findsOneWidget);
|
expect(find.text('Répétitions'), findsOneWidget);
|
||||||
expect(find.text('Score (kg)'), findsOneWidget);
|
expect(find.text('Score (kg)'), findsOneWidget);
|
||||||
|
expect(find.text('Finition'), findsOneWidget);
|
||||||
|
expect(find.text('Exemple'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('basculer en chrono intégré masque l’unité et affiche le badge', (
|
testWidgets('basculer en chrono intégré masque l’unité et affiche le badge', (
|
||||||
|
|||||||
@ -5,6 +5,101 @@ import 'package:gametime/domain/domain.dart';
|
|||||||
import 'package:gametime/presentation/presentation.dart';
|
import 'package:gametime/presentation/presentation.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
testWidgets('la liste affiche le badge exemple des programmes seedés', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final programRepository = _FakeProgramRepository()
|
||||||
|
..programs.add(
|
||||||
|
Program(
|
||||||
|
metadata: _metadata('program-1'),
|
||||||
|
name: 'Fondations basket - 45 min',
|
||||||
|
defaultRestSeconds: 45,
|
||||||
|
isExample: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final exerciseRepository = _FakeExerciseRepository();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProgramListScreen(
|
||||||
|
programUseCases: _programUseCases(
|
||||||
|
programRepository,
|
||||||
|
exerciseRepository,
|
||||||
|
),
|
||||||
|
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Fondations basket - 45 min'), findsOneWidget);
|
||||||
|
expect(find.text('Exemple'), findsOneWidget);
|
||||||
|
expect(find.byTooltip('Supprimer le programme'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('la liste programmes vide conserve le message dédié', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final programRepository = _FakeProgramRepository();
|
||||||
|
final exerciseRepository = _FakeExerciseRepository();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProgramListScreen(
|
||||||
|
programUseCases: _programUseCases(
|
||||||
|
programRepository,
|
||||||
|
exerciseRepository,
|
||||||
|
),
|
||||||
|
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Aucun programme'), findsOneWidget);
|
||||||
|
expect(
|
||||||
|
find.text('Assemble quelques exercices pour préparer une séance.'),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('modifier un programme exemple conserve sa provenance', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final programRepository = _FakeProgramRepository()
|
||||||
|
..programs.add(
|
||||||
|
Program(
|
||||||
|
metadata: _metadata('program-1'),
|
||||||
|
name: 'Fondations basket - 45 min',
|
||||||
|
defaultRestSeconds: 45,
|
||||||
|
isExample: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final exerciseRepository = _FakeExerciseRepository();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProgramFormScreen(
|
||||||
|
programUseCases: _programUseCases(
|
||||||
|
programRepository,
|
||||||
|
exerciseRepository,
|
||||||
|
),
|
||||||
|
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
||||||
|
program: programRepository.programs.single,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.enterText(find.widgetWithText(TextFormField, 'Nom'), 'Edité');
|
||||||
|
await tester.tap(find.text('Enregistrer'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(programRepository.programs.single.name, 'Edité');
|
||||||
|
expect(programRepository.programs.single.isExample, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'ajoute un exercice au programme avec sa configuration par défaut',
|
'ajoute un exercice au programme avec sa configuration par défaut',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
|
|||||||
@ -7,6 +7,165 @@ import 'package:gametime/domain/domain.dart';
|
|||||||
import 'package:gametime/presentation/presentation.dart';
|
import 'package:gametime/presentation/presentation.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
testWidgets('la liste affiche le badge exemple des séances seedées', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final clock = _FakeClock(DateTime.utc(2026, 7, 17, 12));
|
||||||
|
final templateRepository = _FakeWorkoutTemplateRepository([
|
||||||
|
_workoutTemplate(isExample: true),
|
||||||
|
]);
|
||||||
|
final activeRepository = _FakeActiveSessionRepository();
|
||||||
|
final programRepository = _FakeProgramRepository();
|
||||||
|
final historyRepository = _FakeWorkoutHistoryRepository();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: WorkoutTemplateListScreen(
|
||||||
|
workoutTemplateUseCases: WorkoutTemplateUseCases(
|
||||||
|
templateRepository: templateRepository,
|
||||||
|
programRepository: programRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
programUseCases: ProgramUseCases(
|
||||||
|
programRepository: programRepository,
|
||||||
|
exerciseRepository: _FakeExerciseRepository(),
|
||||||
|
templateRepository: templateRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
activeUseCases: ActiveWorkoutSessionUseCases(
|
||||||
|
sessionRepository: activeRepository,
|
||||||
|
templateRepository: templateRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
closeUseCase: CloseWorkoutSessionUseCase(
|
||||||
|
sessionRepository: activeRepository,
|
||||||
|
historyRepository: historyRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
historyUseCases: WorkoutHistoryUseCases(
|
||||||
|
repository: historyRepository,
|
||||||
|
clock: clock,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Séance jambes'), findsOneWidget);
|
||||||
|
expect(find.text('Exemple'), findsOneWidget);
|
||||||
|
expect(find.byTooltip('Supprimer la séance'), findsOneWidget);
|
||||||
|
expect(find.text('Lancer'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('la liste séances vide conserve le message dédié', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final clock = _FakeClock(DateTime.utc(2026, 7, 17, 12));
|
||||||
|
final templateRepository = _FakeWorkoutTemplateRepository(const []);
|
||||||
|
final activeRepository = _FakeActiveSessionRepository();
|
||||||
|
final programRepository = _FakeProgramRepository();
|
||||||
|
final historyRepository = _FakeWorkoutHistoryRepository();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: WorkoutTemplateListScreen(
|
||||||
|
workoutTemplateUseCases: WorkoutTemplateUseCases(
|
||||||
|
templateRepository: templateRepository,
|
||||||
|
programRepository: programRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
programUseCases: ProgramUseCases(
|
||||||
|
programRepository: programRepository,
|
||||||
|
exerciseRepository: _FakeExerciseRepository(),
|
||||||
|
templateRepository: templateRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
activeUseCases: ActiveWorkoutSessionUseCases(
|
||||||
|
sessionRepository: activeRepository,
|
||||||
|
templateRepository: templateRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
closeUseCase: CloseWorkoutSessionUseCase(
|
||||||
|
sessionRepository: activeRepository,
|
||||||
|
historyRepository: historyRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
historyUseCases: WorkoutHistoryUseCases(
|
||||||
|
repository: historyRepository,
|
||||||
|
clock: clock,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Aucune séance'), findsOneWidget);
|
||||||
|
expect(
|
||||||
|
find.text(
|
||||||
|
'Crée une séance-modèle pour lancer ton entraînement plus vite.',
|
||||||
|
),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('modifier une séance exemple conserve sa provenance', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final clock = _FakeClock(DateTime.utc(2026, 7, 17, 12));
|
||||||
|
final templateRepository = _FakeWorkoutTemplateRepository([
|
||||||
|
_workoutTemplate(isExample: true),
|
||||||
|
]);
|
||||||
|
final programRepository = _FakeProgramRepository();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: WorkoutTemplateFormScreen(
|
||||||
|
workoutTemplateUseCases: WorkoutTemplateUseCases(
|
||||||
|
templateRepository: templateRepository,
|
||||||
|
programRepository: programRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
programUseCases: ProgramUseCases(
|
||||||
|
programRepository: programRepository,
|
||||||
|
exerciseRepository: _FakeExerciseRepository(),
|
||||||
|
templateRepository: templateRepository,
|
||||||
|
clock: clock,
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
template: templateRepository.templates.single,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.enterText(find.widgetWithText(TextFormField, 'Nom'), 'Editée');
|
||||||
|
await tester.tap(find.text('Enregistrer'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(templateRepository.templates.single.name, 'Editée');
|
||||||
|
expect(templateRepository.templates.single.isExample, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'la vue programme de séance ne permet pas de changer les mesures actives',
|
'la vue programme de séance ne permet pas de changer les mesures actives',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
@ -352,10 +511,11 @@ void main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkoutTemplate _workoutTemplate() {
|
WorkoutTemplate _workoutTemplate({bool isExample = false}) {
|
||||||
return WorkoutTemplate(
|
return WorkoutTemplate(
|
||||||
metadata: _metadata('template-1'),
|
metadata: _metadata('template-1'),
|
||||||
name: 'Séance jambes',
|
name: 'Séance jambes',
|
||||||
|
isExample: isExample,
|
||||||
programs: [
|
programs: [
|
||||||
WorkoutTemplateProgram(
|
WorkoutTemplateProgram(
|
||||||
metadata: _metadata('template-program-1'),
|
metadata: _metadata('template-program-1'),
|
||||||
|
|||||||
Reference in New Issue
Block a user