test: cover local backup export/import panel in Profil
Covers F1/F2 (#84): panel visibility in both profile states, export success/failure snackbars, merge success, replace-all double confirmation (including cancel), invalid file dialog, active-session block, and silent cancellation of the file picker.
This commit is contained in:
@ -1,8 +1,12 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:gametime/application/application.dart';
|
import 'package:gametime/application/application.dart';
|
||||||
import 'package:gametime/domain/domain.dart';
|
import 'package:gametime/domain/domain.dart';
|
||||||
import 'package:gametime/presentation/profile_screen.dart';
|
import 'package:gametime/presentation/profile_screen.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('le profil déconnecté invite sans bloquer le reste', (
|
testWidgets('le profil déconnecté invite sans bloquer le reste', (
|
||||||
@ -16,6 +20,8 @@ void main() {
|
|||||||
authUseCases: harness.useCases,
|
authUseCases: harness.useCases,
|
||||||
syncUseCases: harness.syncUseCases,
|
syncUseCases: harness.syncUseCases,
|
||||||
shareUseCases: harness.shareUseCases,
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -39,6 +45,8 @@ void main() {
|
|||||||
authUseCases: harness.useCases,
|
authUseCases: harness.useCases,
|
||||||
syncUseCases: harness.syncUseCases,
|
syncUseCases: harness.syncUseCases,
|
||||||
shareUseCases: harness.shareUseCases,
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -72,6 +80,8 @@ void main() {
|
|||||||
authUseCases: harness.useCases,
|
authUseCases: harness.useCases,
|
||||||
syncUseCases: harness.syncUseCases,
|
syncUseCases: harness.syncUseCases,
|
||||||
shareUseCases: harness.shareUseCases,
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -116,6 +126,8 @@ void main() {
|
|||||||
authUseCases: harness.useCases,
|
authUseCases: harness.useCases,
|
||||||
syncUseCases: harness.syncUseCases,
|
syncUseCases: harness.syncUseCases,
|
||||||
shareUseCases: harness.shareUseCases,
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -144,6 +156,8 @@ void main() {
|
|||||||
authUseCases: harness.useCases,
|
authUseCases: harness.useCases,
|
||||||
syncUseCases: harness.syncUseCases,
|
syncUseCases: harness.syncUseCases,
|
||||||
shareUseCases: harness.shareUseCases,
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -171,6 +185,8 @@ void main() {
|
|||||||
authUseCases: harness.useCases,
|
authUseCases: harness.useCases,
|
||||||
syncUseCases: harness.syncUseCases,
|
syncUseCases: harness.syncUseCases,
|
||||||
shareUseCases: harness.shareUseCases,
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -198,6 +214,8 @@ void main() {
|
|||||||
authUseCases: harness.useCases,
|
authUseCases: harness.useCases,
|
||||||
syncUseCases: harness.syncUseCases,
|
syncUseCases: harness.syncUseCases,
|
||||||
shareUseCases: harness.shareUseCases,
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -222,6 +240,8 @@ void main() {
|
|||||||
authUseCases: harness.useCases,
|
authUseCases: harness.useCases,
|
||||||
syncUseCases: harness.syncUseCases,
|
syncUseCases: harness.syncUseCases,
|
||||||
shareUseCases: harness.shareUseCases,
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -232,6 +252,341 @@ void main() {
|
|||||||
|
|
||||||
expect(find.text('Aucun partage reçu'), findsOneWidget);
|
expect(find.text('Aucun partage reçu'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'la section Sauvegarde locale est visible déconnecté et connecté',
|
||||||
|
(tester) async {
|
||||||
|
final harness = _AuthHarness();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProfileScreen(
|
||||||
|
authUseCases: harness.useCases,
|
||||||
|
syncUseCases: harness.syncUseCases,
|
||||||
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Sauvegarde locale'), findsOneWidget);
|
||||||
|
expect(find.text('Exporter mes données'), findsOneWidget);
|
||||||
|
expect(find.text('Importer des données'), findsOneWidget);
|
||||||
|
|
||||||
|
final connectedHarness = _AuthHarness.connected();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProfileScreen(
|
||||||
|
authUseCases: connectedHarness.useCases,
|
||||||
|
syncUseCases: connectedHarness.syncUseCases,
|
||||||
|
shareUseCases: connectedHarness.shareUseCases,
|
||||||
|
dataExportUseCase: connectedHarness.dataExportUseCase,
|
||||||
|
dataImportUseCase: connectedHarness.dataImportUseCase,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Sauvegarde locale'), findsOneWidget);
|
||||||
|
expect(find.text('Exporter mes données'), findsOneWidget);
|
||||||
|
expect(find.text('Importer des données'), findsOneWidget);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'export réussi affiche le message de sauvegarde exportée',
|
||||||
|
(tester) async {
|
||||||
|
final harness = _AuthHarness();
|
||||||
|
final exporter = _FakeBackupFileExporter(
|
||||||
|
status: ShareResultStatus.success,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProfileScreen(
|
||||||
|
authUseCases: harness.useCases,
|
||||||
|
syncUseCases: harness.syncUseCases,
|
||||||
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
|
backupFileExporter: exporter,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Exporter mes données'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('Sauvegarde exportée.'), findsOneWidget);
|
||||||
|
expect(exporter.exportedFileName, isNotNull);
|
||||||
|
expect(exporter.exportedFileName, endsWith('.gametime'));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testWidgets('un échec d’export affiche un message neutre', (tester) async {
|
||||||
|
final harness = _AuthHarness();
|
||||||
|
final exporter = _FakeBackupFileExporter()..throwOnExport = true;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProfileScreen(
|
||||||
|
authUseCases: harness.useCases,
|
||||||
|
syncUseCases: harness.syncUseCases,
|
||||||
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
|
backupFileExporter: exporter,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Exporter mes données'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('Export impossible pour le moment.'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('import fusion réussie affiche les données importées', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final harness = _AuthHarness()..backupRepository.hasData = true;
|
||||||
|
final bytes = const LocalBackupCodec().encode(
|
||||||
|
LocalDataExportSnapshot(
|
||||||
|
exportedAt: DateTime.utc(2026, 7, 22, 10, 15),
|
||||||
|
appSchemaVersion: 19,
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
mediaAssets: const [],
|
||||||
|
exercises: const [],
|
||||||
|
programs: const [],
|
||||||
|
workoutTemplates: const [],
|
||||||
|
workoutHistories: const [],
|
||||||
|
mediaFiles: const [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final picker = _FakeBackupFilePicker(
|
||||||
|
file: LocalBackupPickedFile(
|
||||||
|
fileName: 'gametime-sauvegarde-2026-07-22.gametime',
|
||||||
|
bytes: bytes,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProfileScreen(
|
||||||
|
authUseCases: harness.useCases,
|
||||||
|
syncUseCases: harness.syncUseCases,
|
||||||
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
|
backupFilePicker: picker,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Importer des données'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('Importer cette sauvegarde ?'), findsOneWidget);
|
||||||
|
expect(find.text('Fusionner'), findsOneWidget);
|
||||||
|
expect(find.text('Remplacer tout'), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Fusionner'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('Données importées.'), findsOneWidget);
|
||||||
|
expect(harness.backupRepository.appliedModes, [
|
||||||
|
LocalBackupImportMode.merge,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'remplacer tout exige une seconde confirmation destructive',
|
||||||
|
(tester) async {
|
||||||
|
final harness = _AuthHarness()..backupRepository.hasData = true;
|
||||||
|
final bytes = const LocalBackupCodec().encode(
|
||||||
|
LocalDataExportSnapshot(
|
||||||
|
exportedAt: DateTime.utc(2026, 7, 22, 10, 15),
|
||||||
|
appSchemaVersion: 19,
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
mediaAssets: const [],
|
||||||
|
exercises: const [],
|
||||||
|
programs: const [],
|
||||||
|
workoutTemplates: const [],
|
||||||
|
workoutHistories: const [],
|
||||||
|
mediaFiles: const [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final picker = _FakeBackupFilePicker(
|
||||||
|
file: LocalBackupPickedFile(
|
||||||
|
fileName: 'gametime-sauvegarde-2026-07-22.gametime',
|
||||||
|
bytes: bytes,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProfileScreen(
|
||||||
|
authUseCases: harness.useCases,
|
||||||
|
syncUseCases: harness.syncUseCases,
|
||||||
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
|
backupFilePicker: picker,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Importer des données'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Remplacer tout'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.text('Remplacer toutes les données locales ?'),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
expect(harness.backupRepository.appliedModes, isEmpty);
|
||||||
|
|
||||||
|
await tester.tap(find.widgetWithText(TextButton, 'Annuler'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(harness.backupRepository.appliedModes, isEmpty);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Importer des données'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.text('Remplacer tout'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.widgetWithText(FilledButton, 'Remplacer tout'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('Sauvegarde restaurée.'), findsOneWidget);
|
||||||
|
expect(harness.backupRepository.appliedModes, [
|
||||||
|
LocalBackupImportMode.replaceAll,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testWidgets('un fichier invalide affiche le message dédié', (tester) async {
|
||||||
|
final harness = _AuthHarness();
|
||||||
|
final picker = _FakeBackupFilePicker(
|
||||||
|
file: LocalBackupPickedFile(
|
||||||
|
fileName: 'notes.txt',
|
||||||
|
bytes: Uint8List.fromList(utf8.encode('{"kind":"something.else"}')),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProfileScreen(
|
||||||
|
authUseCases: harness.useCases,
|
||||||
|
syncUseCases: harness.syncUseCases,
|
||||||
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
|
backupFilePicker: picker,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Importer des données'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('Fichier invalide'), findsOneWidget);
|
||||||
|
expect(
|
||||||
|
find.text('Ce fichier n’est pas une sauvegarde GameTime valide.'),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('une séance active bloque l’import', (tester) async {
|
||||||
|
final harness = _AuthHarness()
|
||||||
|
..backupRepository.hasOpenSession = true
|
||||||
|
..backupRepository.hasData = true;
|
||||||
|
final bytes = const LocalBackupCodec().encode(
|
||||||
|
LocalDataExportSnapshot(
|
||||||
|
exportedAt: DateTime.utc(2026, 7, 22, 10, 15),
|
||||||
|
appSchemaVersion: 19,
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
mediaAssets: const [],
|
||||||
|
exercises: const [],
|
||||||
|
programs: const [],
|
||||||
|
workoutTemplates: const [],
|
||||||
|
workoutHistories: const [],
|
||||||
|
mediaFiles: const [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final picker = _FakeBackupFilePicker(
|
||||||
|
file: LocalBackupPickedFile(
|
||||||
|
fileName: 'gametime-sauvegarde-2026-07-22.gametime',
|
||||||
|
bytes: bytes,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProfileScreen(
|
||||||
|
authUseCases: harness.useCases,
|
||||||
|
syncUseCases: harness.syncUseCases,
|
||||||
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
|
backupFilePicker: picker,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Importer des données'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('Importer cette sauvegarde ?'), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Fusionner'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('Séance en cours'), findsOneWidget);
|
||||||
|
expect(
|
||||||
|
find.text(
|
||||||
|
'Termine ou sauvegarde ta séance avant d’importer des données.',
|
||||||
|
),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('annuler la sélection de fichier reste silencieux', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final harness = _AuthHarness();
|
||||||
|
final picker = _FakeBackupFilePicker();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ProfileScreen(
|
||||||
|
authUseCases: harness.useCases,
|
||||||
|
syncUseCases: harness.syncUseCases,
|
||||||
|
shareUseCases: harness.shareUseCases,
|
||||||
|
dataExportUseCase: harness.dataExportUseCase,
|
||||||
|
dataImportUseCase: harness.dataImportUseCase,
|
||||||
|
backupFilePicker: picker,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Importer des données'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byType(AlertDialog), findsNothing);
|
||||||
|
expect(find.byType(SnackBar), findsNothing);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final class _AuthHarness {
|
final class _AuthHarness {
|
||||||
@ -247,7 +602,9 @@ final class _AuthHarness {
|
|||||||
shareInbox = _FakeShareInboxRepository(),
|
shareInbox = _FakeShareInboxRepository(),
|
||||||
pendingShares = _FakePendingShareActionRepository(),
|
pendingShares = _FakePendingShareActionRepository(),
|
||||||
programRepository = _FakeProgramRepository(),
|
programRepository = _FakeProgramRepository(),
|
||||||
templateRepository = _FakeWorkoutTemplateRepository() {
|
templateRepository = _FakeWorkoutTemplateRepository(),
|
||||||
|
backupRepository = _FakeLocalDataBackupRepository(),
|
||||||
|
backupMediaStore = _FakeLocalBackupMediaStore() {
|
||||||
useCases = AuthUseCases(
|
useCases = AuthUseCases(
|
||||||
tokenStore: tokenStore,
|
tokenStore: tokenStore,
|
||||||
accountRepository: accountRepository,
|
accountRepository: accountRepository,
|
||||||
@ -276,6 +633,16 @@ final class _AuthHarness {
|
|||||||
ids: _FakeIds(),
|
ids: _FakeIds(),
|
||||||
originDeviceId: 'device-1',
|
originDeviceId: 'device-1',
|
||||||
);
|
);
|
||||||
|
dataExportUseCase = DataExportUseCase(
|
||||||
|
repository: backupRepository,
|
||||||
|
mediaStore: backupMediaStore,
|
||||||
|
clock: const _FakeClock(),
|
||||||
|
);
|
||||||
|
dataImportUseCase = DataImportUseCase(
|
||||||
|
repository: backupRepository,
|
||||||
|
mediaStore: backupMediaStore,
|
||||||
|
clock: const _FakeClock(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory _AuthHarness.connected() {
|
factory _AuthHarness.connected() {
|
||||||
@ -305,9 +672,13 @@ final class _AuthHarness {
|
|||||||
final _FakePendingShareActionRepository pendingShares;
|
final _FakePendingShareActionRepository pendingShares;
|
||||||
final _FakeProgramRepository programRepository;
|
final _FakeProgramRepository programRepository;
|
||||||
final _FakeWorkoutTemplateRepository templateRepository;
|
final _FakeWorkoutTemplateRepository templateRepository;
|
||||||
|
final _FakeLocalDataBackupRepository backupRepository;
|
||||||
|
final _FakeLocalBackupMediaStore backupMediaStore;
|
||||||
late final AuthUseCases useCases;
|
late final AuthUseCases useCases;
|
||||||
late final SyncUseCases syncUseCases;
|
late final SyncUseCases syncUseCases;
|
||||||
late final ShareUseCases shareUseCases;
|
late final ShareUseCases shareUseCases;
|
||||||
|
late final DataExportUseCase dataExportUseCase;
|
||||||
|
late final DataImportUseCase dataImportUseCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class _FakeClock implements Clock {
|
final class _FakeClock implements Clock {
|
||||||
@ -621,3 +992,96 @@ final class _FakeWorkoutTemplateRepository
|
|||||||
@override
|
@override
|
||||||
Future<void> saveProgram(WorkoutTemplateProgram program) async {}
|
Future<void> saveProgram(WorkoutTemplateProgram program) async {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class _FakeLocalDataBackupRepository
|
||||||
|
implements LocalDataBackupRepository {
|
||||||
|
var hasOpenSession = false;
|
||||||
|
var hasData = false;
|
||||||
|
var throwOnApply = false;
|
||||||
|
final appliedModes = <LocalBackupImportMode>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<LocalBackupImportResult> applyImportSnapshot({
|
||||||
|
required LocalDataExportSnapshot snapshot,
|
||||||
|
required LocalBackupImportMode mode,
|
||||||
|
required DateTime importedAt,
|
||||||
|
}) async {
|
||||||
|
if (throwOnApply) {
|
||||||
|
throw Exception('transaction failed');
|
||||||
|
}
|
||||||
|
appliedModes.add(mode);
|
||||||
|
return const LocalBackupImportResult(
|
||||||
|
insertedCount: 1,
|
||||||
|
updatedCount: 0,
|
||||||
|
ignoredOlderCount: 0,
|
||||||
|
deletedByReplaceCount: 0,
|
||||||
|
missingMediaCount: 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> hasAnyUserData() async => hasData;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> hasOpenActiveWorkoutSession() async => hasOpenSession;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<LocalDataExportSnapshot> readExportSnapshot(
|
||||||
|
DateTime exportedAt,
|
||||||
|
) async {
|
||||||
|
return LocalDataExportSnapshot(
|
||||||
|
exportedAt: exportedAt,
|
||||||
|
appSchemaVersion: 19,
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
mediaAssets: const [],
|
||||||
|
exercises: const [],
|
||||||
|
programs: const [],
|
||||||
|
workoutTemplates: const [],
|
||||||
|
workoutHistories: const [],
|
||||||
|
mediaFiles: const [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeLocalBackupMediaStore implements LocalBackupMediaStore {
|
||||||
|
@override
|
||||||
|
Future<List<EmbeddedBackupMediaFile>> readEmbeddableFiles(
|
||||||
|
List<LocalBackupResource> mediaAssets,
|
||||||
|
) async => const [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, RestoredMediaFile>> restoreEmbeddedFiles(
|
||||||
|
List<EmbeddedBackupMediaFile> files,
|
||||||
|
) async => const {};
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeBackupFileExporter implements LocalBackupFileExporter {
|
||||||
|
_FakeBackupFileExporter({this.status = ShareResultStatus.success});
|
||||||
|
|
||||||
|
ShareResultStatus status;
|
||||||
|
String? exportedFileName;
|
||||||
|
Uint8List? exportedBytes;
|
||||||
|
var throwOnExport = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ShareResultStatus> exportFile({
|
||||||
|
required String fileName,
|
||||||
|
required Uint8List bytes,
|
||||||
|
}) async {
|
||||||
|
if (throwOnExport) {
|
||||||
|
throw Exception('share failed');
|
||||||
|
}
|
||||||
|
exportedFileName = fileName;
|
||||||
|
exportedBytes = bytes;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeBackupFilePicker implements LocalBackupFilePicker {
|
||||||
|
_FakeBackupFilePicker({this.file});
|
||||||
|
|
||||||
|
LocalBackupPickedFile? file;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<LocalBackupPickedFile?> pickFile() async => file;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user