- Test loading tasks with success and error states - Test priority sorting (high > medium > low) - Test filter functionality (all, active, completed) - Test task toggle and delete operations - Test date navigation (previousDay, nextDay) - Use mocktail for repository mocking
261 lines
8.2 KiB
Dart
261 lines
8.2 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:mocktail/mocktail.dart';
|
|
|
|
import 'package:agenda_tasks/core/errors/failures.dart';
|
|
import 'package:agenda_tasks/core/errors/result.dart';
|
|
import 'package:agenda_tasks/core/logging/app_logger.dart';
|
|
import 'package:agenda_tasks/features/tasks/domain/entities/task_entity.dart';
|
|
import 'package:agenda_tasks/features/tasks/domain/enums/priority.dart';
|
|
import 'package:agenda_tasks/features/tasks/domain/repositories/task_repository.dart';
|
|
import 'package:agenda_tasks/features/tasks/presentation/viewmodels/daily_tasks_viewmodel.dart';
|
|
|
|
// Mock classes
|
|
class MockTaskRepository extends Mock implements TaskRepository {}
|
|
|
|
class MockAppLogger extends Mock implements AppLogger {}
|
|
|
|
void main() {
|
|
late DailyTasksViewModel viewModel;
|
|
late MockTaskRepository mockRepository;
|
|
late MockAppLogger mockLogger;
|
|
|
|
setUp(() {
|
|
mockRepository = MockTaskRepository();
|
|
mockLogger = MockAppLogger();
|
|
viewModel = DailyTasksViewModel(
|
|
repository: mockRepository,
|
|
logger: mockLogger,
|
|
);
|
|
});
|
|
|
|
group('DailyTasksViewModel', () {
|
|
final testDate = DateTime(2024, 1, 15);
|
|
|
|
// Helper function to create fresh test data for each test
|
|
List<TaskEntity> createTestTasks() => [
|
|
TaskEntity(
|
|
id: '1',
|
|
title: 'Task 1',
|
|
date: testDate,
|
|
priority: Priority.high,
|
|
isDone: false,
|
|
),
|
|
TaskEntity(
|
|
id: '2',
|
|
title: 'Task 2',
|
|
date: testDate,
|
|
priority: Priority.medium,
|
|
isDone: true,
|
|
),
|
|
TaskEntity(
|
|
id: '3',
|
|
title: 'Task 3',
|
|
date: testDate,
|
|
priority: Priority.low,
|
|
isDone: false,
|
|
),
|
|
];
|
|
|
|
group('loadTasks', () {
|
|
test('should set status to loading then success when tasks loaded', () async {
|
|
// Arrange
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success(createTestTasks()));
|
|
|
|
// Act
|
|
final future = viewModel.loadTasks();
|
|
|
|
// Assert - initial state should be loading
|
|
expect(viewModel.status, equals(TasksStatus.loading));
|
|
|
|
await future;
|
|
|
|
// After completion
|
|
expect(viewModel.status, equals(TasksStatus.success));
|
|
expect(viewModel.tasks.length, equals(3));
|
|
});
|
|
|
|
test('should set status to error when loading fails', () async {
|
|
// Arrange
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Error(const NetworkFailure()));
|
|
|
|
// Act
|
|
await viewModel.loadTasks();
|
|
|
|
// Assert
|
|
expect(viewModel.status, equals(TasksStatus.error));
|
|
expect(viewModel.failure, isA<NetworkFailure>());
|
|
});
|
|
|
|
test('should sort tasks by priority (high > medium > low)', () async {
|
|
// Arrange
|
|
final unsortedTasks = [
|
|
TaskEntity(id: '1', title: 'Low', date: testDate, priority: Priority.low, isDone: false),
|
|
TaskEntity(id: '2', title: 'High', date: testDate, priority: Priority.high, isDone: false),
|
|
TaskEntity(id: '3', title: 'Medium', date: testDate, priority: Priority.medium, isDone: false),
|
|
];
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success(unsortedTasks));
|
|
|
|
// Act
|
|
await viewModel.loadTasks();
|
|
|
|
// Assert
|
|
expect(viewModel.tasks[0].priority, equals(Priority.high));
|
|
expect(viewModel.tasks[1].priority, equals(Priority.medium));
|
|
expect(viewModel.tasks[2].priority, equals(Priority.low));
|
|
});
|
|
});
|
|
|
|
group('setFilter', () {
|
|
setUp(() async {
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success(createTestTasks()));
|
|
await viewModel.loadTasks();
|
|
});
|
|
|
|
test('should show all tasks when filter is all', () {
|
|
// Act
|
|
viewModel.setFilter(TaskFilter.all);
|
|
|
|
// Assert
|
|
expect(viewModel.filter, equals(TaskFilter.all));
|
|
expect(viewModel.tasks.length, equals(3));
|
|
});
|
|
|
|
test('should show only active tasks when filter is active', () {
|
|
// Act
|
|
viewModel.setFilter(TaskFilter.active);
|
|
|
|
// Assert
|
|
expect(viewModel.filter, equals(TaskFilter.active));
|
|
expect(viewModel.tasks.length, equals(2));
|
|
expect(viewModel.tasks.every((t) => !t.isDone), isTrue);
|
|
});
|
|
|
|
test('should show only completed tasks when filter is completed', () {
|
|
// Act
|
|
viewModel.setFilter(TaskFilter.completed);
|
|
|
|
// Assert
|
|
expect(viewModel.filter, equals(TaskFilter.completed));
|
|
expect(viewModel.tasks.length, equals(1));
|
|
expect(viewModel.tasks.every((t) => t.isDone), isTrue);
|
|
});
|
|
});
|
|
|
|
group('toggleTask', () {
|
|
test('should update task in list when toggle succeeds', () async {
|
|
// Arrange
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success(createTestTasks()));
|
|
await viewModel.loadTasks();
|
|
|
|
final toggledTask = createTestTasks()[0].copyWith(isDone: true);
|
|
when(() => mockRepository.toggleTaskStatus('1'))
|
|
.thenAnswer((_) async => Success(toggledTask));
|
|
|
|
// Act
|
|
await viewModel.toggleTask('1');
|
|
|
|
// Assert
|
|
final updatedTask = viewModel.tasks.firstWhere((t) => t.id == '1');
|
|
expect(updatedTask.isDone, isTrue);
|
|
});
|
|
|
|
test('should set failure when toggle fails', () async {
|
|
// Arrange
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success(createTestTasks()));
|
|
await viewModel.loadTasks();
|
|
|
|
when(() => mockRepository.toggleTaskStatus('1'))
|
|
.thenAnswer((_) async => Error(const ServerFailure(message: 'Error')));
|
|
|
|
// Act
|
|
await viewModel.toggleTask('1');
|
|
|
|
// Assert
|
|
expect(viewModel.failure, isA<ServerFailure>());
|
|
});
|
|
});
|
|
|
|
group('deleteTask', () {
|
|
test('should remove task from list when delete succeeds', () async {
|
|
// Arrange
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success(createTestTasks()));
|
|
await viewModel.loadTasks();
|
|
|
|
when(() => mockRepository.deleteTask('1'))
|
|
.thenAnswer((_) async => const Success(null));
|
|
|
|
// Act
|
|
await viewModel.deleteTask('1');
|
|
|
|
// Assert
|
|
expect(viewModel.tasks.any((t) => t.id == '1'), isFalse);
|
|
expect(viewModel.totalCount, equals(2));
|
|
});
|
|
});
|
|
|
|
group('date navigation', () {
|
|
test('should update selectedDate when setSelectedDate is called', () {
|
|
// Arrange
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success([]));
|
|
|
|
// Act
|
|
viewModel.setSelectedDate(testDate);
|
|
|
|
// Assert
|
|
expect(viewModel.selectedDate.year, equals(2024));
|
|
expect(viewModel.selectedDate.month, equals(1));
|
|
expect(viewModel.selectedDate.day, equals(15));
|
|
});
|
|
|
|
test('previousDay should subtract one day', () {
|
|
// Arrange
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success([]));
|
|
viewModel.setSelectedDate(testDate);
|
|
|
|
// Act
|
|
viewModel.previousDay();
|
|
|
|
// Assert
|
|
expect(viewModel.selectedDate.day, equals(14));
|
|
});
|
|
|
|
test('nextDay should add one day', () {
|
|
// Arrange
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success([]));
|
|
viewModel.setSelectedDate(testDate);
|
|
|
|
// Act
|
|
viewModel.nextDay();
|
|
|
|
// Assert
|
|
expect(viewModel.selectedDate.day, equals(16));
|
|
});
|
|
});
|
|
|
|
group('counts', () {
|
|
test('should return correct total and completed counts', () async {
|
|
// Arrange
|
|
when(() => mockRepository.getTasksByDate(any()))
|
|
.thenAnswer((_) async => Success(createTestTasks()));
|
|
|
|
// Act
|
|
await viewModel.loadTasks();
|
|
|
|
// Assert
|
|
expect(viewModel.totalCount, equals(3));
|
|
expect(viewModel.completedCount, equals(1));
|
|
});
|
|
});
|
|
});
|
|
}
|