import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.dart'; import 'package:fuel_tax_tracker/services/app_state.dart'; import 'package:fuel_tax_tracker/widgets/ad_free_upsell_dialog.dart'; /// Covers the "remove ads for a year" upsell shown right after a user /// watches the app's one ad placement — see `AppState.syncNow`'s /// `onAdWatched` hook, wired up in main.dart. Tested here as a standalone /// dialog function, matching backup_reminder_test.dart's convention. void main() { Future openDialog(WidgetTester tester, AppState appState) async { await tester.pumpWidget( ChangeNotifierProvider.value( value: appState, child: MaterialApp( home: Builder( builder: (context) => ElevatedButton( onPressed: () => showAdFreeUpsellDialog(context), child: const Text('open'), ), ), ), ), ); await tester.tap(find.text('open')); await tester.pumpAndSettle(); } testWidgets('shows the pitch with both actions', (tester) async { await openDialog(tester, AppState()); expect(find.text('Tired of Ads?'), findsOneWidget); expect(find.text('Not Now'), findsOneWidget); expect(find.text('Remove Ads'), findsOneWidget); }); testWidgets('falls back to generic copy when no price is loaded yet', (tester) async { await openDialog(tester, AppState()); expect(find.textContaining('a single one-time purchase'), findsOneWidget); expect(find.textContaining('\$'), findsNothing); }); testWidgets('Not Now closes the dialog without navigating anywhere', (tester) async { await openDialog(tester, AppState()); await tester.tap(find.text('Not Now')); await tester.pumpAndSettle(); expect(find.text('Tired of Ads?'), findsNothing); }); testWidgets('Remove Ads closes the dialog and starts the purchase flow', (tester) async { final appState = AppState(); await openDialog(tester, appState); await tester.tap(find.text('Remove Ads')); await tester.pumpAndSettle(); expect(find.text('Tired of Ads?'), findsNothing, reason: 'dialog closed'); // buyAdFreeYear() delegates to PurchaseService, which — with no real // store connection in tests — surfaces its failure through // AppState.purchaseError rather than throwing, so tapping through is // enough to confirm the button actually invoked it without crashing. expect(appState.purchaseError, isNotNull); }); }