import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:geolocator/geolocator.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:motorcycle_dash/core/theme/theme_extensions.dart'; import 'package:motorcycle_dash/features/map/map_screen.dart'; import 'package:motorcycle_dash/services/location_service.dart'; import 'package:motorcycle_dash/services/map/map_provider_interface.dart'; import 'package:motorcycle_dash/services/routes/route_models.dart'; import 'package:motorcycle_dash/services/routes/route_provider.dart'; import 'package:motorcycle_dash/services/routes/route_providers.dart'; class _FakeRouteProvider implements RouteProvider { @override String get name => 'Fake AWS Provider'; @override Future> searchPlaces( String query, { AppLatLng? nearLocation, }) async { if (query.toLowerCase().contains('blood mountain')) { return const [ PlaceSearchResult( providerId: 'place_blood_mtn', primaryText: 'Blood Mountain Gap', secondaryText: 'US-19 / GA-11, Blairsville, GA', latitude: 34.7330, longitude: -83.9180, formattedAddress: 'Blood Mountain Gap, GA', ), ]; } return [ PlaceSearchResult( providerId: 'place_gas', primaryText: 'Shell Gas Station', secondaryText: 'Main St', latitude: 34.7200, longitude: -83.9100, formattedAddress: 'Shell Gas Station, Main St', ), ]; } @override Future resolvePlace(String placeId) async { return const ResolvedPlace( providerId: 'place_blood_mtn', displayName: 'Blood Mountain Gap', formattedAddress: 'Blood Mountain Gap, GA', latitude: 34.7330, longitude: -83.9180, ); } @override Future calculateRoute( List stops, { bool avoidHighways = false, bool avoidTolls = false, bool avoidFerries = false, RoutingOptions? options, }) async { final geom = [ AppLatLng(stops.first.latitude, stops.first.longitude), const AppLatLng(34.7250, -83.9150), AppLatLng(stops.last.latitude, stops.last.longitude), ]; return RouteCalculationResult( straightLineDistanceMeters: 12000.0, estimatedDistanceMeters: 14500.0, estimatedDurationSeconds: 1080, geometry: geom, isStraightLine: false, providerName: name, options: options, curvatureMetric: 85.0, // Twisty curves maneuvers: const [ RouteManeuver( index: 0, instruction: 'Depart on GA-60', type: ManeuverType.depart, distanceMeters: 4500.0, durationSeconds: 360, location: AppLatLng(34.7200, -83.9100), ), RouteManeuver( index: 1, instruction: 'Turn right on US-19 N', type: ManeuverType.turnRight, distanceMeters: 10000.0, durationSeconds: 720, location: AppLatLng(34.7250, -83.9150), ), RouteManeuver( index: 2, instruction: 'Arrive at Blood Mountain Gap', type: ManeuverType.arrive, distanceMeters: 0.0, durationSeconds: 0, location: AppLatLng(34.7330, -83.9180), ), ], ); } } void main() { TestWidgetsFlutterBinding.ensureInitialized(); setUp(() { SharedPreferences.setMockInitialValues({}); }); Widget buildSubject() { return ProviderScope( overrides: [ activeRouteCalculationProvider.overrideWithValue(_FakeRouteProvider()), locationStreamProvider.overrideWith((ref) => Stream.value( Position( latitude: 34.7200, longitude: -83.9100, timestamp: DateTime.now(), accuracy: 5.0, altitude: 300.0, altitudeAccuracy: 1.0, heading: 45.0, headingAccuracy: 1.0, speed: 15.0, speedAccuracy: 0.5, ), )), ], child: MaterialApp( theme: ThemeData.dark().copyWith( extensions: const [MotorSignalThemeColors.dark], ), home: const MapScreen(), ), ); } testWidgets('MapScreen destination search autocomplete and route preview flow', (tester) async { await tester.binding.setSurfaceSize(const Size(412, 915)); // Google Pixel 8a portrait size await tester.pumpWidget(buildSubject()); await tester.pumpAndSettle(); // Verify search input is displayed final searchField = find.byType(TextField); expect(searchField, findsOneWidget); // Enter search query await tester.enterText(searchField, 'Blood Mountain'); await tester.pump(const Duration(milliseconds: 400)); await tester.pumpAndSettle(); // Verify search results overlay rendered expect(find.byKey(const ValueKey('search_results_overlay')), findsOneWidget); expect(find.text('Blood Mountain Gap'), findsOneWidget); // Tap on search result item await tester.tap(find.text('Blood Mountain Gap')); await tester.pumpAndSettle(); // Verify route preview card appears expect(find.byKey(const ValueKey('route_preview_card')), findsOneWidget); expect(find.textContaining('Twisty Curves'), findsOneWidget); expect(find.byKey(const ValueKey('start_navigation_button')), findsOneWidget); // Tap "START NAVIGATION" await tester.tap(find.byKey(const ValueKey('start_navigation_button'))); await tester.pumpAndSettle(); // Verify navigation maneuver card appears on map expect(find.byKey(const ValueKey('navigation_maneuver_card')), findsOneWidget); expect(find.text('Depart on GA-60'), findsOneWidget); // Exit navigation await tester.tap(find.byKey(const ValueKey('nav_stop_button'))); await tester.pumpAndSettle(); expect(find.byKey(const ValueKey('navigation_maneuver_card')), findsNothing); }); }