187 lines
6.0 KiB
Dart
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);
|
|
});
|
|
}
|