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 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 attemptSilentSignIn() async => true; @override Future signIn() async => accountLabel!; @override Future signOut() async {} @override CloudStorageSession beginSession() => throw UnimplementedError(); }