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/clubs_hub_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'; class FakeClubServiceForHub extends ClubService { final Map clubs; final Map handles; FakeClubServiceForHub({ this.clubs = const {}, this.handles = const {}, }); @override Future getClubBySlug(String slug) async { final clubId = handles[slug]; if (clubId == null) return null; return clubs[clubId]; } @override Future> discoverPublicClubs({int limit = 25, dynamic startAfter}) async { return clubs.values.where((c) => c.visibility == ClubVisibility.public).toList(); } } void main() { group('ClubsHubScreen Widget Tests', () { testWidgets('renders account-required notice when signed out', (tester) async { await tester.pumpWidget( ProviderScope( overrides: [ currentUserProvider.overrideWith((ref) => null), ], child: MaterialApp( theme: ThemeData.dark().copyWith( extensions: [MotorSignalThemeColors.dark], ), home: const ClubsHubScreen(), ), ), ); expect(find.text('Account Required for Clubs'), findsOneWidget); expect( find.text('Sign in or create a MotorSignal account to join and manage riding clubs.'), findsOneWidget, ); expect(find.text('Sign In / Register'), findsOneWidget); }); testWidgets('renders tabs, empty my clubs, and public clubs when authenticated', (tester) async { const user = AuthUser(uid: 'alice', isAnonymous: false); final publicClub = Club( id: 'club_pub_1', name: 'Bay Area Motos', slug: 'bay-area-motos', description: 'San Francisco & Bay Area motorcycle rides.', ownerUid: 'bob', memberCount: 15, createdAt: DateTime(2026, 1, 1), updatedAt: DateTime(2026, 1, 1), ); final fakeService = FakeClubServiceForHub( clubs: {'club_pub_1': publicClub}, handles: {'bay-area-motos': 'club_pub_1'}, ); await tester.pumpWidget( ProviderScope( overrides: [ currentUserProvider.overrideWith((ref) => user), clubServiceProvider.overrideWithValue(fakeService), myClubsProvider.overrideWith((ref) => Stream.value(const [])), publicClubsProvider.overrideWith((ref) => Future.value([publicClub])), ], child: MaterialApp( theme: ThemeData.dark().copyWith( extensions: [MotorSignalThemeColors.dark], ), home: const ClubsHubScreen(), ), ), ); await tester.pumpAndSettle(); // Verify tabs expect(find.text('MY CLUBS'), findsOneWidget); expect(find.text('DISCOVER'), findsOneWidget); // Verify Empty My Clubs state expect(find.text('No Club Memberships Yet'), findsOneWidget); expect(find.text('Discover Clubs'), findsOneWidget); // Tap on DISCOVER tab await tester.tap(find.text('DISCOVER')); await tester.pumpAndSettle(); // Verify Public Discovery content expect(find.text('Public Clubs Directory'), findsOneWidget); expect(find.text('Bay Area Motos'), findsOneWidget); expect(find.text('@bay-area-motos'), findsOneWidget); // Verify search input expect(find.byType(TextField), findsOneWidget); }); testWidgets('executes exact handle search in Discover tab', (tester) async { const user = AuthUser(uid: 'alice', isAnonymous: false); final targetClub = Club( id: 'club_norcal', name: 'NorCal Riders', slug: 'norcal-riders', description: 'Northern California twisties.', ownerUid: 'charlie', memberCount: 8, createdAt: DateTime(2026, 1, 1), updatedAt: DateTime(2026, 1, 1), ); final fakeService = FakeClubServiceForHub( clubs: {'club_norcal': targetClub}, handles: {'norcal-riders': 'club_norcal'}, ); await tester.pumpWidget( ProviderScope( overrides: [ currentUserProvider.overrideWith((ref) => user), clubServiceProvider.overrideWithValue(fakeService), myClubsProvider.overrideWith((ref) => Stream.value(const [])), publicClubsProvider.overrideWith((ref) => Future.value(const [])), ], child: MaterialApp( theme: ThemeData.dark().copyWith( extensions: [MotorSignalThemeColors.dark], ), home: const ClubsHubScreen(), ), ), ); await tester.pumpAndSettle(); // Switch to Discover tab await tester.tap(find.text('DISCOVER')); await tester.pumpAndSettle(); // Search for club by slug await tester.enterText(find.byType(TextField), 'norcal-riders'); await tester.testTextInput.receiveAction(TextInputAction.search); await tester.pumpAndSettle(); expect(find.text('EXACT MATCH: @norcal-riders'), findsOneWidget); expect(find.text('NorCal Riders'), findsOneWidget); // Search for non-existent slug await tester.enterText(find.byType(TextField), 'unknown-club'); await tester.testTextInput.receiveAction(TextInputAction.search); await tester.pumpAndSettle(); expect(find.text('No club found with handle @unknown-club'), findsOneWidget); }); testWidgets('renders incoming invitations section in My Clubs tab and handles accept', (tester) async { const user = AuthUser(uid: 'alice', isAnonymous: false); final inviteClub = Club( id: 'club_invite_1', name: 'Bay Area Motos', slug: 'bay-area-motos', description: 'San Francisco & Bay Area motorcycle rides.', ownerUid: 'bob', memberCount: 15, createdAt: DateTime(2026, 1, 1), updatedAt: DateTime(2026, 1, 1), ); final inviteMember = ClubMember( uid: 'alice', clubId: 'club_invite_1', status: ClubMemberStatus.invited, role: ClubRole.member, invitedByUid: 'bob', updatedAt: DateTime(2026, 1, 1), ); final fakeService = FakeClubServiceForHub( clubs: {'club_invite_1': inviteClub}, handles: {'bay-area-motos': 'club_invite_1'}, ); await tester.pumpWidget( ProviderScope( overrides: [ currentUserProvider.overrideWith((ref) => user), clubServiceProvider.overrideWithValue(fakeService), clubDetailProvider('club_invite_1').overrideWith((ref) => Stream.value(inviteClub)), myClubsProvider.overrideWith((ref) => Stream.value(const [])), myClubInvitesProvider.overrideWith((ref) => Stream.value([inviteMember])), publicClubsProvider.overrideWith((ref) => Future.value([inviteClub])), ], child: MaterialApp( theme: ThemeData.dark().copyWith( extensions: [MotorSignalThemeColors.dark], ), home: const ClubsHubScreen(), ), ), ); await tester.pumpAndSettle(); expect(find.text('CLUB INVITATIONS'), findsOneWidget); expect(find.text('Bay Area Motos'), findsOneWidget); expect(find.text('INVITED'), findsOneWidget); expect(find.text('Accept'), findsOneWidget); expect(find.text('Decline'), findsOneWidget); }); }); }