import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:motorcycle_dash/features/entitlements/widgets/pro_lock_badge.dart'; import 'package:motorcycle_dash/features/rides/ride_state_machine.dart'; import 'package:motorcycle_dash/services/entitlements/feature_access_service.dart'; /// Regression for Issue #52 / BUG-005 follow-up: the "Forensic CSV & PDF Export" Pro /// upsell sheet showed "BOTTOM OVERFLOWED BY 90 PIXELS" in landscape on a Pixel 8a /// (~914x411 dp) because showProUpgradeSheet built a non-scrolling Column inside a default /// modal bottom sheet, which Flutter caps at 9/16 of the screen height. class _FakeRideStateMachine extends StateNotifier implements RideStateMachine { _FakeRideStateMachine(super.initialState); @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } Future _pumpHost( WidgetTester tester, { required Size viewport, double textScale = 1.0, }) async { await tester.binding.setSurfaceSize(viewport); addTearDown(() => tester.binding.setSurfaceSize(null)); await tester.pumpWidget( ProviderScope( overrides: [ rideStateMachineProvider.overrideWith( (ref) => _FakeRideStateMachine(RideSession(state: RideState.idle)), ), ], child: MediaQuery( data: MediaQueryData( size: viewport, textScaler: TextScaler.linear(textScale), ), child: MaterialApp( home: Scaffold( body: Consumer( builder: (context, ref, _) => ElevatedButton( onPressed: () => showProUpgradeSheet( context, ref, feature: MotorSignalFeature.csvExport, ), child: const Text('Open upsell'), ), ), ), ), ), ), ); } void main() { TestWidgetsFlutterBinding.ensureInitialized(); for (final viewport in const [ Size(914, 411), // Pixel 8a landscape Size(800, 360), Size(780, 360), ]) { for (final scale in const [1.0, 1.3]) { testWidgets( 'Pro upgrade sheet has no bottom overflow in landscape ' '${viewport.width.toInt()}x${viewport.height.toInt()} (text scale $scale)', (tester) async { await _pumpHost(tester, viewport: viewport, textScale: scale); await tester.tap(find.text('Open upsell')); await tester.pumpAndSettle(); expect(find.text('Forensic CSV & PDF Export'), findsOneWidget); expect(find.text('VIEW MOTORSIGNAL PRO'), findsOneWidget); expect(find.text('NOT NOW'), findsOneWidget); // No RenderFlex overflow exception. expect(tester.takeException(), isNull); }, ); } } testWidgets('NOT NOW is reachable and dismisses the sheet in landscape', ( tester, ) async { await _pumpHost(tester, viewport: const Size(800, 360), textScale: 1.3); await tester.tap(find.text('Open upsell')); await tester.pumpAndSettle(); await tester.ensureVisible(find.text('NOT NOW')); await tester.tap(find.text('NOT NOW')); await tester.pumpAndSettle(); expect(find.text('VIEW MOTORSIGNAL PRO'), findsNothing); expect(tester.takeException(), isNull); }); }