MotorSignal-App/test/club_rides_discovery_ui_tes...

169 lines
6.1 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:motorcycle_dash/core/theme/theme_extensions.dart';
import 'package:motorcycle_dash/features/social/clubs/club_detail_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/clubs/club_models.dart';
import 'package:motorcycle_dash/services/clubs/club_providers.dart';
import 'package:motorcycle_dash/services/clubs/club_service.dart';
void main() {
group('ClubDetailScreen Discovery UI State Tests', () {
const testUser = AuthUser(uid: 'rider_alice', isAnonymous: false);
final testClub = Club(
id: 'club_alpha',
name: 'Alpha Riders',
slug: 'alpha-riders',
description: 'The premier Alpha motorcycle community',
ownerUid: 'owner_bob',
memberCount: 15,
visibility: ClubVisibility.public,
admissionPolicy: ClubAdmissionPolicy.open,
createdAt: DateTime(2026, 1, 1),
updatedAt: DateTime(2026, 1, 1),
);
final activeMember = ClubMember(
clubId: 'club_alpha',
uid: 'rider_alice',
role: ClubRole.member,
status: ClubMemberStatus.active,
joinedAt: DateTime(2026, 1, 1),
updatedAt: DateTime(2026, 1, 1),
);
Widget buildTestWidget({
bool overrideMember = false,
ClubMember? memberOverride,
required AsyncValue<List<ClubRideDiscovery>> discoveryOverride,
}) {
return ProviderScope(
overrides: [
currentUserProvider.overrideWithValue(testUser),
clubDetailProvider('club_alpha').overrideWith((ref) => Stream.value(testClub)),
currentMemberProvider('club_alpha').overrideWith(
(ref) => Stream.value(overrideMember ? memberOverride : activeMember),
),
clubDiscoveryRidesProvider('club_alpha').overrideWith((ref) => discoveryOverride.when(
data: (data) => Future.value(data),
error: (e, s) => Future.error(e, s),
loading: () => Completer<List<ClubRideDiscovery>>().future,
)),
],
child: MaterialApp(
theme: ThemeData.dark().copyWith(
extensions: [MotorSignalThemeColors.dark],
),
home: const ClubDetailScreen(clubId: 'club_alpha'),
),
);
}
testWidgets('Non-member sees active members only notice', (tester) async {
await tester.binding.setSurfaceSize(const Size(800, 2400));
addTearDown(() => tester.binding.setSurfaceSize(null));
await tester.pumpWidget(
buildTestWidget(
overrideMember: true,
memberOverride: null,
discoveryOverride: const AsyncValue.data([]),
),
);
await tester.pumpAndSettle();
expect(find.text('CLUB RIDES'), findsOneWidget);
expect(find.text('Club rides are visible to active members only.'), findsOneWidget);
});
testWidgets('Active member sees loading progress indicator during discovery fetch', (tester) async {
await tester.binding.setSurfaceSize(const Size(800, 2400));
addTearDown(() => tester.binding.setSurfaceSize(null));
await tester.pumpWidget(
buildTestWidget(
discoveryOverride: const AsyncValue.loading(),
),
);
await tester.pump();
expect(find.text('CLUB RIDES'), findsOneWidget);
expect(find.byType(CircularProgressIndicator), findsOneWidget);
});
testWidgets('Active member sees empty state when 0 upcoming rides found', (tester) async {
await tester.binding.setSurfaceSize(const Size(800, 2400));
addTearDown(() => tester.binding.setSurfaceSize(null));
await tester.pumpWidget(
buildTestWidget(
discoveryOverride: const AsyncValue.data([]),
),
);
await tester.pumpAndSettle();
expect(find.text('CLUB RIDES'), findsOneWidget);
expect(find.text('No upcoming club rides scheduled.'), findsOneWidget);
});
testWidgets('Active member sees populated discovery ride cards', (tester) async {
await tester.binding.setSurfaceSize(const Size(800, 2400));
addTearDown(() => tester.binding.setSurfaceSize(null));
final testRides = [
ClubRideDiscovery(
rideId: 'ride_101',
clubId: 'club_alpha',
title: 'Sunday Morning Twisties',
scheduledStartAt: DateTime(2026, 9, 27, 9, 0),
departureAt: DateTime(2026, 9, 27, 9, 30),
joinPolicy: 'requestToJoin',
participantCount: 6,
participantLimit: 12,
meetingLocationLabel: 'Lookout Point Parking',
paceClassification: 'Moderate',
organizerDisplayName: 'Captain Dan',
),
];
await tester.pumpWidget(
buildTestWidget(
discoveryOverride: AsyncValue.data(testRides),
),
);
await tester.pumpAndSettle();
expect(find.text('CLUB RIDES'), findsOneWidget);
expect(find.text('Sunday Morning Twisties'), findsOneWidget);
expect(find.text('REQUEST TO JOIN'), findsOneWidget);
expect(find.text('Lookout Point Parking'), findsOneWidget);
expect(find.text('6 / 12 riders'), findsOneWidget);
expect(find.text('Moderate'), findsOneWidget);
expect(find.text('Captain Dan'), findsOneWidget);
});
testWidgets('Active member sees error state with retry button on failure', (tester) async {
await tester.binding.setSurfaceSize(const Size(800, 2400));
addTearDown(() => tester.binding.setSurfaceSize(null));
await tester.pumpWidget(
buildTestWidget(
discoveryOverride: const AsyncValue.error(
ClubOperationException('Network error', code: 'network_error'),
StackTrace.empty,
),
),
);
await tester.pumpAndSettle();
expect(find.text('CLUB RIDES'), findsOneWidget);
expect(find.text('Network connection issue. Please check your connection and try again.'), findsOneWidget);
expect(find.widgetWithText(OutlinedButton, 'Retry'), findsOneWidget);
});
});
}