merge(main): programme simplifié en cartes compactes (ticket #20)
Fusionne feature/#20-programme-cartes-compactes — analyze propre, 30/30 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#20"
|
||||
version: 2
|
||||
version: 3
|
||||
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||
updatedAt: 1784325859788
|
||||
updatedAt: 1784327055822
|
||||
---
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: "8355edb7-bdbd-4f93-820c-d7de0affebef"
|
||||
number: 20
|
||||
title: "[DevFrontend] Programme : cartes exercice compactes + écran de personnalisation séparé"
|
||||
status: "inProgress"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: [{"target":"#7","kind":"relatesTo"}]
|
||||
@ -10,7 +10,7 @@ agentRefs: [{"agentId":"9933c93a-b8a1-4164-a3bb-7063fdad747d","role":"assigned"}
|
||||
createdBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||
createdAt: 1784325837553
|
||||
updatedAt: 1784325859788
|
||||
version: 2
|
||||
updatedAt: 1784327055822
|
||||
version: 3
|
||||
---
|
||||
Simplifier la liste d'exercices d'un programme (lib/presentation/program_screen.dart) : chaque ligne n'affiche par défaut que la poignée de déplacement, le nom de l'exercice, le repos affiché, et une icône "personnaliser" (tooltip "Personnaliser l'exercice") ouvrant un écran séparé "Personnaliser l'exercice" avec la configuration complète (séries, mesures, cibles, repos). Ajout d'un exercice = valeurs par défaut immédiates (3 séries, toutes les mesures disponibles actives, repos = défaut du programme) + snackbar "Exercice ajouté" avec action rapide "Personnaliser". Cf. mémoire "gametime-ux-execution-nav-and-program-simplification" point 2 pour le détail complet.
|
||||
@ -219,13 +219,13 @@
|
||||
"issueRef": "#20",
|
||||
"path": "20",
|
||||
"title": "[DevFrontend] Programme : cartes exercice compactes + écran de personnalisation séparé",
|
||||
"status": "inProgress",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [
|
||||
"9933c93a-b8a1-4164-a3bb-7063fdad747d"
|
||||
],
|
||||
"updatedAt": 1784325859788
|
||||
"updatedAt": 1784327055822
|
||||
},
|
||||
{
|
||||
"issueRef": "#21",
|
||||
|
||||
@ -226,7 +226,7 @@ final class _ProgramFormScreenState extends State<ProgramFormScreen> {
|
||||
key: ValueKey(exercise.key),
|
||||
draft: exercise,
|
||||
defaultRestSeconds: _defaultRestSeconds,
|
||||
onChanged: () => setState(() {}),
|
||||
onCustomize: () => _customizeExercise(exercise.key),
|
||||
index: index,
|
||||
);
|
||||
},
|
||||
@ -246,8 +246,46 @@ final class _ProgramFormScreenState extends State<ProgramFormScreen> {
|
||||
);
|
||||
if (exercise == null) return;
|
||||
if (!mounted) return;
|
||||
final draft = _ProgramExerciseDraft.fromExercise(
|
||||
exercise,
|
||||
restSecondsOverride: _defaultRestSeconds,
|
||||
);
|
||||
setState(() {
|
||||
_exercises.add(_ProgramExerciseDraft.fromExercise(exercise));
|
||||
_exercises.add(draft);
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: const Text('Exercice ajouté'),
|
||||
action: SnackBarAction(
|
||||
label: 'Personnaliser',
|
||||
onPressed: () => _customizeExercise(draft.key),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _customizeExercise(String key) async {
|
||||
final index = _exercises.indexWhere((exercise) => exercise.key == key);
|
||||
if (index == -1) return;
|
||||
final result = await Navigator.of(context).push<_ProgramExerciseEditResult>(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _ProgramExerciseCustomizationScreen(
|
||||
draft: _exercises[index],
|
||||
defaultRestSeconds: _defaultRestSeconds,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (result == null || !mounted) return;
|
||||
setState(() {
|
||||
final currentIndex = _exercises.indexWhere(
|
||||
(exercise) => exercise.key == key,
|
||||
);
|
||||
if (currentIndex == -1) return;
|
||||
if (result.deleted) {
|
||||
_exercises.removeAt(currentIndex);
|
||||
} else if (result.draft != null) {
|
||||
_exercises[currentIndex] = result.draft!;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -418,116 +456,198 @@ final class _ProgramExerciseCard extends StatelessWidget {
|
||||
const _ProgramExerciseCard({
|
||||
required this.draft,
|
||||
required this.defaultRestSeconds,
|
||||
required this.onChanged,
|
||||
required this.onCustomize,
|
||||
required this.index,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final _ProgramExerciseDraft draft;
|
||||
final int defaultRestSeconds;
|
||||
final VoidCallback onChanged;
|
||||
final VoidCallback onCustomize;
|
||||
final int index;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scoreUnit = draft.scoreUnitSnapshot;
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
ReorderableDragStartListener(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
child: ListTile(
|
||||
leading: ReorderableDragStartListener(
|
||||
index: index,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(right: 8),
|
||||
child: Icon(Icons.drag_handle),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
draft.exerciseNameSnapshot,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
child: const Icon(Icons.drag_handle),
|
||||
),
|
||||
title: Text(draft.exerciseNameSnapshot),
|
||||
subtitle: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Text(_restLabel(draft, defaultRestSeconds)),
|
||||
if (draft.exerciseArchivedSnapshot)
|
||||
const Chip(label: Text('Exercice archivé')),
|
||||
],
|
||||
),
|
||||
trailing: IconButton(
|
||||
tooltip: "Personnaliser l'exercice",
|
||||
onPressed: onCustomize,
|
||||
icon: const Icon(Icons.tune),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class _ProgramExerciseCustomizationScreen extends StatefulWidget {
|
||||
const _ProgramExerciseCustomizationScreen({
|
||||
required _ProgramExerciseDraft draft,
|
||||
required this.defaultRestSeconds,
|
||||
super.key,
|
||||
}) : _initialDraft = draft;
|
||||
|
||||
final _ProgramExerciseDraft _initialDraft;
|
||||
final int defaultRestSeconds;
|
||||
|
||||
@override
|
||||
State<_ProgramExerciseCustomizationScreen> createState() =>
|
||||
_ProgramExerciseCustomizationScreenState();
|
||||
}
|
||||
|
||||
final class _ProgramExerciseCustomizationScreenState
|
||||
extends State<_ProgramExerciseCustomizationScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late final _ProgramExerciseDraft _draft;
|
||||
late final TextEditingController _setsController;
|
||||
late final TextEditingController _restController;
|
||||
String? _measureError;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_draft = widget._initialDraft.copy();
|
||||
_setsController = TextEditingController(text: '${_draft.setsCount}');
|
||||
_restController = TextEditingController(
|
||||
text: '${_draft.restSecondsOverride ?? widget.defaultRestSeconds}',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_setsController.dispose();
|
||||
_restController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scoreUnit = _draft.scoreUnitSnapshot;
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text("Personnaliser l'exercice")),
|
||||
bottomNavigationBar: SafeArea(
|
||||
minimum: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
FilledButton.icon(
|
||||
onPressed: _save,
|
||||
icon: const Icon(Icons.check),
|
||||
label: const Text('Enregistrer'),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextButton.icon(
|
||||
onPressed: _confirmDelete,
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
label: const Text('Supprimer du programme'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
Text(
|
||||
_draft.exerciseNameSnapshot,
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(_exerciseSummary(draft, defaultRestSeconds)),
|
||||
const SizedBox(height: 12),
|
||||
Text(_exerciseSummary(_draft, widget.defaultRestSeconds)),
|
||||
const SizedBox(height: 24),
|
||||
Text('Séries', style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
initialValue: '${draft.setsCount}',
|
||||
controller: _setsController,
|
||||
decoration: const InputDecoration(labelText: 'Nombre de séries'),
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
draft.setsCount = int.tryParse(value) ?? draft.setsCount;
|
||||
onChanged();
|
||||
},
|
||||
validator: _positiveIntValidator,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'Mesures à suivre',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
if (draft.availableTimeSnapshot)
|
||||
CheckboxListTile(
|
||||
if (_draft.availableTimeSnapshot)
|
||||
SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('Temps'),
|
||||
value: draft.enabledMeasures.contains(WorkoutMeasure.time),
|
||||
value: _draft.enabledMeasures.contains(WorkoutMeasure.time),
|
||||
onChanged: (value) => _toggle(WorkoutMeasure.time, value),
|
||||
),
|
||||
if (draft.availableRepsSnapshot)
|
||||
CheckboxListTile(
|
||||
if (_draft.availableRepsSnapshot)
|
||||
SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('Répétitions'),
|
||||
value: draft.enabledMeasures.contains(WorkoutMeasure.reps),
|
||||
value: _draft.enabledMeasures.contains(WorkoutMeasure.reps),
|
||||
onChanged: (value) => _toggle(WorkoutMeasure.reps, value),
|
||||
),
|
||||
if (draft.availableScoreSnapshot)
|
||||
CheckboxListTile(
|
||||
if (_draft.availableScoreSnapshot)
|
||||
SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(scoreUnit == null ? 'Score' : 'Score ($scoreUnit)'),
|
||||
value: draft.enabledMeasures.contains(WorkoutMeasure.score),
|
||||
value: _draft.enabledMeasures.contains(WorkoutMeasure.score),
|
||||
onChanged: (value) => _toggle(WorkoutMeasure.score, value),
|
||||
),
|
||||
if (draft.enabledMeasures.contains(WorkoutMeasure.time))
|
||||
if (_measureError != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
_measureError!,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text('Objectifs', style: Theme.of(context).textTheme.titleMedium),
|
||||
if (_draft.enabledMeasures.contains(WorkoutMeasure.time))
|
||||
_NumberField(
|
||||
label: 'Cible temps (s)',
|
||||
initialValue: draft.targetTimeSeconds,
|
||||
onChanged: (value) => draft.targetTimeSeconds = value?.round(),
|
||||
initialValue: _draft.targetTimeSeconds,
|
||||
onChanged: (value) => _draft.targetTimeSeconds = value?.round(),
|
||||
),
|
||||
if (draft.enabledMeasures.contains(WorkoutMeasure.reps))
|
||||
if (_draft.enabledMeasures.contains(WorkoutMeasure.reps))
|
||||
_NumberField(
|
||||
label: 'Cible répétitions',
|
||||
initialValue: draft.targetReps,
|
||||
onChanged: (value) => draft.targetReps = value?.round(),
|
||||
initialValue: _draft.targetReps,
|
||||
onChanged: (value) => _draft.targetReps = value?.round(),
|
||||
),
|
||||
if (draft.enabledMeasures.contains(WorkoutMeasure.score))
|
||||
if (_draft.enabledMeasures.contains(WorkoutMeasure.score))
|
||||
_NumberField(
|
||||
label: scoreUnit == null
|
||||
? 'Cible score'
|
||||
: 'Cible score ($scoreUnit)',
|
||||
initialValue: draft.targetScore,
|
||||
onChanged: (value) => draft.targetScore = value,
|
||||
initialValue: _draft.targetScore,
|
||||
onChanged: (value) => _draft.targetScore = value,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: 24),
|
||||
Text('Repos', style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
initialValue:
|
||||
'${draft.restSecondsOverride ?? defaultRestSeconds}',
|
||||
controller: _restController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Repos après chaque série (s)',
|
||||
helperText: 'Pas de repos après la toute dernière série.',
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
draft.restSecondsOverride = int.tryParse(value);
|
||||
onChanged();
|
||||
},
|
||||
validator: _positiveIntValidator,
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -535,14 +655,75 @@ final class _ProgramExerciseCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _toggle(WorkoutMeasure measure, bool? value) {
|
||||
if (value == true) {
|
||||
draft.enabledMeasures = {...draft.enabledMeasures, measure};
|
||||
} else if (draft.enabledMeasures.length > 1) {
|
||||
draft.enabledMeasures = {...draft.enabledMeasures}..remove(measure);
|
||||
void _toggle(WorkoutMeasure measure, bool value) {
|
||||
setState(() {
|
||||
_measureError = null;
|
||||
if (value) {
|
||||
_draft.enabledMeasures = {..._draft.enabledMeasures, measure};
|
||||
} else {
|
||||
_draft.enabledMeasures = {..._draft.enabledMeasures}..remove(measure);
|
||||
}
|
||||
onChanged();
|
||||
});
|
||||
}
|
||||
|
||||
String? _positiveIntValidator(String? value) {
|
||||
final number = int.tryParse(value?.trim() ?? '');
|
||||
if (number == null || number <= 0) {
|
||||
return 'Saisis un nombre supérieur à 0.';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _save() {
|
||||
final hasMeasure = _draft.enabledMeasures.isNotEmpty;
|
||||
setState(() {
|
||||
_measureError = hasMeasure
|
||||
? null
|
||||
: 'Active au moins une mesure pour enregistrer.';
|
||||
});
|
||||
if (!_formKey.currentState!.validate() || !hasMeasure) {
|
||||
return;
|
||||
}
|
||||
_draft.setsCount = int.parse(_setsController.text.trim());
|
||||
_draft.restSecondsOverride = int.parse(_restController.text.trim());
|
||||
Navigator.of(context).pop(_ProgramExerciseEditResult.saved(_draft));
|
||||
}
|
||||
|
||||
Future<void> _confirmDelete() async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Supprimer cet exercice ?'),
|
||||
content: const Text('Il sera retiré de ce programme.'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Annuler'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('Supprimer'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed == true && mounted) {
|
||||
Navigator.of(context).pop(const _ProgramExerciseEditResult.deleted());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class _ProgramExerciseEditResult {
|
||||
const _ProgramExerciseEditResult._({this.draft, required this.deleted});
|
||||
|
||||
const _ProgramExerciseEditResult.deleted()
|
||||
: this._(draft: null, deleted: true);
|
||||
|
||||
const _ProgramExerciseEditResult.saved(_ProgramExerciseDraft draft)
|
||||
: this._(draft: draft, deleted: false);
|
||||
|
||||
final _ProgramExerciseDraft? draft;
|
||||
final bool deleted;
|
||||
}
|
||||
|
||||
final class _NumberField extends StatelessWidget {
|
||||
@ -593,7 +774,10 @@ final class _ProgramExerciseDraft {
|
||||
this.restSecondsOverride,
|
||||
});
|
||||
|
||||
factory _ProgramExerciseDraft.fromExercise(Exercise exercise) {
|
||||
factory _ProgramExerciseDraft.fromExercise(
|
||||
Exercise exercise, {
|
||||
int? restSecondsOverride,
|
||||
}) {
|
||||
return _ProgramExerciseDraft(
|
||||
key: UniqueKey().toString(),
|
||||
sourceExerciseId: exercise.metadata.id,
|
||||
@ -609,6 +793,7 @@ final class _ProgramExerciseDraft {
|
||||
scoreUnitSnapshot: exercise.scoreUnit,
|
||||
setsCount: 3,
|
||||
enabledMeasures: exercise.availableMeasures,
|
||||
restSecondsOverride: restSecondsOverride,
|
||||
);
|
||||
}
|
||||
|
||||
@ -660,6 +845,30 @@ final class _ProgramExerciseDraft {
|
||||
double? targetScore;
|
||||
int? restSecondsOverride;
|
||||
|
||||
_ProgramExerciseDraft copy() {
|
||||
return _ProgramExerciseDraft(
|
||||
key: key,
|
||||
existingMetadata: existingMetadata,
|
||||
sourceExerciseId: sourceExerciseId,
|
||||
exerciseNameSnapshot: exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
||||
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
||||
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: availableTimeSnapshot,
|
||||
availableRepsSnapshot: availableRepsSnapshot,
|
||||
availableScoreSnapshot: availableScoreSnapshot,
|
||||
scoreLabelSnapshot: scoreLabelSnapshot,
|
||||
scoreUnitSnapshot: scoreUnitSnapshot,
|
||||
setsCount: setsCount,
|
||||
enabledMeasures: {...enabledMeasures},
|
||||
targetTimeSeconds: targetTimeSeconds,
|
||||
targetReps: targetReps,
|
||||
targetScore: targetScore,
|
||||
restSecondsOverride: restSecondsOverride,
|
||||
);
|
||||
}
|
||||
|
||||
ProgramExerciseConfig toConfig() {
|
||||
return ProgramExerciseConfig(
|
||||
existingMetadata: existingMetadata,
|
||||
@ -710,6 +919,11 @@ String _exerciseSummary(_ProgramExerciseDraft draft, int defaultRestSeconds) {
|
||||
return '${draft.setsCount} séries · $measures · Repos $rest s';
|
||||
}
|
||||
|
||||
String _restLabel(_ProgramExerciseDraft draft, int defaultRestSeconds) {
|
||||
final rest = draft.restSecondsOverride ?? defaultRestSeconds;
|
||||
return 'Repos $rest s';
|
||||
}
|
||||
|
||||
String _measureLabel(WorkoutMeasure measure) {
|
||||
return switch (measure) {
|
||||
WorkoutMeasure.time => 'Temps',
|
||||
|
||||
@ -8,6 +8,7 @@ void main() {
|
||||
testWidgets(
|
||||
'ajoute un exercice au programme avec sa configuration par défaut',
|
||||
(tester) async {
|
||||
final programRepository = _FakeProgramRepository();
|
||||
final exerciseRepository = _FakeExerciseRepository()
|
||||
..exercises.add(
|
||||
Exercise(
|
||||
@ -23,7 +24,7 @@ void main() {
|
||||
MaterialApp(
|
||||
home: ProgramFormScreen(
|
||||
programUseCases: _programUseCases(
|
||||
_FakeProgramRepository(),
|
||||
programRepository,
|
||||
exerciseRepository,
|
||||
),
|
||||
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
||||
@ -31,21 +32,30 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
await tester.enterText(
|
||||
find.widgetWithText(TextFormField, 'Nom'),
|
||||
'Jambes',
|
||||
);
|
||||
await tester.tap(find.text('Ajouter un exercice'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Squat'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Squat'), findsOneWidget);
|
||||
expect(
|
||||
find.text('3 séries · Temps + Répétitions · Repos 60 s'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.widgetWithText(CheckboxListTile, 'Temps'), findsOneWidget);
|
||||
expect(
|
||||
find.widgetWithText(CheckboxListTile, 'Répétitions'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('Repos 60 s'), findsOneWidget);
|
||||
expect(find.text('Exercice ajouté'), findsOneWidget);
|
||||
expect(find.byTooltip("Personnaliser l'exercice"), findsOneWidget);
|
||||
expect(find.byType(CheckboxListTile), findsNothing);
|
||||
|
||||
await tester.tap(find.text('Enregistrer'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final savedExercise = programRepository.programs.single.exercises.single;
|
||||
expect(savedExercise.setsCount, 3);
|
||||
expect(savedExercise.timeEnabled, isTrue);
|
||||
expect(savedExercise.repsEnabled, isTrue);
|
||||
expect(savedExercise.scoreEnabled, isFalse);
|
||||
expect(savedExercise.restSecondsOverride, 60);
|
||||
},
|
||||
);
|
||||
|
||||
@ -80,12 +90,16 @@ void main() {
|
||||
await tester.tap(find.text('Pompes'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byTooltip("Personnaliser l'exercice"));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text("Personnaliser l'exercice"), findsOneWidget);
|
||||
expect(
|
||||
find.widgetWithText(CheckboxListTile, 'Répétitions'),
|
||||
find.widgetWithText(SwitchListTile, 'Répétitions'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.widgetWithText(CheckboxListTile, 'Temps'), findsNothing);
|
||||
expect(find.widgetWithText(CheckboxListTile, 'Score'), findsNothing);
|
||||
expect(find.widgetWithText(SwitchListTile, 'Temps'), findsNothing);
|
||||
expect(find.widgetWithText(SwitchListTile, 'Score'), findsNothing);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user