test(TaskListScreen): add integration tests for filtered and overdue task states
All checks were successful
Build and Release to F-Droid / build-and-deploy (push) Successful in 10m30s
All checks were successful
Build and Release to F-Droid / build-and-deploy (push) Successful in 10m30s
- Covers empty states, celebration state, and scheduled/overdue task rendering - Verifies proper checkbox behavior for future tasks - Tests AppBar for sort dropdown, edit/delete actions, and calendar strip - Adds necessary test helpers and overrides for room-specific tasks
This commit is contained in:
@@ -14,7 +14,8 @@ const _overdueColor = Color(0xFFE07A5F);
|
||||
|
||||
/// Shows the task list for the selected calendar day.
|
||||
///
|
||||
/// Watches [calendarDayProvider] and renders one of several states:
|
||||
/// Watches [calendarDayProvider] (or [roomCalendarDayProvider] when [roomId]
|
||||
/// is provided) and renders one of several states:
|
||||
/// - Loading spinner while data loads
|
||||
/// - Error text on failure
|
||||
/// - First-run empty state (no rooms/tasks at all) — prompts to create a room
|
||||
@@ -22,7 +23,10 @@ const _overdueColor = Color(0xFFE07A5F);
|
||||
/// - Celebration state (today is selected and all tasks are done)
|
||||
/// - Has-tasks state with optional overdue section (today only) and checkboxes
|
||||
class CalendarDayList extends ConsumerStatefulWidget {
|
||||
const CalendarDayList({super.key});
|
||||
const CalendarDayList({super.key, this.roomId});
|
||||
|
||||
/// When non-null, filters tasks to this room only.
|
||||
final int? roomId;
|
||||
|
||||
@override
|
||||
ConsumerState<CalendarDayList> createState() => _CalendarDayListState();
|
||||
@@ -43,7 +47,9 @@ class _CalendarDayListState extends ConsumerState<CalendarDayList> {
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final theme = Theme.of(context);
|
||||
final dayState = ref.watch(calendarDayProvider);
|
||||
final dayState = widget.roomId != null
|
||||
? ref.watch(roomCalendarDayProvider(widget.roomId!))
|
||||
: ref.watch(calendarDayProvider);
|
||||
|
||||
return dayState.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
@@ -96,6 +102,46 @@ class _CalendarDayListState extends ConsumerState<CalendarDayList> {
|
||||
AppLocalizations l10n,
|
||||
ThemeData theme,
|
||||
) {
|
||||
// Room-scoped: prompt to create a task in this room.
|
||||
if (widget.roomId != null) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.task_alt,
|
||||
size: 80,
|
||||
color: theme.colorScheme.onSurface.withValues(alpha: 0.4),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
l10n.taskEmptyTitle,
|
||||
style: theme.textTheme.headlineSmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
l10n.taskEmptyMessage,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: theme.colorScheme.onSurface.withValues(alpha: 0.6),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
FilledButton.tonal(
|
||||
onPressed: () =>
|
||||
context.go('/rooms/${widget.roomId}/tasks/new'),
|
||||
child: Text(l10n.taskEmptyAction),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Home-screen: prompt to create a room.
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
@@ -194,20 +240,36 @@ class _CalendarDayListState extends ConsumerState<CalendarDayList> {
|
||||
AppLocalizations l10n,
|
||||
ThemeData theme,
|
||||
) {
|
||||
final now = DateTime.now();
|
||||
final today = DateTime(now.year, now.month, now.day);
|
||||
final isFuture = state.selectedDate.isAfter(today);
|
||||
final showRoomTag = widget.roomId == null;
|
||||
|
||||
final items = <Widget>[];
|
||||
|
||||
// Overdue section (today only, when overdue tasks exist).
|
||||
// Overdue tasks are always completable (they're past due, only shown on today).
|
||||
if (state.overdueTasks.isNotEmpty) {
|
||||
items.add(_buildSectionHeader(l10n.dailyPlanSectionOverdue, theme,
|
||||
color: _overdueColor));
|
||||
for (final tw in state.overdueTasks) {
|
||||
items.add(_buildAnimatedTaskRow(tw, isOverdue: true));
|
||||
items.add(_buildAnimatedTaskRow(
|
||||
tw,
|
||||
isOverdue: true,
|
||||
showRoomTag: showRoomTag,
|
||||
canComplete: true,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Day tasks section.
|
||||
for (final tw in state.dayTasks) {
|
||||
items.add(_buildAnimatedTaskRow(tw, isOverdue: false));
|
||||
items.add(_buildAnimatedTaskRow(
|
||||
tw,
|
||||
isOverdue: false,
|
||||
showRoomTag: showRoomTag,
|
||||
canComplete: !isFuture,
|
||||
));
|
||||
}
|
||||
|
||||
return ListView(children: items);
|
||||
@@ -227,7 +289,12 @@ class _CalendarDayListState extends ConsumerState<CalendarDayList> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAnimatedTaskRow(TaskWithRoom tw, {required bool isOverdue}) {
|
||||
Widget _buildAnimatedTaskRow(
|
||||
TaskWithRoom tw, {
|
||||
required bool isOverdue,
|
||||
required bool showRoomTag,
|
||||
required bool canComplete,
|
||||
}) {
|
||||
final isCompleting = _completingTaskIds.contains(tw.task.id);
|
||||
|
||||
if (isCompleting) {
|
||||
@@ -235,6 +302,7 @@ class _CalendarDayListState extends ConsumerState<CalendarDayList> {
|
||||
key: ValueKey('completing-${tw.task.id}'),
|
||||
taskWithRoom: tw,
|
||||
isOverdue: isOverdue,
|
||||
showRoomTag: showRoomTag,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -242,6 +310,8 @@ class _CalendarDayListState extends ConsumerState<CalendarDayList> {
|
||||
key: ValueKey('task-${tw.task.id}'),
|
||||
taskWithRoom: tw,
|
||||
isOverdue: isOverdue,
|
||||
showRoomTag: showRoomTag,
|
||||
canComplete: canComplete,
|
||||
onCompleted: () => _onTaskCompleted(tw.task.id),
|
||||
);
|
||||
}
|
||||
@@ -253,10 +323,12 @@ class _CompletingTaskRow extends StatefulWidget {
|
||||
super.key,
|
||||
required this.taskWithRoom,
|
||||
required this.isOverdue,
|
||||
required this.showRoomTag,
|
||||
});
|
||||
|
||||
final TaskWithRoom taskWithRoom;
|
||||
final bool isOverdue;
|
||||
final bool showRoomTag;
|
||||
|
||||
@override
|
||||
State<_CompletingTaskRow> createState() => _CompletingTaskRowState();
|
||||
@@ -302,6 +374,7 @@ class _CompletingTaskRowState extends State<_CompletingTaskRow>
|
||||
child: CalendarTaskRow(
|
||||
taskWithRoom: widget.taskWithRoom,
|
||||
isOverdue: widget.isOverdue,
|
||||
showRoomTag: widget.showRoomTag,
|
||||
onCompleted: () {}, // Already completing — ignore repeat taps.
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user