Lots B1+B2+B3 : schéma/domaine, requêtes d'agrégation et ports/use cases pour les statistiques de progression, avec tests associés. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
796 lines
22 KiB
Dart
796 lines
22 KiB
Dart
import 'dart:async';
|
||
import 'dart:convert';
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:gametime/application/application.dart';
|
||
import 'package:gametime/application/app_bootstrap.dart';
|
||
import 'package:gametime/domain/domain.dart';
|
||
import 'package:gametime/presentation/home_screen.dart';
|
||
|
||
void main() {
|
||
testWidgets('l’entrée Profil est présente sur l’accueil', (tester) async {
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
navigatorObservers: [homeRouteObserver],
|
||
home: HomeScreen(
|
||
bootstrap: _FakeBootstrap(_FakeActiveSessionRepository()),
|
||
),
|
||
),
|
||
);
|
||
await tester.pump();
|
||
|
||
expect(find.text('Profil'), findsOneWidget);
|
||
expect(find.text('Compte, synchronisation et partages'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('le bandeau de reprise apparaît au retour sur l’accueil', (
|
||
tester,
|
||
) async {
|
||
final navigatorKey = GlobalKey<NavigatorState>();
|
||
final activeRepository = _FakeActiveSessionRepository();
|
||
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
navigatorKey: navigatorKey,
|
||
navigatorObservers: [homeRouteObserver],
|
||
home: HomeScreen(bootstrap: _FakeBootstrap(activeRepository)),
|
||
),
|
||
);
|
||
await tester.pump();
|
||
await tester.pump();
|
||
|
||
expect(find.textContaining('Séance en cours'), findsNothing);
|
||
|
||
unawaited(
|
||
navigatorKey.currentState!.push<void>(
|
||
MaterialPageRoute(
|
||
builder: (context) => const Scaffold(body: Text('Séance active')),
|
||
),
|
||
),
|
||
);
|
||
await tester.pump();
|
||
await tester.pump(const Duration(milliseconds: 350));
|
||
|
||
activeRepository.session = ActiveWorkoutSession(
|
||
metadata: _metadata('session-1'),
|
||
sourceWorkoutTemplateId: 'template-1',
|
||
status: ActiveWorkoutStatus.paused,
|
||
startedAt: DateTime.utc(2026, 7, 17, 12),
|
||
pausedAt: DateTime.utc(2026, 7, 17, 12, 5),
|
||
lastPersistedAt: DateTime.utc(2026, 7, 17, 12, 5),
|
||
elapsedActiveMs: 300000,
|
||
currentProgramIndex: 0,
|
||
currentExerciseIndex: 0,
|
||
currentSetIndex: 1,
|
||
resolvedTemplateSnapshotJson: _sessionSnapshot(),
|
||
);
|
||
|
||
navigatorKey.currentState!.pop();
|
||
await tester.pump();
|
||
await tester.pump(const Duration(milliseconds: 350));
|
||
await tester.pump();
|
||
|
||
expect(
|
||
find.text('Séance en cours — Reprendre · Squat, série 2/3'),
|
||
findsOneWidget,
|
||
);
|
||
});
|
||
|
||
testWidgets('le bandeau de reprise disparaît au retour après abandon', (
|
||
tester,
|
||
) async {
|
||
final navigatorKey = GlobalKey<NavigatorState>();
|
||
final activeRepository = _FakeActiveSessionRepository()
|
||
..session = ActiveWorkoutSession(
|
||
metadata: _metadata('session-1'),
|
||
sourceWorkoutTemplateId: 'template-1',
|
||
status: ActiveWorkoutStatus.paused,
|
||
startedAt: DateTime.utc(2026, 7, 17, 12),
|
||
pausedAt: DateTime.utc(2026, 7, 17, 12, 5),
|
||
lastPersistedAt: DateTime.utc(2026, 7, 17, 12, 5),
|
||
elapsedActiveMs: 300000,
|
||
currentProgramIndex: 0,
|
||
currentExerciseIndex: 0,
|
||
currentSetIndex: 1,
|
||
resolvedTemplateSnapshotJson: _sessionSnapshot(),
|
||
);
|
||
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
navigatorKey: navigatorKey,
|
||
navigatorObservers: [homeRouteObserver],
|
||
home: HomeScreen(bootstrap: _FakeBootstrap(activeRepository)),
|
||
),
|
||
);
|
||
await tester.pump();
|
||
await tester.pump();
|
||
|
||
expect(find.textContaining('Séance en cours'), findsOneWidget);
|
||
|
||
unawaited(
|
||
navigatorKey.currentState!.push<void>(
|
||
MaterialPageRoute(
|
||
builder: (context) => const Scaffold(body: Text('Séance active')),
|
||
),
|
||
),
|
||
);
|
||
await tester.pump();
|
||
await tester.pump(const Duration(milliseconds: 350));
|
||
|
||
activeRepository.session = null;
|
||
navigatorKey.currentState!.pop();
|
||
await tester.pump();
|
||
await tester.pump(const Duration(milliseconds: 350));
|
||
await tester.pump();
|
||
|
||
expect(find.textContaining('Séance en cours'), findsNothing);
|
||
});
|
||
}
|
||
|
||
final class _FakeBootstrap implements AppDependencies {
|
||
_FakeBootstrap(_FakeActiveSessionRepository activeRepository)
|
||
: authUseCases = AuthUseCases(
|
||
tokenStore: _FakeAuthTokenStore(),
|
||
accountRepository: _FakeOnlineAccountRepository(),
|
||
remoteAuthApi: _FakeRemoteAuthApi(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
ids: _FakeIds(),
|
||
),
|
||
syncUseCases = SyncUseCases(
|
||
tokenStore: _FakeAuthTokenStore(),
|
||
remoteSyncApi: _FakeRemoteSyncApi(),
|
||
metadataRepository: _FakeSyncMetadataRepository(),
|
||
mappingRepository: _FakeRemoteResourceMappingRepository(),
|
||
localChanges: _FakeLocalSyncChangeRepository(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
deviceId: 'device-1',
|
||
),
|
||
shareUseCases = ShareUseCases(
|
||
tokenStore: _FakeAuthTokenStore(),
|
||
remoteShareApi: _FakeRemoteShareApi(),
|
||
inboxRepository: _FakeShareInboxRepository(),
|
||
pendingActionRepository: _FakePendingShareActionRepository(),
|
||
localChanges: _FakeLocalSyncChangeRepository(),
|
||
programRepository: _FakeProgramRepository(),
|
||
templateRepository: _FakeWorkoutTemplateRepository(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
ids: _FakeIds(),
|
||
originDeviceId: 'device-1',
|
||
),
|
||
activeWorkoutSessionUseCases = ActiveWorkoutSessionUseCases(
|
||
sessionRepository: activeRepository,
|
||
templateRepository: _FakeWorkoutTemplateRepository(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
ids: _FakeIds(),
|
||
originDeviceId: 'device-1',
|
||
),
|
||
activeExerciseStepUseCases = ActiveExerciseStepUseCases(
|
||
sessionRepository: activeRepository,
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
ids: _FakeIds(),
|
||
originDeviceId: 'device-1',
|
||
),
|
||
closeWorkoutSessionUseCase = CloseWorkoutSessionUseCase(
|
||
sessionRepository: activeRepository,
|
||
historyRepository: _FakeWorkoutHistoryRepository(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
ids: _FakeIds(),
|
||
originDeviceId: 'device-1',
|
||
),
|
||
exerciseUseCases = ExerciseUseCases(
|
||
repository: _FakeExerciseRepository(),
|
||
programRepository: _FakeProgramRepository(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
ids: _FakeIds(),
|
||
originDeviceId: 'device-1',
|
||
),
|
||
mediaUseCases = MediaUseCases(
|
||
mediaRepository: _FakeMediaAssetRepository(),
|
||
exerciseRepository: _FakeExerciseRepository(),
|
||
storage: _FakeMediaStorage(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
ids: _FakeIds(),
|
||
originDeviceId: 'device-1',
|
||
),
|
||
programUseCases = ProgramUseCases(
|
||
programRepository: _FakeProgramRepository(),
|
||
exerciseRepository: _FakeExerciseRepository(),
|
||
templateRepository: _FakeWorkoutTemplateRepository(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
ids: _FakeIds(),
|
||
originDeviceId: 'device-1',
|
||
),
|
||
workoutTemplateUseCases = WorkoutTemplateUseCases(
|
||
templateRepository: _FakeWorkoutTemplateRepository(),
|
||
programRepository: _FakeProgramRepository(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
ids: _FakeIds(),
|
||
originDeviceId: 'device-1',
|
||
),
|
||
workoutHistoryUseCases = WorkoutHistoryUseCases(
|
||
repository: _FakeWorkoutHistoryRepository(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
),
|
||
progressionStatsUseCase = ProgressionStatsUseCase(
|
||
repository: _FakeProgressionStatsRepository(),
|
||
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||
),
|
||
exercisePerformanceReferenceUseCase = ExercisePerformanceReferenceUseCase(
|
||
repository: _FakeExercisePerformanceReferenceRepository(),
|
||
);
|
||
|
||
@override
|
||
final AuthUseCases authUseCases;
|
||
|
||
@override
|
||
final SyncUseCases syncUseCases;
|
||
|
||
@override
|
||
final ShareUseCases shareUseCases;
|
||
|
||
@override
|
||
final ExerciseUseCases exerciseUseCases;
|
||
|
||
@override
|
||
final MediaUseCases mediaUseCases;
|
||
|
||
@override
|
||
final ProgramUseCases programUseCases;
|
||
|
||
@override
|
||
final WorkoutTemplateUseCases workoutTemplateUseCases;
|
||
|
||
@override
|
||
final ActiveWorkoutSessionUseCases activeWorkoutSessionUseCases;
|
||
|
||
@override
|
||
final ActiveExerciseStepUseCases activeExerciseStepUseCases;
|
||
|
||
@override
|
||
final CloseWorkoutSessionUseCase closeWorkoutSessionUseCase;
|
||
|
||
@override
|
||
final WorkoutHistoryUseCases workoutHistoryUseCases;
|
||
|
||
@override
|
||
final ProgressionStatsUseCase progressionStatsUseCase;
|
||
|
||
@override
|
||
final ExercisePerformanceReferenceUseCase exercisePerformanceReferenceUseCase;
|
||
}
|
||
|
||
String _sessionSnapshot() {
|
||
return jsonEncode({
|
||
'name': 'Séance jambes',
|
||
'programs': [
|
||
{
|
||
'id': 'template-program-1',
|
||
'programNameSnapshot': 'Programme jambes',
|
||
'programSnapshotJson': jsonEncode({
|
||
'exercises': [
|
||
{
|
||
'id': 'exercise-1',
|
||
'exerciseNameSnapshot': 'Squat',
|
||
'setsCount': 3,
|
||
'timeEnabled': true,
|
||
'repsEnabled': false,
|
||
'scoreEnabled': false,
|
||
'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',
|
||
);
|
||
}
|
||
|
||
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 _FakeAuthTokenStore implements AuthTokenStore {
|
||
@override
|
||
Future<void> clearToken() async {}
|
||
|
||
@override
|
||
Future<String?> readToken() async => null;
|
||
|
||
@override
|
||
Future<void> saveToken(String token, DateTime expiresAt) async {}
|
||
}
|
||
|
||
final class _FakeOnlineAccountRepository implements OnlineAccountRepository {
|
||
@override
|
||
Future<void> clearSession() async {}
|
||
|
||
@override
|
||
Future<UserAccountSession?> currentSession() async => null;
|
||
|
||
@override
|
||
Future<void> saveSession(UserAccountSession session) async {}
|
||
}
|
||
|
||
final class _FakeRemoteAuthApi implements RemoteAuthApi {
|
||
@override
|
||
Future<RemoteAuthResult> login({
|
||
required String email,
|
||
required String password,
|
||
}) async {
|
||
throw const RemoteAuthException(RemoteAuthFailure.network);
|
||
}
|
||
|
||
@override
|
||
Future<void> logout(String token) async {}
|
||
|
||
@override
|
||
Future<RemoteAuthResult> register({
|
||
required String email,
|
||
required String password,
|
||
String? displayName,
|
||
}) async {
|
||
throw const RemoteAuthException(RemoteAuthFailure.network);
|
||
}
|
||
}
|
||
|
||
final class _FakeRemoteSyncApi implements RemoteSyncApi {
|
||
@override
|
||
Future<RemoteSyncPullResult> pull({
|
||
required String? since,
|
||
required String token,
|
||
}) async {
|
||
return const RemoteSyncPullResult(serverCursor: null, items: []);
|
||
}
|
||
|
||
@override
|
||
Future<RemoteSyncPushResult> push({
|
||
required String deviceId,
|
||
required List<RemoteSyncPushItem> items,
|
||
required String token,
|
||
}) async {
|
||
return const RemoteSyncPushResult(serverCursor: null, results: []);
|
||
}
|
||
}
|
||
|
||
final class _FakeSyncMetadataRepository implements SyncMetadataRepository {
|
||
SyncMetadataSnapshot _metadata = const SyncMetadataSnapshot();
|
||
|
||
@override
|
||
Future<SyncMetadataSnapshot> read() async => _metadata;
|
||
|
||
@override
|
||
Future<void> save(SyncMetadataSnapshot metadata) async {
|
||
_metadata = metadata;
|
||
}
|
||
}
|
||
|
||
final class _FakeRemoteResourceMappingRepository
|
||
implements RemoteResourceMappingRepository {
|
||
@override
|
||
Future<RemoteResourceMapping?> find({
|
||
required SyncResourceType resourceType,
|
||
required String clientId,
|
||
}) async {
|
||
return null;
|
||
}
|
||
|
||
@override
|
||
Future<void> save(RemoteResourceMapping mapping) async {}
|
||
}
|
||
|
||
final class _FakeLocalSyncChangeRepository
|
||
implements LocalSyncChangeRepository {
|
||
@override
|
||
Future<bool> applyRemoteItem(RemoteSyncedItem item) async => false;
|
||
|
||
@override
|
||
Future<List<PendingSyncChange>> listPendingChanges() async => const [];
|
||
|
||
@override
|
||
Future<void> markChangesSynced(
|
||
List<String> changeLogIds,
|
||
DateTime syncedAt,
|
||
) async {}
|
||
}
|
||
|
||
final class _FakeRemoteShareApi implements RemoteShareApi {
|
||
@override
|
||
Future<RemoteSyncedItem> acceptShare(String shareId, String token) async {
|
||
throw const RemoteAuthException(RemoteAuthFailure.network);
|
||
}
|
||
|
||
@override
|
||
Future<void> declineShare(String shareId, String token) async {}
|
||
|
||
@override
|
||
Future<List<ShareInboxItem>> fetchInbox(String token) async => const [];
|
||
|
||
@override
|
||
Future<void> revokeShare(String shareId, String token) async {}
|
||
|
||
@override
|
||
Future<RemoteShareSendResult> sendShare({
|
||
required ShareResourceType resourceType,
|
||
required Map<String, Object?> payload,
|
||
required List<String> recipientEmails,
|
||
required String token,
|
||
}) async {
|
||
return const RemoteShareSendResult(
|
||
shareId: 'share-1',
|
||
recipientUserIds: [],
|
||
unresolvedEmails: [],
|
||
);
|
||
}
|
||
}
|
||
|
||
final class _FakeShareInboxRepository implements ShareInboxRepository {
|
||
@override
|
||
Future<ShareInboxItem?> findByShareId(String shareId) async => null;
|
||
|
||
@override
|
||
Future<List<ShareInboxItem>> listAll() async => const [];
|
||
|
||
@override
|
||
Future<void> markStatus(
|
||
String shareId,
|
||
ShareInboxStatus status,
|
||
DateTime respondedAt,
|
||
) async {}
|
||
|
||
@override
|
||
Future<void> upsert(ShareInboxItem item) async {}
|
||
|
||
@override
|
||
Future<void> upsertAll(List<ShareInboxItem> items) async {}
|
||
}
|
||
|
||
final class _FakePendingShareActionRepository
|
||
implements PendingShareActionRepository {
|
||
@override
|
||
Future<void> add(PendingShareAction action) async {}
|
||
|
||
@override
|
||
Future<List<PendingShareAction>> listPending() async => const [];
|
||
|
||
@override
|
||
Future<void> markFailed(String id, DateTime attemptedAt) async {}
|
||
|
||
@override
|
||
Future<void> markSucceeded(String id, DateTime attemptedAt) async {}
|
||
}
|
||
|
||
final class _FakeActiveSessionRepository implements ActiveSessionRepository {
|
||
ActiveWorkoutSession? session;
|
||
final restStates = <ActiveRestState>[];
|
||
final setTimerStates = <ActiveSetTimerState>[];
|
||
final scoreStopwatchStates = <ActiveScoreStopwatchState>[];
|
||
final results = <ActiveSetResult>[];
|
||
|
||
@override
|
||
Future<ActiveWorkoutSession?> findById(String id) async {
|
||
return session?.metadata.id == id ? session : null;
|
||
}
|
||
|
||
@override
|
||
Future<ActiveWorkoutSession?> findOpen() async => session;
|
||
|
||
@override
|
||
Future<ActiveRestState?> findRestStateById(String id) async => null;
|
||
|
||
@override
|
||
Future<ActiveScoreStopwatchState?> findScoreStopwatchState({
|
||
required String sessionId,
|
||
required int programIndex,
|
||
required int exerciseIndex,
|
||
required int setIndex,
|
||
}) async {
|
||
return null;
|
||
}
|
||
|
||
@override
|
||
Future<ActiveSetTimerState?> findSetTimerState({
|
||
required String sessionId,
|
||
required int programIndex,
|
||
required int exerciseIndex,
|
||
required int setIndex,
|
||
}) async {
|
||
return null;
|
||
}
|
||
|
||
@override
|
||
Future<ActiveExerciseStepProgressState?> findExerciseStepProgressState({
|
||
required String sessionId,
|
||
required int programIndex,
|
||
required int exerciseIndex,
|
||
required int setIndex,
|
||
}) async {
|
||
return null;
|
||
}
|
||
|
||
@override
|
||
Future<List<ActiveRestState>> listRestStates(String sessionId) async {
|
||
return restStates;
|
||
}
|
||
|
||
@override
|
||
Future<List<ActiveScoreStopwatchState>> listScoreStopwatchStates(
|
||
String sessionId,
|
||
) async {
|
||
return scoreStopwatchStates;
|
||
}
|
||
|
||
@override
|
||
Future<List<ActiveSetTimerState>> listSetTimerStates(String sessionId) async {
|
||
return setTimerStates;
|
||
}
|
||
|
||
@override
|
||
Future<List<ActiveSetResult>> listSetResults(String sessionId) async {
|
||
return results;
|
||
}
|
||
|
||
@override
|
||
Future<List<ActiveExerciseStepProgressState>> listExerciseStepProgressStates(
|
||
String sessionId,
|
||
) async {
|
||
return const [];
|
||
}
|
||
|
||
@override
|
||
Future<List<ActiveExerciseStepResult>> listExerciseStepResults(
|
||
String sessionId,
|
||
) async {
|
||
return const [];
|
||
}
|
||
|
||
@override
|
||
Future<void> save(ActiveWorkoutSession session) async {
|
||
this.session = session;
|
||
}
|
||
|
||
@override
|
||
Future<void> saveRestState(ActiveRestState restState) async {
|
||
restStates.add(restState);
|
||
}
|
||
|
||
@override
|
||
Future<void> saveScoreStopwatchState(ActiveScoreStopwatchState state) async {
|
||
scoreStopwatchStates.add(state);
|
||
}
|
||
|
||
@override
|
||
Future<void> saveSetTimerState(ActiveSetTimerState state) async {
|
||
setTimerStates.add(state);
|
||
}
|
||
|
||
@override
|
||
Future<void> saveExerciseStepProgressState(
|
||
ActiveExerciseStepProgressState state,
|
||
) async {}
|
||
|
||
@override
|
||
Future<void> saveExerciseStepResult(ActiveExerciseStepResult result) async {}
|
||
|
||
@override
|
||
Future<void> deleteScoreStopwatchState({
|
||
required String sessionId,
|
||
required int programIndex,
|
||
required int exerciseIndex,
|
||
required int setIndex,
|
||
required DateTime deletedAt,
|
||
}) async {}
|
||
|
||
@override
|
||
Future<void> saveSetResult(ActiveSetResult result) async {
|
||
results.add(result);
|
||
}
|
||
}
|
||
|
||
final class _FakeExerciseRepository implements ExerciseRepository {
|
||
@override
|
||
Future<Exercise?> findById(String id) async => null;
|
||
|
||
@override
|
||
Future<bool> isReferencedByProgram(String id) async => false;
|
||
|
||
@override
|
||
Future<List<Exercise>> listActive() async => const [];
|
||
|
||
@override
|
||
Future<void> save(Exercise exercise) async {}
|
||
}
|
||
|
||
final class _FakeMediaAssetRepository implements MediaAssetRepository {
|
||
@override
|
||
Future<MediaAsset?> findById(String id) async => null;
|
||
|
||
@override
|
||
Future<List<MediaAsset>> listActive() async => const [];
|
||
|
||
@override
|
||
Future<void> save(MediaAsset mediaAsset) async {}
|
||
}
|
||
|
||
final class _FakeMediaStorage implements LocalMediaStorage {
|
||
@override
|
||
Future<void> deleteByLocalUri(String localUri) async {}
|
||
|
||
@override
|
||
Future<StoredMediaFile> importFile({
|
||
required String sourcePath,
|
||
required MediaKind kind,
|
||
required String stableFileName,
|
||
}) async {
|
||
return StoredMediaFile(
|
||
localUri: 'file:///$stableFileName',
|
||
mimeType: 'image/png',
|
||
sizeBytes: 0,
|
||
);
|
||
}
|
||
|
||
@override
|
||
Future<Set<String>> listManagedLocalUris() async => const {};
|
||
}
|
||
|
||
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 _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 _FakeWorkoutHistoryRepository implements WorkoutHistoryRepository {
|
||
@override
|
||
Future<void> delete(String id, DateTime deletedAt) async {}
|
||
|
||
@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 {}
|
||
|
||
@override
|
||
Future<void> saveStepResult(WorkoutHistoryStepResult result) async {}
|
||
}
|
||
|
||
final class _FakeProgressionStatsRepository
|
||
implements ProgressionStatsRepository {
|
||
@override
|
||
Future<ProgressionGlobalStatsData> readGlobalStats(
|
||
ProgressionDateRange range,
|
||
) async {
|
||
return const ProgressionGlobalStatsData(
|
||
completedSessionCount: 0,
|
||
totalActiveMs: 0,
|
||
activeWeekStarts: [],
|
||
hasAnyCompletedHistory: false,
|
||
);
|
||
}
|
||
|
||
@override
|
||
Future<List<ProgressionExerciseOptionData>> listExerciseOptions(
|
||
ProgressionDateRange range,
|
||
) async {
|
||
return const [];
|
||
}
|
||
|
||
@override
|
||
Future<List<ProgressionMeasureOptionData>> listMeasureOptions({
|
||
required ProgressionDateRange range,
|
||
required String exerciseKey,
|
||
}) async {
|
||
return const [];
|
||
}
|
||
|
||
@override
|
||
Future<ProgressionExerciseSeriesData> readExerciseSeries({
|
||
required ProgressionDateRange range,
|
||
required String exerciseKey,
|
||
required ProgressionMeasure measure,
|
||
}) async {
|
||
return ProgressionExerciseSeriesData(
|
||
exerciseKey: exerciseKey,
|
||
exerciseNameSnapshot: exerciseKey,
|
||
measure: ProgressionMeasureOptionData(
|
||
measure: measure,
|
||
label: 'Score',
|
||
lowerIsBetter: false,
|
||
),
|
||
points: const [],
|
||
hasAnyAllTimeData: false,
|
||
hasAnyCompletedExerciseResult: false,
|
||
);
|
||
}
|
||
}
|
||
|
||
final class _FakeExercisePerformanceReferenceRepository
|
||
implements ExercisePerformanceReferenceRepository {
|
||
@override
|
||
Future<bool> hasAnyCompletedHistoryForExercise(String exerciseId) async {
|
||
return false;
|
||
}
|
||
|
||
@override
|
||
Future<WorkoutHistorySetPerformance?> findLatestSetPerformance({
|
||
required String exerciseId,
|
||
required ActivePerformanceMeasures activeMeasures,
|
||
required int currentSetIndex,
|
||
}) async {
|
||
return null;
|
||
}
|
||
|
||
@override
|
||
Future<WorkoutHistoryMetricPerformance?> findBestMetricPerformance({
|
||
required String exerciseId,
|
||
required PerformanceMetric metric,
|
||
required ScoreInputMode scoreInputMode,
|
||
}) async {
|
||
return null;
|
||
}
|
||
}
|