You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
basedFood-app/test/ui/navigation/main_navigation_scaffold_te...

313 lines
13 KiB

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/session/models/user.dart';
import 'package:app_boilerplate/data/firebase/firebase_service.dart';
import 'package:app_boilerplate/data/recipes/recipe_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(() async {
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 LocalStorageService.getItem for app_settings (used by RelayManagementScreen)
when(mockLocalStorageService.getItem('app_settings')).thenAnswer((_) async => null);
when(mockLocalStorageService.getItem('app_preferences')).thenAnswer((_) async => null);
// Stub NostrService methods that might be called by UI
final mockKeyPair = NostrKeyPair.generate();
when(mockNostrService.generateKeyPair()).thenReturn(mockKeyPair);
// Create a RecipeService for tests (needed by RecipesScreen)
// Use a simple setup - RecipesScreen will handle initialization errors gracefully
RecipeService? recipeService;
try {
recipeService = RecipeService(
localStorage: mockLocalStorageService,
nostrService: mockNostrService,
);
// Don't initialize here - let screens handle it or fail gracefully
} catch (e) {
// RecipeService creation failed, that's ok for tests
recipeService = null;
}
// Register services with ServiceLocator
ServiceLocator.instance.registerServices(
localStorageService: mockLocalStorageService,
nostrService: mockNostrService,
syncEngine: mockSyncEngine,
sessionService: mockSessionService,
firebaseService: mockFirebaseService,
recipeService: recipeService,
);
});
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 {
// When not logged in, only Home tab is visible in bottom nav
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Check for navigation icons (custom bottom nav, not standard BottomNavigationBar)
expect(find.byIcon(Icons.home), findsWidgets);
// Check for labels
expect(find.text('Home'), findsWidgets);
// User icon is now in AppBar, not bottom nav
// Recipes, Favourites, and Add button in bottom nav are hidden when not logged in
expect(find.text('Recipes'), findsNothing);
expect(find.text('Favourites'), findsNothing);
// Note: Home screen may have its own add icon, so we check for the bottom nav add button specifically
// The bottom nav add button is in a Material widget with CircleBorder
expect(find.text('Add Recipe'), findsNothing);
});
testWidgets('displays Add Recipe button in center of bottom nav', (WidgetTester tester) async {
// Set up as logged in to see Add button
when(mockSessionService.isLoggedIn).thenReturn(true);
final testUser = User(id: 'test_user', username: 'Test User');
when(mockSessionService.currentUser).thenReturn(testUser);
await tester.pumpWidget(createTestWidget());
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow widget to build
// 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 {
// Set up as logged in to access Recipes tab
when(mockSessionService.isLoggedIn).thenReturn(true);
final testUser = User(id: 'test_user', username: 'Test User');
when(mockSessionService.currentUser).thenReturn(testUser);
await tester.pumpWidget(createTestWidget());
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow widget to build
// Tap Recipes tab
final recipesTab = find.text('Recipes');
expect(recipesTab, findsWidgets);
await tester.tap(recipesTab);
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow navigation
// Verify Recipes screen is shown (check for AppBar title)
expect(find.text('Recipes'), findsWidgets);
});
testWidgets('can navigate to Favourites screen', (WidgetTester tester) async {
// Set up as logged in to access Favourites tab
when(mockSessionService.isLoggedIn).thenReturn(true);
final testUser = User(id: 'test_user', username: 'Test User');
when(mockSessionService.currentUser).thenReturn(testUser);
await tester.pumpWidget(createTestWidget());
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow widget to build
// Tap Favourites tab
final favouritesTab = find.text('Favourites');
expect(favouritesTab, findsWidgets);
await tester.tap(favouritesTab);
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow navigation
// Verify Favourites screen is shown
expect(find.text('Favourites'), findsWidgets);
});
testWidgets('can navigate to User/Session screen', (WidgetTester tester) async {
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Tap User icon in AppBar (person icon)
final userIcon = find.byIcon(Icons.person);
expect(userIcon, findsWidgets);
// Tap the first person icon (should be in AppBar)
await tester.tap(userIcon.first);
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow navigation
// Verify User screen is shown
expect(find.text('User'), findsWidgets);
});
testWidgets('Add Recipe button navigates to Add Recipe screen', (WidgetTester tester) async {
// Set up as logged in to see Add button
when(mockSessionService.isLoggedIn).thenReturn(true);
final testUser = User(id: 'test_user', username: 'Test User');
when(mockSessionService.currentUser).thenReturn(testUser);
await tester.pumpWidget(createTestWidget());
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow widget to build
// Find the add icon in the bottom navigation (there may be multiple - Home screen has one too)
// We want the one in the bottom nav, which should be the last one or in a Material widget
final addButtons = find.byIcon(Icons.add);
expect(addButtons, findsWidgets);
// Tap the last add button (should be the bottom nav one)
await tester.tap(addButtons.last);
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 200)); // Allow 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 {
// Set up as logged in to access User screen
when(mockSessionService.isLoggedIn).thenReturn(true);
final testUser = User(id: 'test_user', username: 'Test User');
when(mockSessionService.currentUser).thenReturn(testUser);
await tester.pumpWidget(createTestWidget());
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow widget to build
// Navigate to User screen via AppBar icon (only screen with settings icon)
final userIcon = find.byIcon(Icons.person);
expect(userIcon, findsWidgets);
await tester.tap(userIcon.first);
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow navigation
// Settings icon should be in AppBar actions
expect(find.byIcon(Icons.settings), findsWidgets);
});
testWidgets('settings icon is tappable and triggers navigation', (WidgetTester tester) async {
// Set up as logged in to access User screen
when(mockSessionService.isLoggedIn).thenReturn(true);
final testUser = User(id: 'test_user', username: 'Test User');
when(mockSessionService.currentUser).thenReturn(testUser);
await tester.pumpWidget(createTestWidget());
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow widget to build
// Navigate to User screen via AppBar icon (only screen with settings icon)
final userIcon = find.byIcon(Icons.person);
expect(userIcon, findsWidgets);
await tester.tap(userIcon.first);
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow navigation
// Find settings icon in AppBar
final settingsIcons = find.byIcon(Icons.settings);
expect(settingsIcons, findsWidgets);
// Tap the 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('User screen has settings icon in AppBar', (WidgetTester tester) async {
// Set up as logged in to access all tabs
when(mockSessionService.isLoggedIn).thenReturn(true);
final testUser = User(id: 'test_user', username: 'Test User');
when(mockSessionService.currentUser).thenReturn(testUser);
await tester.pumpWidget(createTestWidget());
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow widget to build
// Navigate to User screen via AppBar icon (only screen with settings icon)
final userIcon = find.byIcon(Icons.person);
expect(userIcon, findsWidgets);
await tester.tap(userIcon.first);
await tester.pump(); // Initial pump
await tester.pump(const Duration(milliseconds: 100)); // Allow navigation
expect(find.byIcon(Icons.settings), findsWidgets);
});
});
}

Powered by TurnKey Linux.