diff --git a/test/features/onboarding/presentation/pages/setup_page_test.dart b/test/features/onboarding/presentation/pages/setup_page_test.dart new file mode 100644 index 0000000..d6b6dd8 --- /dev/null +++ b/test/features/onboarding/presentation/pages/setup_page_test.dart @@ -0,0 +1,225 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; + +import 'package:agenda_tasks/l10n/app_localizations.dart'; +import 'package:agenda_tasks/features/onboarding/presentation/pages/setup_page.dart'; +import 'package:agenda_tasks/features/settings/presentation/viewmodels/settings_viewmodel.dart'; +import 'package:agenda_tasks/features/settings/domain/enums/app_mode.dart'; +import 'package:agenda_tasks/features/settings/data/settings_local_datasource.dart'; +import 'package:agenda_tasks/core/logging/app_logger.dart'; + +// Manual mock for SettingsLocalDataSource +class MockSettingsLocalDataSource implements SettingsLocalDataSource { + AppMode? _appMode; + bool _setupCompleted = false; + bool _onboardingShown = false; + Locale? _locale; + ThemeMode _themeMode = ThemeMode.system; + + @override + AppMode? getAppMode() => _appMode; + + @override + Future setAppMode(AppMode mode) async { + _appMode = mode; + } + + @override + bool isSetupCompleted() => _setupCompleted; + + @override + Future setSetupCompleted(bool completed) async { + _setupCompleted = completed; + } + + @override + bool isOnboardingShown() => _onboardingShown; + + @override + Future setOnboardingShown(bool shown) async { + _onboardingShown = shown; + } + + @override + Locale? getLocale() => _locale; + + @override + Future setLocale(Locale locale) async { + _locale = locale; + } + + @override + ThemeMode getThemeMode() => _themeMode; + + @override + Future setThemeMode(ThemeMode mode) async { + _themeMode = mode; + } +} + +// Manual mock for AppLogger +class MockAppLogger extends AppLogger { + @override + void debug(String message, [dynamic error, StackTrace? stackTrace]) {} + + @override + void info(String message, [dynamic error, StackTrace? stackTrace]) {} + + @override + void warning(String message, [dynamic error, StackTrace? stackTrace]) {} + + @override + void error(String message, [dynamic error, StackTrace? stackTrace]) {} + + @override + void verbose(String message, [dynamic error, StackTrace? stackTrace]) {} +} + +void main() { + group('SetupPage', () { + late SettingsViewModel settingsViewModel; + late MockSettingsLocalDataSource mockDataSource; + late MockAppLogger mockLogger; + + setUp(() { + mockDataSource = MockSettingsLocalDataSource(); + mockLogger = MockAppLogger(); + settingsViewModel = SettingsViewModel( + dataSource: mockDataSource, + logger: mockLogger, + ); + }); + + Widget buildSetupPage() { + final router = GoRouter( + initialLocation: '/setup', + routes: [ + GoRoute( + path: '/setup', + builder: (context, state) => const SetupPage(), + ), + GoRoute( + path: '/onboarding', + builder: (context, state) => const Scaffold( + body: Center(child: Text('Onboarding Page')), + ), + ), + ], + ); + + return ChangeNotifierProvider.value( + value: settingsViewModel, + child: MaterialApp.router( + routerConfig: router, + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [ + Locale('en'), + Locale('de'), + ], + locale: const Locale('en'), + ), + ); + } + + testWidgets('should display setup title', (tester) async { + await tester.pumpWidget(buildSetupPage()); + await tester.pumpAndSettle(); + + expect(find.text('Welcome to Agenda Tasks'), findsOneWidget); + }); + + testWidgets('should display setup subtitle', (tester) async { + await tester.pumpWidget(buildSetupPage()); + await tester.pumpAndSettle(); + + expect(find.text('Choose how you want to use the app'), findsOneWidget); + }); + + testWidgets('should display app icon', (tester) async { + await tester.pumpWidget(buildSetupPage()); + await tester.pumpAndSettle(); + + expect(find.byIcon(Icons.task_alt), findsOneWidget); + }); + + testWidgets('should display local mode option', (tester) async { + await tester.pumpWidget(buildSetupPage()); + await tester.pumpAndSettle(); + + expect(find.text('Use Locally'), findsOneWidget); + expect(find.text('Keep tasks on this device only. No account needed.'), findsOneWidget); + expect(find.byIcon(Icons.smartphone), findsOneWidget); + }); + + testWidgets('should display online mode option', (tester) async { + await tester.pumpWidget(buildSetupPage()); + await tester.pumpAndSettle(); + + expect(find.text('Sync Online'), findsOneWidget); + expect(find.text('Create an account to sync across devices.'), findsOneWidget); + expect(find.byIcon(Icons.cloud_sync), findsOneWidget); + }); + + // Test ViewModel behavior directly since widget tests with navigation + // cause timing issues with widget disposal + test('ViewModel should set app mode to local correctly', () async { + await settingsViewModel.setAppMode(AppMode.local); + expect(settingsViewModel.appMode, AppMode.local); + }); + + test('ViewModel should set setup completed correctly', () async { + await settingsViewModel.setSetupCompleted(true); + expect(settingsViewModel.setupCompleted, true); + }); + + test('ViewModel should set app mode to online correctly', () async { + await settingsViewModel.setAppMode(AppMode.online); + expect(settingsViewModel.appMode, AppMode.online); + }); + + testWidgets('should have tappable local mode card', (tester) async { + await tester.pumpWidget(buildSetupPage()); + await tester.pumpAndSettle(); + + // Verify the InkWell is present and tappable + final localCard = find.ancestor( + of: find.text('Use Locally'), + matching: find.byType(InkWell), + ); + expect(localCard, findsOneWidget); + }); + + testWidgets('should have tappable online mode card', (tester) async { + await tester.pumpWidget(buildSetupPage()); + await tester.pumpAndSettle(); + + final onlineCard = find.ancestor( + of: find.text('Sync Online'), + matching: find.byType(InkWell), + ); + expect(onlineCard, findsOneWidget); + }); + + testWidgets('should display two mode cards', (tester) async { + await tester.pumpWidget(buildSetupPage()); + await tester.pumpAndSettle(); + + expect(find.byType(Card), findsNWidgets(2)); + }); + + testWidgets('should have arrow icons on mode cards', (tester) async { + await tester.pumpWidget(buildSetupPage()); + await tester.pumpAndSettle(); + + expect(find.byIcon(Icons.arrow_forward_ios), findsNWidgets(2)); + }); + }); +}