MO-Fuel-Tax-Back/test/backup_reminder_test.dart

229 lines
7.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:fuel_tax_tracker/screens/data_settings_screen.dart';
import 'package:fuel_tax_tracker/screens/faq_screen.dart';
import 'package:fuel_tax_tracker/services/app_state.dart';
import 'package:fuel_tax_tracker/services/cloud/cloud_storage_provider.dart';
import 'package:fuel_tax_tracker/services/cloud_sync_service.dart';
import 'package:fuel_tax_tracker/services/database_service.dart';
import 'package:fuel_tax_tracker/widgets/backup_reminder.dart';
/// Covers the "your data isn't backed up" nudge shown after saving a fuel
/// entry with no cloud backup configured — see
/// `ConfirmFuelEntryScreen._save`. The dialog and the spotlight it can lead
/// to (`showBackupReminderDialog`/`openCloudBackupSetup`) are tested here
/// as standalone functions rather than through a full save, matching
/// settings_purge_test.dart's convention of not exercising actions that
/// need an initialized database.
void main() {
setUp(() {
SharedPreferences.setMockInitialValues({});
});
group('showBackupReminderDialog', () {
Future<bool?> openDialog(WidgetTester tester) async {
bool? result;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (context) => ElevatedButton(
onPressed: () async => result = await showBackupReminderDialog(context),
child: const Text('open'),
),
),
),
);
await tester.tap(find.text('open'));
await tester.pumpAndSettle();
return result;
}
testWidgets('shows the warning with both actions', (tester) async {
await openDialog(tester);
expect(find.text("Your Data Isn't Backed Up"), findsOneWidget);
expect(find.text('Learn More'), findsOneWidget);
expect(find.text('Not Now'), findsOneWidget);
expect(find.text('Set Up Backup'), findsOneWidget);
});
testWidgets('Learn More resolves false, closes the dialog, and opens the FAQ to the '
'backup question', (tester) async {
bool? result;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (context) => ElevatedButton(
onPressed: () async => result = await showBackupReminderDialog(context),
child: const Text('open'),
),
),
),
);
await tester.tap(find.text('open'));
await tester.pumpAndSettle();
await tester.tap(find.text('Learn More'));
await tester.pumpAndSettle();
expect(result, isFalse);
expect(find.text("Your Data Isn't Backed Up"), findsNothing, reason: 'dialog closed');
expect(find.byType(FaqScreen), findsOneWidget);
await tester.scrollUntilVisible(find.text('Is my data backed up?'), 300,
scrollable: find.byType(Scrollable));
expect(find.textContaining('Not by default'), findsOneWidget,
reason: 'backup question starts expanded');
});
testWidgets('Not Now resolves false and closes the dialog', (tester) async {
bool? result;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (context) => ElevatedButton(
onPressed: () async => result = await showBackupReminderDialog(context),
child: const Text('open'),
),
),
),
);
await tester.tap(find.text('open'));
await tester.pumpAndSettle();
await tester.tap(find.text('Not Now'));
await tester.pumpAndSettle();
expect(result, isFalse);
expect(find.text("Your Data Isn't Backed Up"), findsNothing);
});
testWidgets('Set Up Backup resolves true and closes the dialog', (tester) async {
bool? result;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (context) => ElevatedButton(
onPressed: () async => result = await showBackupReminderDialog(context),
child: const Text('open'),
),
),
),
);
await tester.tap(find.text('open'));
await tester.pumpAndSettle();
await tester.tap(find.text('Set Up Backup'));
await tester.pumpAndSettle();
expect(result, isTrue);
expect(find.text("Your Data Isn't Backed Up"), findsNothing);
});
});
group('openCloudBackupSetup', () {
testWidgets('pushes Settings > Data with the cloud storage card spotlighted',
(tester) async {
final appState = AppState()..isLoading = false;
await tester.pumpWidget(
ChangeNotifierProvider.value(
value: appState,
child: MaterialApp(
home: Builder(
builder: (context) => ElevatedButton(
onPressed: () => openCloudBackupSetup(context),
child: const Text('open'),
),
),
),
),
);
await tester.tap(find.text('open'));
await tester.pumpAndSettle();
expect(find.byType(DataSettingsScreen), findsOneWidget);
expect(find.text('Back Up to the Cloud'), findsOneWidget);
expect(find.widgetWithText(FilledButton, 'Got It'), findsOneWidget);
});
testWidgets('dismissing the spotlight leaves the Data screen up', (tester) async {
final appState = AppState()..isLoading = false;
await tester.pumpWidget(
ChangeNotifierProvider.value(
value: appState,
child: MaterialApp(
home: Builder(
builder: (context) => ElevatedButton(
onPressed: () => openCloudBackupSetup(context),
child: const Text('open'),
),
),
),
),
);
await tester.tap(find.text('open'));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(FilledButton, 'Got It'));
await tester.pumpAndSettle();
expect(find.text('Back Up to the Cloud'), findsNothing, reason: 'spotlight dismissed');
expect(find.byType(DataSettingsScreen), findsOneWidget, reason: 'still on the Data screen');
});
});
group('AppState.hasCloudBackupConfigured', () {
test('false with no cloud provider connected at all', () {
final appState = AppState();
expect(appState.hasCloudBackupConfigured, isFalse);
});
test('false when a provider is connected but no folder has been chosen', () {
final appState = AppState()
..cloudSync = CloudSyncService(provider: _FakeProvider(), databaseService: DatabaseService());
expect(appState.hasCloudBackupConfigured, isFalse);
});
test('true once a backup folder has been configured', () {
final appState = AppState()
..cloudSync =
(CloudSyncService(provider: _FakeProvider(), databaseService: DatabaseService())
..configure('folder-id'));
expect(appState.hasCloudBackupConfigured, isTrue);
});
});
}
class _FakeProvider implements CloudStorageProvider {
@override
CloudProviderId get id => CloudProviderId.googleDrive;
@override
String get displayName => 'Fake Provider';
@override
bool get isSignedIn => true;
@override
String? get accountLabel => 'tester@example.com';
@override
Future<bool> attemptSilentSignIn() async => true;
@override
Future<String> signIn() async => accountLabel!;
@override
Future<void> signOut() async {}
@override
CloudStorageSession beginSession() => throw UnimplementedError();
}