Files
HouseHoldKeaper/test/features/home/presentation/home_screen_test.dart
Jean-Luc Makiola b2f14dcd97 feat(08-01): add isActive filters to CalendarDao, DailyPlanDao, RoomsDao
- CalendarDao: filter all 6 task queries (watchTasksForDate,
  watchTasksForDateInRoom, watchOverdueTasks, watchOverdueTasksInRoom,
  getTaskCount, getTaskCountInRoom) by isActive=true
- DailyPlanDao: filter all 3 queries (watchAllTasksWithRoomName,
  getOverdueAndTodayTaskCount, getOverdueTaskCount) by isActive=true
- RoomsDao: filter watchRoomWithStats task query by isActive=true
- Update migration test: add schema_v3.dart, test v1->v3 and v2->v3 paths
- Update database_test schemaVersion assertion to expect 3
- Fix test helpers in home_screen_test and task_list_screen_test to pass isActive=true
2026-03-18 20:56:34 +01:00

280 lines
9.1 KiB
Dart

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<TaskSortOption>
expect(
find.byType(PopupMenuButton<TaskSortOption>),
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);
});
});
}