Ajoute l'écran de programme (presentation/program_screen.dart) et un écran d'accueil (presentation/home_screen.dart) pour la navigation, avec les ajustements application/domaine/repositories nécessaires. flutter analyze propre, 11/11 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
124 lines
3.3 KiB
Dart
124 lines
3.3 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:gametime/application/application.dart';
|
|
import 'package:gametime/domain/domain.dart';
|
|
|
|
void main() {
|
|
test('Exercise requires at least one active measure', () {
|
|
expect(
|
|
() => Exercise(
|
|
metadata: _metadata('exercise-1'),
|
|
name: 'Push up',
|
|
hasTimeMeasure: false,
|
|
hasRepsMeasure: false,
|
|
hasScoreMeasure: false,
|
|
),
|
|
throwsA(isA<DomainException>()),
|
|
);
|
|
});
|
|
|
|
test('Workout template use case rejects active measure overrides', () async {
|
|
final useCase = WorkoutTemplateUseCases(
|
|
templateRepository: _FakeWorkoutTemplateRepository(),
|
|
programRepository: _FakeProgramRepository(),
|
|
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
|
ids: _FakeIds(),
|
|
originDeviceId: 'device-1',
|
|
);
|
|
|
|
await expectLater(
|
|
useCase.overrideExerciseTargets(
|
|
workoutTemplateProgramId: 'template-program-1',
|
|
snapshotProgramExerciseId: 'exercise-snapshot-1',
|
|
attemptedMeasureOverride: {WorkoutMeasure.reps},
|
|
),
|
|
throwsA(isA<DomainException>()),
|
|
);
|
|
});
|
|
|
|
test('Active session elapsed time is based on persisted timestamps', () {
|
|
final clock = _FakeClock(DateTime.utc(2026, 7, 17, 12));
|
|
final session = ActiveWorkoutSession(
|
|
metadata: _metadata('session-1'),
|
|
status: ActiveWorkoutStatus.running,
|
|
startedAt: clock.now(),
|
|
lastPersistedAt: clock.now(),
|
|
elapsedActiveMs: 0,
|
|
currentProgramIndex: 0,
|
|
currentExerciseIndex: 0,
|
|
currentSetIndex: 0,
|
|
resolvedTemplateSnapshotJson: '{}',
|
|
);
|
|
|
|
clock.value = clock.value.add(const Duration(seconds: 30));
|
|
final paused = session.pause(clock.now());
|
|
expect(paused.elapsedActiveMs, 30000);
|
|
|
|
clock.value = clock.value.add(const Duration(minutes: 5));
|
|
final resumed = paused.resume(clock.now());
|
|
expect(resumed.elapsedActiveMillisecondsAt(clock.now()), 30000);
|
|
|
|
clock.value = clock.value.add(const Duration(seconds: 15));
|
|
expect(resumed.elapsedActiveMillisecondsAt(clock.now()), 45000);
|
|
});
|
|
}
|
|
|
|
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 {
|
|
_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 _FakeWorkoutTemplateRepository
|
|
implements WorkoutTemplateRepository {
|
|
@override
|
|
Future<WorkoutTemplate?> findById(String id) async => null;
|
|
|
|
@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 {}
|
|
}
|