Réorganise l'écran d'exécution autour d'une carte "Exercice actif" sous le compteur SÉRIE X/Y (nom, médias, temps de série, action "Démarrer l'exercice"). Le timer de série devient un état persistant dédié (plus un DateTime UI volatile), pause-aware, et "Démarrer l'exercice" lance en une action tous les chronos qui doivent démarrer en début de série (série, première étape temps, score chrono si stopwatch). Implémentation restée non commitée depuis sa réalisation ; QA a validé les vérifications statiques exécutables en sandbox (dart analyze, git diff --check). flutter analyze / flutter test restent à relancer dans un environnement avec cache Flutter SDK accessible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
707 lines
19 KiB
Dart
707 lines
19 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)),
|
||
);
|
||
|
||
@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;
|
||
}
|
||
|
||
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 {}
|
||
}
|