MotorSignal-App/test/route_planner_screen_test.dart

229 lines
7.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:motorcycle_dash/features/routes/route_planner_screen.dart';
import 'package:motorcycle_dash/services/auth/auth_providers.dart';
import 'package:motorcycle_dash/services/auth/auth_user.dart';
import 'package:motorcycle_dash/services/entitlements/entitlement_providers.dart';
void main() {
testWidgets('RoutePlannerScreen renders title, empty state, and stop controls', (tester) async {
await tester.binding.setSurfaceSize(const Size(360, 640));
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWith((ref) => const AuthUser(
uid: 'alice',
isAnonymous: false,
)),
isProSubscriberProvider.overrideWith((ref) => false),
],
child: const MaterialApp(
home: RoutePlannerScreen(),
),
),
);
await tester.pumpAndSettle();
// Verify AppBar title
expect(find.text('ROUTE PLANNER'), findsOneWidget);
expect(find.text('SAVE'), findsOneWidget);
// Verify Empty state message
expect(find.text('No stops added yet.'), findsOneWidget);
expect(find.text('ADD STOP'), findsOneWidget);
expect(find.text('CURRENT GPS'), findsOneWidget);
});
testWidgets('RoutePlannerScreen handles adding a stop', (tester) async {
await tester.binding.setSurfaceSize(const Size(360, 640));
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWith((ref) => const AuthUser(
uid: 'alice',
isAnonymous: false,
)),
isProSubscriberProvider.overrideWith((ref) => false),
],
child: const MaterialApp(
home: RoutePlannerScreen(),
),
),
);
await tester.pumpAndSettle();
// Tap ADD STOP
await tester.tap(find.text('ADD STOP'));
await tester.pumpAndSettle();
// Dialog appears
expect(find.text('Add Route Stop'), findsOneWidget);
// Enter name, lat, lng
final dialogTextFields = find.descendant(
of: find.byType(AlertDialog),
matching: find.byType(TextField),
);
await tester.enterText(dialogTextFields.at(0), 'Alice\'s Restaurant');
await tester.enterText(dialogTextFields.at(1), '37.3861');
await tester.enterText(dialogTextFields.at(2), '-122.2641');
// Tap ADD button in dialog
await tester.tap(find.text('ADD'));
await tester.pumpAndSettle();
// Verify stop is added in list
expect(find.text('Alice\'s Restaurant'), findsOneWidget);
expect(find.text('1/25 STOPS'), findsOneWidget);
});
testWidgets('RoutePlannerScreen responsive layout on narrow 320dp screen', (tester) async {
// 320dp narrow device test
await tester.binding.setSurfaceSize(const Size(320, 568));
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWith((ref) => const AuthUser(
uid: 'alice',
isAnonymous: false,
)),
isProSubscriberProvider.overrideWith((ref) => false),
],
child: const MaterialApp(
home: RoutePlannerScreen(),
),
),
);
await tester.pumpAndSettle();
// On narrow screen, compact labels 'ADD' and 'GPS' are shown
expect(find.text('ADD'), findsOneWidget);
expect(find.text('GPS'), findsOneWidget);
// No RenderFlex overflow
expect(tester.takeException(), isNull);
});
testWidgets('RoutePlannerScreen handles title editing under Pixel 8a IME keyboard inset without overflow', (tester) async {
// Pixel 8a viewport with keyboard open (height reduced from 857 to 440)
await tester.binding.setSurfaceSize(const Size(385, 440));
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWith((ref) => const AuthUser(
uid: 'alice',
isAnonymous: false,
)),
isProSubscriberProvider.overrideWith((ref) => false),
],
child: const MaterialApp(
home: RoutePlannerScreen(),
),
),
);
await tester.pumpAndSettle();
// Find Route Title TextField
final titleField = find.byType(TextField).first;
expect(titleField, findsOneWidget);
// Enter title text while constrained
await tester.enterText(titleField, 'Marionette Route Validation');
await tester.pumpAndSettle();
expect(find.text('Marionette Route Validation'), findsOneWidget);
// Verify SAVE button in AppBar and stop controls remain visible and reachable
expect(find.text('SAVE'), findsOneWidget);
expect(find.byKey(const ValueKey('route_planner_add_stop_button')), findsOneWidget);
expect(find.byKey(const ValueKey('route_planner_current_gps_button')), findsOneWidget);
expect(find.text('No stops added yet.'), findsOneWidget);
// No RenderFlex overflow
expect(tester.takeException(), isNull);
});
testWidgets('RoutePlannerScreen handles constrained 360x360 and 320x320 dimensions without overflow', (tester) async {
// Severely constrained 360x360 viewport
await tester.binding.setSurfaceSize(const Size(360, 360));
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWith((ref) => const AuthUser(
uid: 'alice',
isAnonymous: false,
)),
isProSubscriberProvider.overrideWith((ref) => false),
],
child: const MaterialApp(
home: RoutePlannerScreen(),
),
),
);
await tester.pumpAndSettle();
final titleField = find.byType(TextField).first;
await tester.enterText(titleField, 'Constrained 360');
await tester.pumpAndSettle();
expect(find.text('Constrained 360'), findsOneWidget);
expect(tester.takeException(), isNull);
// Extremely constrained 320x320 viewport
await tester.binding.setSurfaceSize(const Size(320, 320));
await tester.pumpAndSettle();
await tester.enterText(titleField, 'Constrained 320');
await tester.pumpAndSettle();
expect(find.text('Constrained 320'), findsOneWidget);
expect(tester.takeException(), isNull);
});
testWidgets('RoutePlannerScreen landscape layout (800x360) renders without RenderFlex overflow', (tester) async {
// Landscape orientation on wide screen
await tester.binding.setSurfaceSize(const Size(800, 360));
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWith((ref) => const AuthUser(
uid: 'alice',
isAnonymous: false,
)),
isProSubscriberProvider.overrideWith((ref) => false),
],
child: const MaterialApp(
home: RoutePlannerScreen(),
),
),
);
await tester.pumpAndSettle();
// Verify title field and controls render properly
final titleField = find.byType(TextField).first;
await tester.enterText(titleField, 'Skyline Ridge Landscape');
await tester.pumpAndSettle();
expect(find.text('Skyline Ridge Landscape'), findsOneWidget);
expect(find.text('SAVE'), findsOneWidget);
expect(find.byKey(const ValueKey('route_planner_add_stop_button')), findsOneWidget);
expect(find.byKey(const ValueKey('route_planner_current_gps_button')), findsOneWidget);
// No RenderFlex overflow
expect(tester.takeException(), isNull);
});
}