Files
GameTime/test/presentation/program_screen_test.dart
Blomios 2f732aa1b1 feat(ui): configuration du score chronométré (ticket #26)
Ajoute la configuration du score chronométré sur exercise_library_screen.dart
et program_screen.dart, s'appuyant sur les fondations domain du ticket
#25. flutter analyze propre, 41/41 tests verts, build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 09:08:16 +02:00

280 lines
7.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gametime/application/application.dart';
import 'package:gametime/domain/domain.dart';
import 'package:gametime/presentation/presentation.dart';
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(
metadata: _metadata('exercise-1'),
name: 'Squat',
hasTimeMeasure: true,
hasRepsMeasure: true,
hasScoreMeasure: false,
),
);
await tester.pumpWidget(
MaterialApp(
home: ProgramFormScreen(
programUseCases: _programUseCases(
programRepository,
exerciseRepository,
),
exerciseUseCases: _exerciseUseCases(exerciseRepository),
),
),
);
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('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);
},
);
testWidgets(
'limite les mesures aux mesures autorisées par lexercice source',
(tester) async {
final exerciseRepository = _FakeExerciseRepository()
..exercises.add(
Exercise(
metadata: _metadata('exercise-1'),
name: 'Pompes',
hasTimeMeasure: false,
hasRepsMeasure: true,
hasScoreMeasure: false,
),
);
await tester.pumpWidget(
MaterialApp(
home: ProgramFormScreen(
programUseCases: _programUseCases(
_FakeProgramRepository(),
exerciseRepository,
),
exerciseUseCases: _exerciseUseCases(exerciseRepository),
),
),
);
await tester.tap(find.text('Ajouter un exercice'));
await tester.pumpAndSettle();
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(SwitchListTile, 'Répétitions'),
findsOneWidget,
);
expect(find.widgetWithText(SwitchListTile, 'Temps'), findsNothing);
expect(find.widgetWithText(SwitchListTile, 'Score'), findsNothing);
},
);
testWidgets('affiche lobjectif de chrono pour un score en mode chrono', (
tester,
) async {
final exerciseRepository = _FakeExerciseRepository()
..exercises.add(
Exercise(
metadata: _metadata('exercise-1'),
name: 'Suicides',
hasTimeMeasure: true,
hasRepsMeasure: true,
hasScoreMeasure: true,
scoreInputMode: ScoreInputMode.stopwatch,
scoreLabel: 'Temps réalisé',
),
);
await tester.pumpWidget(
MaterialApp(
home: ProgramFormScreen(
programUseCases: _programUseCases(
_FakeProgramRepository(),
exerciseRepository,
),
exerciseUseCases: _exerciseUseCases(exerciseRepository),
),
),
);
await tester.tap(find.text('Ajouter un exercice'));
await tester.pumpAndSettle();
await tester.tap(find.text('Suicides'));
await tester.pumpAndSettle();
await tester.tap(find.byTooltip("Personnaliser l'exercice"));
await tester.pumpAndSettle();
expect(find.text('Score chrono'), findsOneWidget);
await tester.dragUntilVisible(
find.text(
"Temps sert d'objectif de durée ; Score chrono enregistre "
'le temps réalisé.',
),
find.byType(ListView),
const Offset(0, -200),
);
expect(
find.text(
"Temps sert d'objectif de durée ; Score chrono enregistre "
'le temps réalisé.',
),
findsOneWidget,
);
await tester.dragUntilVisible(
find.text('Objectif de chrono'),
find.byType(ListView),
const Offset(0, -200),
);
expect(
find.widgetWithText(TextFormField, 'Objectif de chrono'),
findsOneWidget,
);
expect(find.widgetWithText(TextFormField, 'Cible score'), findsNothing);
expect(
find.text('Le résultat réel sera mesuré pendant la série.'),
findsOneWidget,
);
});
}
ExerciseUseCases _exerciseUseCases(_FakeExerciseRepository repository) {
return ExerciseUseCases(
repository: repository,
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
ids: _FakeIds(),
originDeviceId: 'device-1',
);
}
ProgramUseCases _programUseCases(
_FakeProgramRepository programRepository,
_FakeExerciseRepository exerciseRepository,
) {
return ProgramUseCases(
programRepository: programRepository,
exerciseRepository: exerciseRepository,
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
ids: _FakeIds(),
originDeviceId: 'device-1',
);
}
EntityMetadata _metadata(String id) {
return EntityMetadata(
id: id,
createdAt: DateTime.utc(2026, 7, 17),
updatedAt: DateTime.utc(2026, 7, 17),
originDeviceId: 'device-1',
);
}
final class _FakeClock implements Clock {
const _FakeClock(this.value);
final DateTime value;
@override
DateTime now() => value;
}
final class _FakeIds implements IdGenerator {
var _next = 0;
@override
String newId() {
_next += 1;
return 'id-$_next';
}
}
final class _FakeExerciseRepository implements ExerciseRepository {
final exercises = <Exercise>[];
final saved = <Exercise>[];
@override
Future<Exercise?> findById(String id) async {
for (final exercise in exercises) {
if (exercise.metadata.id == id) {
return exercise;
}
}
return null;
}
@override
Future<bool> isReferencedByProgram(String id) async => false;
@override
Future<List<Exercise>> listActive() async => exercises;
@override
Future<void> save(Exercise exercise) async {
saved.add(exercise);
}
}
final class _FakeProgramRepository implements ProgramRepository {
final programs = <Program>[];
@override
Future<Program?> findById(String id) async {
for (final program in programs) {
if (program.metadata.id == id) {
return program;
}
}
return null;
}
@override
Future<List<Program>> listActive() async => programs;
@override
Future<void> replaceExercises(Program program, DateTime deletedAt) async {
programs.removeWhere((item) => item.metadata.id == program.metadata.id);
programs.add(program);
}
@override
Future<void> save(Program program) async {
programs.add(program);
}
@override
Future<void> saveExercise(ProgramExercise exercise) async {}
}