import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:household_keeper/core/database/database.dart'; import 'package:household_keeper/core/router/router.dart'; import 'package:household_keeper/features/home/domain/calendar_models.dart'; import 'package:household_keeper/features/home/presentation/calendar_providers.dart'; import 'package:household_keeper/features/rooms/presentation/room_providers.dart'; import 'package:household_keeper/features/tasks/domain/effort_level.dart'; import 'package:household_keeper/features/tasks/domain/frequency.dart'; import 'package:household_keeper/features/tasks/domain/task_sort_option.dart'; import 'package:household_keeper/features/tasks/presentation/sort_preference_notifier.dart'; import 'package:household_keeper/l10n/app_localizations.dart'; import 'package:household_keeper/features/home/domain/daily_plan_models.dart'; /// Helper to create a test [Task] with sensible defaults. Task _makeTask({ int id = 1, int roomId = 1, String name = 'Test Task', required DateTime nextDueDate, }) { return Task( id: id, roomId: roomId, name: name, intervalType: IntervalType.weekly, intervalDays: 7, effortLevel: EffortLevel.medium, nextDueDate: nextDueDate, createdAt: DateTime(2026, 1, 1), isActive: true, ); } /// Helper to create a [TaskWithRoom]. TaskWithRoom _makeTaskWithRoom({ int id = 1, int roomId = 1, String taskName = 'Test Task', String roomName = 'Kueche', required DateTime nextDueDate, }) { return TaskWithRoom( task: _makeTask( id: id, roomId: roomId, name: taskName, nextDueDate: nextDueDate, ), roomName: roomName, roomId: roomId, ); } /// Build the app with calendarDayProvider overridden to the given state. /// /// Uses [UncontrolledProviderScope] with a [ProviderContainer] to avoid /// the riverpod_lint scoped_providers_should_specify_dependencies warning. Widget _buildApp(CalendarDayState dayState) { final container = ProviderContainer(overrides: [ calendarDayProvider.overrideWith( (ref) => Stream.value(dayState), ), selectedDateProvider.overrideWith(SelectedDateNotifier.new), roomWithStatsListProvider.overrideWith( (ref) => Stream.value([]), ), sortPreferenceProvider.overrideWith(SortPreferenceNotifier.new), ]); return UncontrolledProviderScope( container: container, child: MaterialApp.router( routerConfig: router, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: const [Locale('de')], locale: const Locale('de'), ), ); } void main() { setUp(() { SharedPreferences.setMockInitialValues({}); }); final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final yesterday = today.subtract(const Duration(days: 1)); group('HomeScreen empty states', () { testWidgets('shows no-tasks empty state when no tasks exist at all', (tester) async { await tester.pumpWidget(_buildApp(CalendarDayState( selectedDate: today, dayTasks: const [], overdueTasks: const [], totalTaskCount: 0, ))); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // Should show "Noch keine Aufgaben angelegt" (dailyPlanNoTasks) expect(find.text('Noch keine Aufgaben angelegt'), findsOneWidget); // Should show action button to create a room expect(find.text('Raum erstellen'), findsOneWidget); }); testWidgets('shows celebration state when tasks exist but today is clear', (tester) async { await tester.pumpWidget(_buildApp(CalendarDayState( selectedDate: today, dayTasks: const [], overdueTasks: const [], totalTaskCount: 5, // tasks exist elsewhere ))); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // Should show celebration state expect(find.byIcon(Icons.celebration_outlined), findsOneWidget); expect(find.text('Alles erledigt! \u{1F31F}'), findsOneWidget); }); testWidgets('shows empty-day state for non-today date with no tasks', (tester) async { final tomorrow = today.add(const Duration(days: 1)); await tester.pumpWidget(_buildApp(CalendarDayState( selectedDate: tomorrow, dayTasks: const [], overdueTasks: const [], totalTaskCount: 5, // tasks exist on other days ))); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // Should show "Keine Aufgaben" (not celebration — not today) expect(find.text('Keine Aufgaben'), findsOneWidget); expect(find.byIcon(Icons.event_available), findsOneWidget); }); }); group('HomeScreen normal state', () { testWidgets('shows overdue section when overdue tasks exist (today)', (tester) async { await tester.pumpWidget(_buildApp(CalendarDayState( selectedDate: today, dayTasks: [ _makeTaskWithRoom( id: 2, taskName: 'Staubsaugen', roomName: 'Wohnzimmer', nextDueDate: today, ), ], overdueTasks: [ _makeTaskWithRoom( id: 1, taskName: 'Boden wischen', roomName: 'Kueche', nextDueDate: yesterday, ), ], totalTaskCount: 2, ))); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // Should show overdue section header expect(find.text('\u00dcberf\u00e4llig'), findsOneWidget); // Should show both tasks expect(find.text('Boden wischen'), findsOneWidget); expect(find.text('Staubsaugen'), findsOneWidget); // Should show room name tags expect(find.text('Kueche'), findsOneWidget); expect(find.text('Wohnzimmer'), findsOneWidget); }); testWidgets('does not show overdue section for non-today date', (tester) async { // On a future date, overdueTasks will be empty (calendarDayProvider // only populates overdueTasks when isToday). final tomorrow = today.add(const Duration(days: 1)); await tester.pumpWidget(_buildApp(CalendarDayState( selectedDate: tomorrow, dayTasks: [ _makeTaskWithRoom( id: 1, taskName: 'Staubsaugen', roomName: 'Wohnzimmer', nextDueDate: tomorrow, ), ], overdueTasks: const [], // No overdue for non-today totalTaskCount: 1, ))); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // Should NOT show overdue section header expect(find.text('\u00dcberf\u00e4llig'), findsNothing); // Should show day task expect(find.text('Staubsaugen'), findsOneWidget); }); testWidgets('tasks have checkboxes', (tester) async { await tester.pumpWidget(_buildApp(CalendarDayState( selectedDate: today, dayTasks: [ _makeTaskWithRoom( id: 1, taskName: 'Staubsaugen', roomName: 'Wohnzimmer', nextDueDate: today, ), ], overdueTasks: const [], totalTaskCount: 1, ))); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // Task should have a checkbox expect(find.byType(Checkbox), findsOneWidget); }); testWidgets('calendar strip is shown', (tester) async { await tester.pumpWidget(_buildApp(CalendarDayState( selectedDate: today, dayTasks: const [], overdueTasks: const [], totalTaskCount: 0, ))); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // The strip is a horizontal ListView — verify it exists by finding // ListView widgets (strip + potentially the task list). expect(find.byType(ListView), findsWidgets); }); }); group('HomeScreen sort dropdown', () { testWidgets('shows sort dropdown in AppBar', (tester) async { await tester.pumpWidget(_buildApp(CalendarDayState( selectedDate: today, dayTasks: const [], overdueTasks: const [], totalTaskCount: 0, ))); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // SortDropdown wraps a PopupMenuButton expect( find.byType(PopupMenuButton), findsOneWidget, ); }); testWidgets('shows AppBar with title', (tester) async { await tester.pumpWidget(_buildApp(CalendarDayState( selectedDate: today, dayTasks: const [], overdueTasks: const [], totalTaskCount: 0, ))); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); // tabHome l10n string is 'Übersicht'. It appears in the AppBar title // and also in the bottom navigation bar label — use findsWidgets. expect(find.text('\u00dcbersicht'), findsWidgets); }); }); }