import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:motorcycle_dash/features/entitlements/pro_screen.dart'; import 'package:motorcycle_dash/features/settings/widgets/current_plan_card.dart'; import 'package:motorcycle_dash/services/billing/billing_providers.dart'; import 'package:motorcycle_dash/services/billing/fake_billing_service.dart'; import 'package:motorcycle_dash/services/billing/product_catalog.dart'; import 'package:motorcycle_dash/services/entitlements/entitlement_providers.dart'; import 'package:motorcycle_dash/services/entitlements/entitlement_state.dart'; import 'package:motorcycle_dash/services/entitlements/entitlement_storage.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); Widget buildTestableWidget({ required Widget child, List overrides = const [], }) { return ProviderScope( overrides: overrides, child: MaterialApp(home: child), ); } group('ProScreen UI and Flow Tests', () { testWidgets( 'renders dynamic products with prices from FakeBillingService', (tester) async { final fakeBilling = FakeBillingService(); await tester.pumpWidget( buildTestableWidget( child: const ProScreen(), overrides: [ billingServiceProvider.overrideWithValue(fakeBilling), entitlementStorageProvider.overrideWithValue( InMemoryEntitlementStorage(), ), ], ), ); // Initial loading pump await tester.pump(); await tester.pump(const Duration(milliseconds: 100)); // Check header and titles expect(find.text('MOTORSIGNAL'), findsOneWidget); expect( find.text('PRECISION TELEMETRY.\nUNLIMITED RIDE DATA.'), findsOneWidget, ); // Check dynamic product prices and plan card titles expect(find.text('\$4.99/month'), findsOneWidget); expect(find.text('\$49.99'), findsOneWidget); expect(find.text('PRO MONTHLY'), findsOneWidget); expect(find.text('PRO LIFETIME'), findsOneWidget); // Check restore purchases button exists expect(find.text('RESTORE'), findsOneWidget); // Scroll to benefits list final benefitsHeader = find.text('WHAT IS INCLUDED IN PRO'); await tester.scrollUntilVisible(benefitsHeader, 200); expect(benefitsHeader, findsOneWidget); final garageBenefit = find.text('Multiple Motorcycle Garage'); await tester.scrollUntilVisible(garageBenefit, 200); expect(garageBenefit, findsOneWidget); }, ); testWidgets('triggers purchase flow on monthly plan card selection', ( tester, ) async { final fakeBilling = FakeBillingService(); await tester.pumpWidget( buildTestableWidget( child: const ProScreen(), overrides: [ billingServiceProvider.overrideWithValue(fakeBilling), entitlementStorageProvider.overrideWithValue( InMemoryEntitlementStorage(), ), ], ), ); await tester.pumpAndSettle(); // Tap on Pro Monthly card final monthlyCard = find.text('PRO MONTHLY'); expect(monthlyCard, findsOneWidget); await tester.tap(monthlyCard); await tester.pumpAndSettle(); expect(fakeBilling.launchCount, 1); expect(fakeBilling.lastPurchasedProductId, ProductCatalog.proMonthly); }); testWidgets('triggers purchase flow on lifetime plan card selection', ( tester, ) async { final fakeBilling = FakeBillingService(); await tester.pumpWidget( buildTestableWidget( child: const ProScreen(), overrides: [ billingServiceProvider.overrideWithValue(fakeBilling), entitlementStorageProvider.overrideWithValue( InMemoryEntitlementStorage(), ), ], ), ); await tester.pumpAndSettle(); // Tap on Pro Lifetime card final lifetimeCard = find.text('PRO LIFETIME'); expect(lifetimeCard, findsOneWidget); await tester.tap(lifetimeCard); await tester.pumpAndSettle(); expect(fakeBilling.launchCount, 1); expect(fakeBilling.lastPurchasedProductId, ProductCatalog.proLifetime); }); testWidgets('handles empty product catalog with retry option', ( tester, ) async { final fakeBilling = FakeBillingService()..available = false; await tester.pumpWidget( buildTestableWidget( child: const ProScreen(), overrides: [ billingServiceProvider.overrideWithValue(fakeBilling), entitlementStorageProvider.overrideWithValue( InMemoryEntitlementStorage(), ), ], ), ); await tester.pumpAndSettle(); expect(find.text('Google Play Billing Unavailable'), findsOneWidget); expect(find.text('RETRY CONNECTION'), findsOneWidget); }); }); group('CurrentPlanCard Component Tests', () { testWidgets('displays Free plan with Upgrade button', (tester) async { final freeState = EntitlementState.free(); await tester.pumpWidget( buildTestableWidget( child: const Scaffold(body: CurrentPlanCard()), overrides: [entitlementProvider.overrideWithValue(freeState)], ), ); await tester.pumpAndSettle(); expect(find.text('MotorSignal Free'), findsOneWidget); expect(find.text('FREE'), findsOneWidget); expect(find.text('Upgrade to Pro'), findsOneWidget); expect( find.text('Basic telemetry & local recording active'), findsOneWidget, ); }); testWidgets('displays Active Pro Monthly with Manage Subscriptions link', ( tester, ) async { final proState = EntitlementState.proMonthlyActive( productId: ProductCatalog.proMonthly, expiresAt: DateTime.now().add(const Duration(days: 20)), ); await tester.pumpWidget( buildTestableWidget( child: const Scaffold(body: CurrentPlanCard()), overrides: [entitlementProvider.overrideWithValue(proState)], ), ); await tester.pumpAndSettle(); expect(find.text('MotorSignal Pro'), findsOneWidget); expect(find.text('Manage Subscription'), findsOneWidget); expect(find.text('MONTHLY'), findsOneWidget); }); testWidgets( 'displays Lifetime with Lifetime License label and no renewal', (tester) async { final lifetimeState = EntitlementState.proLifetime( productId: ProductCatalog.proLifetime, ); await tester.pumpWidget( buildTestableWidget( child: const Scaffold(body: CurrentPlanCard()), overrides: [entitlementProvider.overrideWithValue(lifetimeState)], ), ); await tester.pumpAndSettle(); expect(find.text('MotorSignal Pro'), findsOneWidget); expect( find.text('Lifetime access • Permanent ownership'), findsOneWidget, ); expect(find.text('LIFETIME'), findsOneWidget); }, ); testWidgets('displays Grace Period with warning badge', (tester) async { final graceState = EntitlementState.normalized( plan: MotorSignalPlan.proMonthly, status: MotorSignalEntitlementStatus.proMonthlyGrace, verificationStatus: EntitlementVerificationStatus.verified, source: PurchaseSource.googlePlay, productId: ProductCatalog.proMonthly, expiresAt: DateTime.now().add(const Duration(days: 2)), ); await tester.pumpWidget( buildTestableWidget( child: const Scaffold(body: CurrentPlanCard()), overrides: [entitlementProvider.overrideWithValue(graceState)], ), ); await tester.pumpAndSettle(); expect(find.text('GRACE PERIOD'), findsOneWidget); expect( find.text('Payment grace period • Action needed in Google Play'), findsOneWidget, ); expect(find.text('Manage Subscription'), findsOneWidget); }); testWidgets('renders on narrow viewport without overflow', (tester) async { tester.view.physicalSize = const Size(320, 640); tester.view.devicePixelRatio = 1.0; addTearDown(() => tester.view.resetPhysicalSize()); final fakeBilling = FakeBillingService(); await tester.pumpWidget( buildTestableWidget( child: const ProScreen(), overrides: [ billingServiceProvider.overrideWithValue(fakeBilling), entitlementStorageProvider.overrideWithValue( InMemoryEntitlementStorage(), ), ], ), ); await tester.pump(); await tester.pump(const Duration(milliseconds: 100)); expect(tester.takeException(), isNull); expect(find.text('PRO LIFETIME'), findsOneWidget); expect(find.text('BEST VALUE'), findsOneWidget); }); }); }