import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:app_boilerplate/ui/navigation/main_navigation_scaffold.dart'; import 'package:app_boilerplate/ui/navigation/app_router.dart'; import 'package:app_boilerplate/data/local/local_storage_service.dart'; import 'package:app_boilerplate/data/nostr/nostr_service.dart'; import 'package:app_boilerplate/data/nostr/models/nostr_keypair.dart'; import 'package:app_boilerplate/data/sync/sync_engine.dart'; import 'package:app_boilerplate/data/session/session_service.dart'; import 'package:app_boilerplate/data/firebase/firebase_service.dart'; import 'package:app_boilerplate/core/service_locator.dart'; import 'main_navigation_scaffold_test.mocks.dart'; @GenerateMocks([ LocalStorageService, NostrService, SyncEngine, SessionService, FirebaseService, ]) void main() { late MockLocalStorageService mockLocalStorageService; late MockNostrService mockNostrService; late MockSyncEngine mockSyncEngine; late MockSessionService mockSessionService; late MockFirebaseService mockFirebaseService; setUp(() { mockLocalStorageService = MockLocalStorageService(); mockNostrService = MockNostrService(); mockSyncEngine = MockSyncEngine(); mockSessionService = MockSessionService(); mockFirebaseService = MockFirebaseService(); // Set default return values for mocks when(mockSessionService.isLoggedIn).thenReturn(false); when(mockSessionService.currentUser).thenReturn(null); when(mockFirebaseService.isEnabled).thenReturn(false); when(mockNostrService.getRelays()).thenReturn([]); // Stub NostrService methods that might be called by UI final mockKeyPair = NostrKeyPair.generate(); when(mockNostrService.generateKeyPair()).thenReturn(mockKeyPair); // Register services with ServiceLocator ServiceLocator.instance.registerServices( localStorageService: mockLocalStorageService, nostrService: mockNostrService, syncEngine: mockSyncEngine, sessionService: mockSessionService, firebaseService: mockFirebaseService, ); }); tearDown(() { // Reset ServiceLocator after each test ServiceLocator.instance.reset(); }); Widget createTestWidget() { final appRouter = AppRouter( sessionService: mockSessionService, localStorageService: mockLocalStorageService, nostrService: mockNostrService, syncEngine: mockSyncEngine, firebaseService: mockFirebaseService, ); return MaterialApp( onGenerateRoute: appRouter.generateRoute, home: const MainNavigationScaffold(), ); } group('MainNavigationScaffold - Navigation', () { testWidgets('displays bottom navigation bar with correct tabs', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Check for navigation icons (custom bottom nav, not standard BottomNavigationBar) expect(find.byIcon(Icons.home), findsWidgets); expect(find.byIcon(Icons.menu_book), findsWidgets); expect(find.byIcon(Icons.favorite), findsWidgets); expect(find.byIcon(Icons.person), findsWidgets); // Check for labels expect(find.text('Home'), findsWidgets); expect(find.text('Recipes'), findsWidgets); expect(find.text('Favourites'), findsWidgets); expect(find.text('User'), findsWidgets); }); testWidgets('displays Add Recipe button in center of bottom nav', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Find the add icon in the bottom navigation (should be in center) expect(find.byIcon(Icons.add), findsWidgets); // The add button is now part of the custom bottom nav, not a FloatingActionButton }); testWidgets('renders Home screen by default', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Home text should appear in AppBar expect(find.text('Home'), findsWidgets); expect(find.byIcon(Icons.home), findsWidgets); }); testWidgets('can navigate to Recipes screen', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Tap Recipes tab final recipesTab = find.text('Recipes'); expect(recipesTab, findsWidgets); await tester.tap(recipesTab); await tester.pumpAndSettle(); // Verify Recipes screen is shown expect(find.text('Recipes Screen'), findsOneWidget); expect(find.text('Recipes'), findsWidgets); }); testWidgets('can navigate to Favourites screen', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Tap Favourites tab final favouritesTab = find.text('Favourites'); expect(favouritesTab, findsWidgets); await tester.tap(favouritesTab); await tester.pumpAndSettle(); // Verify Favourites screen is shown expect(find.text('Favourites Screen'), findsOneWidget); expect(find.text('Favourites'), findsWidgets); }); testWidgets('can navigate to User/Session screen', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Tap User tab final userTab = find.text('User'); expect(userTab, findsWidgets); await tester.tap(userTab); await tester.pumpAndSettle(); // Verify User screen is shown expect(find.text('User'), findsWidgets); }); testWidgets('Add Recipe button navigates to Add Recipe screen', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Find all add icons (Home screen has its own FAB, bottom nav has the Add Recipe button) final addButtons = find.byIcon(Icons.add); expect(addButtons, findsWidgets); // The bottom nav add button is in a Material widget with CircleBorder // Find it by looking for Material widgets with CircleBorder that contain add icons // We'll tap the last add icon found (should be the bottom nav one, after Home screen's FAB) final allAddIcons = addButtons.evaluate().toList(); if (allAddIcons.length > 1) { // Tap the last one (bottom nav button) await tester.tap(find.byIcon(Icons.add).last); } else { // Only one found, tap it await tester.tap(addButtons.first); } await tester.pump(); // Initial pump await tester.pumpAndSettle(const Duration(seconds: 1)); // Wait for navigation // Verify Add Recipe screen is shown (check for AppBar title) // If navigation worked, we should see "Add Recipe" in the AppBar expect(find.text('Add Recipe'), findsWidgets); }); testWidgets('settings icon appears in AppBar', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Settings icon should be in AppBar actions expect(find.byIcon(Icons.settings), findsWidgets); }); testWidgets('settings icon is tappable and triggers navigation', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Find settings icon in AppBar final settingsIcons = find.byIcon(Icons.settings); expect(settingsIcons, findsWidgets); // Verify we're on Home screen initially expect(find.text('Home'), findsWidgets); // Tap the first settings icon (should be in AppBar) // This should trigger navigation to Relay Management await tester.tap(settingsIcons.first); await tester.pump(); // Just pump once to trigger navigation // Verify navigation was attempted (no errors thrown) // The actual screen content depends on service availability in test environment }); }); group('MainNavigationScaffold - Screen Rendering', () { testWidgets('renders all main screens correctly', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Verify scaffold structure exists // Custom bottom nav (not standard BottomNavigationBar) expect(find.byType(Scaffold), findsWidgets); expect(find.byType(IndexedStack), findsOneWidget); // Verify navigation icons are present expect(find.byIcon(Icons.home), findsWidgets); }); testWidgets('all screens have settings icon in AppBar', (WidgetTester tester) async { await tester.pumpWidget(createTestWidget()); await tester.pumpAndSettle(); // Check Home screen expect(find.byIcon(Icons.settings), findsWidgets); // Navigate to Recipes await tester.tap(find.text('Recipes')); await tester.pumpAndSettle(); expect(find.byIcon(Icons.settings), findsWidgets); // Navigate to Favourites await tester.tap(find.text('Favourites')); await tester.pumpAndSettle(); expect(find.byIcon(Icons.settings), findsWidgets); // Navigate to User await tester.tap(find.text('User')); await tester.pumpAndSettle(); expect(find.byIcon(Icons.settings), findsWidgets); }); }); }