MotorSignal-App/test/live_group_ride_end_ride_te...

300 lines
10 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/group_rides/live_group_ride_screen.dart';
import 'package:motorcycle_dash/services/auth/auth_user.dart';
import 'package:motorcycle_dash/services/auth/auth_providers.dart';
import 'package:motorcycle_dash/services/group_rides/group_ride_models.dart';
import 'package:motorcycle_dash/services/group_rides/group_ride_providers.dart';
import 'package:motorcycle_dash/services/group_rides/group_ride_service.dart';
class _FakeGroupRideService extends GroupRideService {
String? lastCallerUid;
String? lastGroupRideId;
GroupRideStatus? lastNewStatus;
int updateRideStatusCallCount = 0;
@override
Future<void> updateRideStatus({
required String callerUid,
required String groupRideId,
required GroupRideStatus newStatus,
}) async {
lastCallerUid = callerUid;
lastGroupRideId = groupRideId;
lastNewStatus = newStatus;
updateRideStatusCallCount++;
}
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('LiveGroupRideScreen - Organizer End Ride Action Tests', () {
final now = DateTime.now();
final underwayRide = GroupRide(
id: 'ride_test_123',
organizerUid: 'organizer_alice',
organizerHandle: 'alice_apex',
organizerDisplayName: 'Alice Apex',
title: 'Sunday Morning Twisties',
status: GroupRideStatus.underway,
scheduledStartAt: now,
departureAt: now,
participantCount: 2,
meetingPoint: const GroupRideLocation(
latitude: 34.0,
longitude: -84.0,
displayName: 'Start Point',
),
destination: const GroupRideLocation(
latitude: 34.5,
longitude: -84.5,
displayName: 'End Point',
),
stops: const [],
createdAt: now,
updatedAt: now,
);
final completedRide = underwayRide.copyWith(
status: GroupRideStatus.completed,
);
final participants = [
GroupRideParticipant(
userId: 'organizer_alice',
role: GroupRideRole.lead,
status: ParticipantStatus.accepted,
riderHandle: 'alice_apex',
riderDisplayName: 'Alice Apex',
createdAt: now,
updatedAt: now,
),
GroupRideParticipant(
userId: 'rider_bob',
role: GroupRideRole.participant,
status: ParticipantStatus.accepted,
riderHandle: 'bob_rider',
riderDisplayName: 'Bob Rider',
createdAt: now,
updatedAt: now,
),
];
testWidgets('organizer sees "Ride Options" popup menu with "End Ride" when underway',
(tester) async {
final fakeService = _FakeGroupRideService();
const user = AuthUser(
uid: 'organizer_alice',
email: 'alice@example.com',
isAnonymous: false,
);
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWithValue(user),
groupRideServiceProvider.overrideWithValue(fakeService),
groupRideStreamProvider('ride_test_123')
.overrideWith((ref) => Stream.value(underwayRide)),
groupRideParticipantsProvider('ride_test_123')
.overrideWith((ref) => Stream.value(participants)),
groupRideLivePresenceProvider('ride_test_123')
.overrideWith((ref) => Stream.value([])),
groupRideLiveSharingAllowedProvider.overrideWith((ref) => true),
],
child: const MaterialApp(
home: LiveGroupRideScreen(rideId: 'ride_test_123'),
),
),
);
await tester.pumpAndSettle();
// Ride Options menu button should be visible
final rideOptionsFinder = find.byTooltip('Ride Options');
expect(rideOptionsFinder, findsOneWidget);
// Open the popup menu
await tester.tap(rideOptionsFinder);
await tester.pumpAndSettle();
// "End Ride" menu item should be visible
expect(find.text('End Ride'), findsOneWidget);
});
testWidgets('non-organizer participant does NOT see "Ride Options" popup menu',
(tester) async {
final fakeService = _FakeGroupRideService();
const nonOrganizerUser = AuthUser(
uid: 'rider_bob',
email: 'bob@example.com',
isAnonymous: false,
);
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWithValue(nonOrganizerUser),
groupRideServiceProvider.overrideWithValue(fakeService),
groupRideStreamProvider('ride_test_123')
.overrideWith((ref) => Stream.value(underwayRide)),
groupRideParticipantsProvider('ride_test_123')
.overrideWith((ref) => Stream.value(participants)),
groupRideLivePresenceProvider('ride_test_123')
.overrideWith((ref) => Stream.value([])),
groupRideLiveSharingAllowedProvider.overrideWith((ref) => true),
],
child: const MaterialApp(
home: LiveGroupRideScreen(rideId: 'ride_test_123'),
),
),
);
await tester.pumpAndSettle();
// Non-organizer should NOT see Ride Options
expect(find.byTooltip('Ride Options'), findsNothing);
expect(find.text('End Ride'), findsNothing);
});
testWidgets('organizer does NOT see "Ride Options" popup menu when ride is completed',
(tester) async {
final fakeService = _FakeGroupRideService();
const user = AuthUser(
uid: 'organizer_alice',
email: 'alice@example.com',
isAnonymous: false,
);
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWithValue(user),
groupRideServiceProvider.overrideWithValue(fakeService),
groupRideStreamProvider('ride_test_123')
.overrideWith((ref) => Stream.value(completedRide)),
groupRideParticipantsProvider('ride_test_123')
.overrideWith((ref) => Stream.value(participants)),
groupRideLivePresenceProvider('ride_test_123')
.overrideWith((ref) => Stream.value([])),
groupRideLiveSharingAllowedProvider.overrideWith((ref) => true),
],
child: const MaterialApp(
home: LiveGroupRideScreen(rideId: 'ride_test_123'),
),
),
);
await tester.pumpAndSettle();
expect(find.byTooltip('Ride Options'), findsNothing);
});
testWidgets('tapping End Ride shows confirmation dialog and CANCEL dismisses without state change',
(tester) async {
final fakeService = _FakeGroupRideService();
const user = AuthUser(
uid: 'organizer_alice',
email: 'alice@example.com',
isAnonymous: false,
);
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWithValue(user),
groupRideServiceProvider.overrideWithValue(fakeService),
groupRideStreamProvider('ride_test_123')
.overrideWith((ref) => Stream.value(underwayRide)),
groupRideParticipantsProvider('ride_test_123')
.overrideWith((ref) => Stream.value(participants)),
groupRideLivePresenceProvider('ride_test_123')
.overrideWith((ref) => Stream.value([])),
groupRideLiveSharingAllowedProvider.overrideWith((ref) => true),
],
child: const MaterialApp(
home: LiveGroupRideScreen(rideId: 'ride_test_123'),
),
),
);
await tester.pumpAndSettle();
// Tap Ride Options
await tester.tap(find.byTooltip('Ride Options'));
await tester.pumpAndSettle();
// Tap End Ride
await tester.tap(find.text('End Ride'));
await tester.pumpAndSettle();
// Dialog should be displayed with expected title and copy
expect(find.text('End this group ride?'), findsOneWidget);
expect(
find.text('Ending the ride will stop live location sharing and mark the Group Ride as completed.'),
findsOneWidget,
);
// Tap CANCEL
await tester.tap(find.text('CANCEL'));
await tester.pumpAndSettle();
// Dialog dismissed, no service call made
expect(find.text('End this group ride?'), findsNothing);
expect(fakeService.updateRideStatusCallCount, equals(0));
});
testWidgets('tapping END RIDE in confirmation dialog triggers status update to completed',
(tester) async {
final fakeService = _FakeGroupRideService();
const user = AuthUser(
uid: 'organizer_alice',
email: 'alice@example.com',
isAnonymous: false,
);
await tester.pumpWidget(
ProviderScope(
overrides: [
currentUserProvider.overrideWithValue(user),
groupRideServiceProvider.overrideWithValue(fakeService),
groupRideStreamProvider('ride_test_123')
.overrideWith((ref) => Stream.value(underwayRide)),
groupRideParticipantsProvider('ride_test_123')
.overrideWith((ref) => Stream.value(participants)),
groupRideLivePresenceProvider('ride_test_123')
.overrideWith((ref) => Stream.value([])),
groupRideLiveSharingAllowedProvider.overrideWith((ref) => true),
],
child: const MaterialApp(
home: LiveGroupRideScreen(rideId: 'ride_test_123'),
),
),
);
await tester.pumpAndSettle();
// Tap Ride Options
await tester.tap(find.byTooltip('Ride Options'));
await tester.pumpAndSettle();
// Tap End Ride menu item
await tester.tap(find.text('End Ride'));
await tester.pumpAndSettle();
// Tap END RIDE button in dialog
await tester.tap(find.text('END RIDE'));
await tester.pumpAndSettle();
// Verify updateRideStatus was invoked with correct arguments
expect(fakeService.updateRideStatusCallCount, equals(1));
expect(fakeService.lastCallerUid, equals('organizer_alice'));
expect(fakeService.lastGroupRideId, equals('ride_test_123'));
expect(fakeService.lastNewStatus, equals(GroupRideStatus.completed));
});
});
}