Files
GameTime/test/presentation/history_screen_test.dart
Blomios 9f9d0dbe3b fix(persistence): correctifs post-QA sur les repositories (ticket #13)
Corrige les ports, use cases, entités et l'implémentation Drift des
repositories suite aux retours de QA. flutter analyze propre, 24/24
tests verts, build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:14:07 +02:00

309 lines
8.3 KiB
Dart

import 'dart:convert';
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('la liste groupe les séances par période', (tester) async {
final groups = groupHistoriesByPeriod([
_history(id: 'today', startedAt: DateTime.utc(2026, 7, 17, 9)),
_history(id: 'week', startedAt: DateTime.utc(2026, 7, 15, 9)),
_history(id: 'old', startedAt: DateTime.utc(2026, 7, 1, 9)),
], DateTime.utc(2026, 7, 17, 12));
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Column(children: [for (final key in groups.keys) Text(key)]),
),
),
);
expect(find.text("Aujourd'hui"), findsOneWidget);
expect(find.text('Cette semaine'), findsOneWidget);
expect(find.text('Plus ancien'), findsOneWidget);
});
testWidgets('le détail affiche temps répétitions et score avec unité', (
tester,
) async {
await tester.pumpWidget(
MaterialApp(
home: HistoryDetailScreen(
history: _history(id: 'history-1'),
historyUseCases: _historyUseCases(_FakeWorkoutHistoryRepository()),
workoutTemplateUseCases: _workoutTemplateUseCases(),
activeUseCases: _activeUseCases(),
closeUseCase: _closeUseCase(),
),
),
);
expect(find.text('Programme jambes'), findsOneWidget);
expect(find.text('Squat'), findsOneWidget);
expect(
find.text('Temps 00:45 · 10 répétitions · Score 80 kg'),
findsOneWidget,
);
});
testWidgets('la suppression demande une confirmation', (tester) async {
final repository = _FakeWorkoutHistoryRepository();
await tester.pumpWidget(
MaterialApp(
home: HistoryDetailScreen(
history: _history(id: 'history-1'),
historyUseCases: _historyUseCases(repository),
workoutTemplateUseCases: _workoutTemplateUseCases(),
activeUseCases: _activeUseCases(),
closeUseCase: _closeUseCase(),
),
),
);
await tester.tap(find.text("Supprimer de l'historique"));
await tester.pumpAndSettle();
expect(repository.deletedIds, isEmpty);
expect(find.text("Supprimer de l'historique ?"), findsOneWidget);
await tester.tap(find.text('Annuler'));
await tester.pumpAndSettle();
expect(repository.deletedIds, isEmpty);
await tester.tap(find.text("Supprimer de l'historique"));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(FilledButton, 'Supprimer'));
await tester.pumpAndSettle();
expect(repository.deletedIds, ['history-1']);
});
}
WorkoutHistory _history({required String id, DateTime? startedAt}) {
final start = startedAt ?? DateTime.utc(2026, 7, 17, 10);
return WorkoutHistory(
metadata: _metadata(id),
sourceWorkoutTemplateId: 'template-1',
sourceActiveWorkoutSessionId: 'session-1',
nameSnapshot: 'Séance jambes',
startedAt: start,
endedAt: start.add(const Duration(minutes: 30)),
totalActiveMs: 1800000,
completed: true,
historySnapshotJson: jsonEncode({
'sessionId': 'session-1',
'resolvedTemplateSnapshotJson': _resolvedSnapshot(),
'results': [
{
'programSnapshotId': 'program-snapshot-1',
'exerciseSnapshotId': 'exercise-snapshot-1',
'programIndex': 0,
'exerciseIndex': 0,
'setIndex': 0,
'actualTimeMs': 45000,
'actualReps': 10,
'actualScore': 80,
'scoreLabelSnapshot': 'Charge',
'scoreUnitSnapshot': 'kg',
},
],
}),
);
}
String _resolvedSnapshot() {
return jsonEncode({
'name': 'Séance jambes',
'programs': [
{
'id': 'program-snapshot-1',
'programNameSnapshot': 'Programme jambes',
'programSnapshotJson': jsonEncode({
'exercises': [
{
'id': 'exercise-snapshot-1',
'exerciseNameSnapshot': 'Squat',
'setsCount': 1,
'timeEnabled': true,
'repsEnabled': true,
'scoreEnabled': true,
'targetTimeSeconds': 45,
'targetReps': 10,
'targetScore': 80,
'scoreLabelSnapshot': 'Charge',
'scoreUnitSnapshot': 'kg',
'restSecondsOverride': 0,
},
],
}),
},
],
'overrides': const [],
});
}
EntityMetadata _metadata(String id) {
return EntityMetadata(
id: id,
createdAt: DateTime.utc(2026, 7, 17),
updatedAt: DateTime.utc(2026, 7, 17),
originDeviceId: 'device-1',
);
}
WorkoutHistoryUseCases _historyUseCases(
_FakeWorkoutHistoryRepository repository,
) {
return WorkoutHistoryUseCases(
repository: repository,
clock: _FakeClock(DateTime.utc(2026, 7, 17)),
);
}
WorkoutTemplateUseCases _workoutTemplateUseCases() {
return WorkoutTemplateUseCases(
templateRepository: _FakeWorkoutTemplateRepository(),
programRepository: _FakeProgramRepository(),
clock: _FakeClock(DateTime.utc(2026, 7, 17)),
ids: _FakeIds(),
originDeviceId: 'device-1',
);
}
ActiveWorkoutSessionUseCases _activeUseCases() {
return ActiveWorkoutSessionUseCases(
sessionRepository: _FakeActiveSessionRepository(),
templateRepository: _FakeWorkoutTemplateRepository(),
clock: _FakeClock(DateTime.utc(2026, 7, 17)),
ids: _FakeIds(),
originDeviceId: 'device-1',
);
}
CloseWorkoutSessionUseCase _closeUseCase() {
return CloseWorkoutSessionUseCase(
sessionRepository: _FakeActiveSessionRepository(),
historyRepository: _FakeWorkoutHistoryRepository(),
clock: _FakeClock(DateTime.utc(2026, 7, 17)),
ids: _FakeIds(),
originDeviceId: 'device-1',
);
}
final class _FakeClock implements Clock {
_FakeClock(this.value);
DateTime value;
@override
DateTime now() => value;
}
final class _FakeIds implements IdGenerator {
var _next = 0;
@override
String newId() {
_next += 1;
return 'id-$_next';
}
}
final class _FakeWorkoutHistoryRepository implements WorkoutHistoryRepository {
final deletedIds = <String>[];
@override
Future<void> delete(String id, DateTime deletedAt) async {
deletedIds.add(id);
}
@override
Future<WorkoutHistory?> findById(String id) async => null;
@override
Future<List<WorkoutHistory>> listActive() async => const [];
@override
Future<void> save(WorkoutHistory history) async {}
@override
Future<void> saveSetResult(WorkoutHistorySetResult result) async {}
}
final class _FakeWorkoutTemplateRepository
implements WorkoutTemplateRepository {
@override
Future<WorkoutTemplate?> findById(String id) async => null;
@override
Future<List<WorkoutTemplate>> listActive() async => const [];
@override
Future<void> replaceComposition(
WorkoutTemplate template,
DateTime deletedAt,
) async {}
@override
Future<void> save(WorkoutTemplate template) async {}
@override
Future<void> saveOverride(WorkoutTemplateExerciseOverride override) async {}
@override
Future<void> saveProgram(WorkoutTemplateProgram program) async {}
}
final class _FakeProgramRepository implements ProgramRepository {
@override
Future<Program?> findById(String id) async => null;
@override
Future<List<Program>> listActive() async => const [];
@override
Future<void> replaceExercises(Program program, DateTime deletedAt) async {}
@override
Future<void> save(Program program) async {}
@override
Future<void> saveExercise(ProgramExercise exercise) async {}
}
final class _FakeActiveSessionRepository implements ActiveSessionRepository {
@override
Future<ActiveWorkoutSession?> findById(String id) async => null;
@override
Future<ActiveWorkoutSession?> findOpen() async => null;
@override
Future<ActiveRestState?> findRestStateById(String id) async => null;
@override
Future<List<ActiveRestState>> listRestStates(String sessionId) async {
return const [];
}
@override
Future<List<ActiveSetResult>> listSetResults(String sessionId) async {
return const [];
}
@override
Future<void> save(ActiveWorkoutSession session) async {}
@override
Future<void> saveRestState(ActiveRestState restState) async {}
@override
Future<void> saveSetResult(ActiveSetResult result) async {}
}