MotorSignal-App/test/map_destination_search_test...

187 lines
6.0 KiB
Dart

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<List<PlaceSearchResult>> 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<ResolvedPlace> 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<RouteCalculationResult> calculateRoute(
List<RouteStop> 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);
});
}