feat: initial commit - Habo habit tracking app
- Complete MVP with Repository Pattern, SQLite storage - Provider + ChangeNotifier state management - Navigation 2.0 with deep link support - Habit CRUD with twoDayRule, notifications, categories - Backup/Restore via JSON - Statistics with streak tracking - Material You theme support - Biometric lock support - Desktop widget support - 27 languages i18n structure - Comprehensive test suite (87/89 passing)
This commit is contained in:
70
test/habits/backup_enhancement_test.dart
Normal file
70
test/habits/backup_enhancement_test.dart
Normal file
@@ -0,0 +1,70 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:habo/habits/habit.dart';
|
||||
import 'package:habo/model/habit_data.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
void main() {
|
||||
group('Backup Enhancement Tests', () {
|
||||
group('Timestamp format', () {
|
||||
test('should use correct timestamp format', () {
|
||||
final now = DateTime(2023, 12, 25, 15, 30, 45);
|
||||
final formatted = DateFormat('yyyy-MM-dd_HH-mm-ss').format(now);
|
||||
expect(formatted, '2023-12-25_15-30-45');
|
||||
});
|
||||
|
||||
test('should handle different times correctly', () {
|
||||
final morning = DateTime(2023, 1, 1, 9, 0, 0);
|
||||
final formatted = DateFormat('yyyy-MM-dd_HH-mm-ss').format(morning);
|
||||
expect(formatted, '2023-01-01_09-00-00');
|
||||
});
|
||||
});
|
||||
|
||||
group('Backup structure', () {
|
||||
test('should create backup with correct structure', () async {
|
||||
final testHabits = [
|
||||
Habit(
|
||||
habitData: HabitData(
|
||||
position: 0,
|
||||
title: 'Test Habit',
|
||||
twoDayRule: true,
|
||||
cue: 'Morning coffee',
|
||||
routine: '10 pushups',
|
||||
reward: 'Feel energized',
|
||||
showReward: true,
|
||||
advanced: true,
|
||||
notification: true,
|
||||
notTime: const TimeOfDay(hour: 8, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List<dynamic>>(),
|
||||
sanction: 'No dessert',
|
||||
showSanction: true,
|
||||
accountant: 'self',
|
||||
),
|
||||
)
|
||||
];
|
||||
|
||||
// Test JSON serialization directly
|
||||
final jsonData = jsonEncode(testHabits);
|
||||
expect(jsonData, isNotEmpty);
|
||||
expect(jsonData, contains('Test Habit'));
|
||||
expect(jsonData, contains('pushups'));
|
||||
|
||||
final restoredHabits = jsonDecode(jsonData);
|
||||
expect(restoredHabits, isList);
|
||||
expect(restoredHabits.length, 1);
|
||||
});
|
||||
|
||||
test('should handle empty habits list', () async {
|
||||
final emptyHabits = <Habit>[];
|
||||
|
||||
// Test JSON serialization directly
|
||||
final jsonData = jsonEncode(emptyHabits);
|
||||
expect(jsonData, isNotEmpty);
|
||||
expect(jsonDecode(jsonData), isEmpty);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
377
test/habits/habits_manager_fixed_test.dart
Normal file
377
test/habits/habits_manager_fixed_test.dart
Normal file
@@ -0,0 +1,377 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:habo/generated/l10n.dart';
|
||||
import 'package:habo/habits/habit.dart';
|
||||
import 'package:habo/habits/habits_manager.dart';
|
||||
import 'package:habo/model/habit_data.dart';
|
||||
import 'package:habo/repositories/category_repository.dart';
|
||||
import 'package:habo/repositories/event_repository.dart';
|
||||
import 'package:habo/repositories/habit_repository.dart';
|
||||
import 'package:habo/services/backup_service.dart';
|
||||
import 'package:habo/services/notification_service.dart';
|
||||
import 'package:habo/services/ui_feedback_service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockHabitRepository extends Mock implements HabitRepository {}
|
||||
|
||||
class MockEventRepository extends Mock implements EventRepository {}
|
||||
|
||||
class MockCategoryRepository extends Mock implements CategoryRepository {}
|
||||
|
||||
class MockBackupService extends Mock implements BackupService {}
|
||||
|
||||
class MockNotificationService extends Mock implements NotificationService {}
|
||||
|
||||
class MockUIFeedbackService extends Mock implements UIFeedbackService {}
|
||||
|
||||
void main() {
|
||||
late HabitsManager habitsManager;
|
||||
late MockHabitRepository mockHabitRepository;
|
||||
late MockEventRepository mockEventRepository;
|
||||
late MockCategoryRepository mockCategoryRepository;
|
||||
late MockBackupService mockBackupService;
|
||||
late MockNotificationService mockNotificationService;
|
||||
late MockUIFeedbackService mockUIFeedbackService;
|
||||
|
||||
setUpAll(() async {
|
||||
// Initialize localization for tests
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
await S.load(const Locale('en'));
|
||||
registerFallbackValue(Habit(
|
||||
habitData: HabitData(
|
||||
position: 0,
|
||||
title: 'Fallback',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
));
|
||||
registerFallbackValue(TimeOfDay.now());
|
||||
registerFallbackValue(Colors.grey);
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockHabitRepository = MockHabitRepository();
|
||||
mockEventRepository = MockEventRepository();
|
||||
mockCategoryRepository = MockCategoryRepository();
|
||||
mockBackupService = MockBackupService();
|
||||
mockNotificationService = MockNotificationService();
|
||||
mockUIFeedbackService = MockUIFeedbackService();
|
||||
|
||||
habitsManager = HabitsManager(
|
||||
habitRepository: mockHabitRepository,
|
||||
eventRepository: mockEventRepository,
|
||||
categoryRepository: mockCategoryRepository,
|
||||
backupService: mockBackupService,
|
||||
notificationService: mockNotificationService,
|
||||
uiFeedbackService: mockUIFeedbackService,
|
||||
);
|
||||
});
|
||||
|
||||
group('HabitsManager Tests', () {
|
||||
test('should initialize with provided repositories', () {
|
||||
expect(habitsManager, isNotNull);
|
||||
});
|
||||
|
||||
test('should populate allHabits from repository', () async {
|
||||
// Arrange
|
||||
final mockHabits = [
|
||||
Habit(
|
||||
habitData: HabitData(
|
||||
id: 1,
|
||||
position: 0,
|
||||
title: 'Test Habit',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
when(() => mockHabitRepository.getAllHabits())
|
||||
.thenAnswer((_) async => mockHabits);
|
||||
|
||||
// Act
|
||||
await habitsManager.initModel();
|
||||
|
||||
// Assert
|
||||
verify(() => mockHabitRepository.getAllHabits()).called(1);
|
||||
});
|
||||
|
||||
group('CRUD Operations', () {
|
||||
test('should add habit', () async {
|
||||
// Arrange
|
||||
|
||||
when(() => mockHabitRepository.createHabit(any()))
|
||||
.thenAnswer((_) async => 1);
|
||||
|
||||
// Act
|
||||
habitsManager.addHabit(
|
||||
'Test Habit',
|
||||
false,
|
||||
'Test cue',
|
||||
'Test routine',
|
||||
'Test reward',
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
const TimeOfDay(hour: 9, minute: 0),
|
||||
'Test sanction',
|
||||
false,
|
||||
'Test accountant',
|
||||
);
|
||||
|
||||
// Assert
|
||||
verify(() => mockHabitRepository.createHabit(any())).called(1);
|
||||
});
|
||||
|
||||
test('should edit habit', () async {
|
||||
// Setup
|
||||
final testHabit = Habit(
|
||||
habitData: HabitData(
|
||||
id: 1,
|
||||
position: 0,
|
||||
title: 'Test Habit',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
);
|
||||
|
||||
// Add habit to internal state
|
||||
habitsManager.allHabits.add(testHabit);
|
||||
when(() => mockHabitRepository.updateHabit(any()))
|
||||
.thenAnswer((_) async {});
|
||||
|
||||
// Act
|
||||
habitsManager.editHabit(testHabit.habitData);
|
||||
|
||||
// Assert
|
||||
verify(() => mockHabitRepository.updateHabit(any())).called(1);
|
||||
});
|
||||
|
||||
test('should delete habit', () async {
|
||||
// Setup
|
||||
final testHabit = Habit(
|
||||
habitData: HabitData(
|
||||
id: 1,
|
||||
position: 0,
|
||||
title: 'Test Habit',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
);
|
||||
|
||||
// Add habit to internal state
|
||||
habitsManager.allHabits.add(testHabit);
|
||||
when(() => mockHabitRepository.deleteHabit(any()))
|
||||
.thenAnswer((_) async {});
|
||||
when(() => mockUIFeedbackService.showMessageWithAction(
|
||||
message: any(named: 'message'),
|
||||
actionLabel: any(named: 'actionLabel'),
|
||||
onActionPressed: any(named: 'onActionPressed'),
|
||||
backgroundColor: any(named: 'backgroundColor'),
|
||||
)).thenReturn(null);
|
||||
|
||||
// Act
|
||||
habitsManager.deleteHabit(1);
|
||||
|
||||
// Assert - verify internal state changes immediately
|
||||
expect(habitsManager.allHabits.length, 0);
|
||||
expect(habitsManager.toDelete.length, 1);
|
||||
});
|
||||
});
|
||||
|
||||
group('Archive Operations', () {
|
||||
test('should archive habit', () async {
|
||||
// Setup
|
||||
final testHabit = Habit(
|
||||
habitData: HabitData(
|
||||
id: 1,
|
||||
position: 0,
|
||||
title: 'Test Habit',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
archived: false,
|
||||
),
|
||||
);
|
||||
|
||||
// Add habit to internal state
|
||||
habitsManager.allHabits.add(testHabit);
|
||||
when(() => mockHabitRepository.updateHabit(any()))
|
||||
.thenAnswer((_) async {});
|
||||
when(() => mockNotificationService.disableHabitNotification(any()))
|
||||
.thenReturn(null);
|
||||
when(() => mockUIFeedbackService.showMessageWithAction(
|
||||
message: any(named: 'message'),
|
||||
actionLabel: any(named: 'actionLabel'),
|
||||
onActionPressed: any(named: 'onActionPressed'),
|
||||
backgroundColor: any(named: 'backgroundColor'),
|
||||
)).thenReturn(null);
|
||||
|
||||
// Act
|
||||
habitsManager.archiveHabit(1);
|
||||
|
||||
// Assert
|
||||
expect(testHabit.habitData.archived, true);
|
||||
verify(() => mockHabitRepository.updateHabit(any())).called(1);
|
||||
verify(() => mockNotificationService.disableHabitNotification(1))
|
||||
.called(1);
|
||||
verify(() => mockUIFeedbackService.showMessageWithAction(
|
||||
message: any(named: 'message'),
|
||||
actionLabel: any(named: 'actionLabel'),
|
||||
onActionPressed: any(named: 'onActionPressed'),
|
||||
backgroundColor: any(named: 'backgroundColor'),
|
||||
)).called(1);
|
||||
});
|
||||
|
||||
test('should unarchive habit', () async {
|
||||
// Setup
|
||||
final testHabit = Habit(
|
||||
habitData: HabitData(
|
||||
id: 1,
|
||||
position: 0,
|
||||
title: 'Test Habit',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: true,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
archived: true,
|
||||
),
|
||||
);
|
||||
|
||||
// Add habit to internal state
|
||||
habitsManager.allHabits.add(testHabit);
|
||||
when(() => mockHabitRepository.updateHabit(any()))
|
||||
.thenAnswer((_) async {});
|
||||
when(() => mockNotificationService.setHabitNotification(
|
||||
any(), any(), any(), any())).thenReturn(null);
|
||||
when(() => mockUIFeedbackService.showSuccess(any())).thenReturn(null);
|
||||
|
||||
// Act
|
||||
habitsManager.unarchiveHabit(1);
|
||||
|
||||
// Assert
|
||||
expect(testHabit.habitData.archived, false);
|
||||
verify(() => mockHabitRepository.updateHabit(any())).called(1);
|
||||
verify(() => mockNotificationService.setHabitNotification(
|
||||
1, any(), 'Habo', 'Test Habit')).called(1);
|
||||
verify(() => mockUIFeedbackService.showSuccess(any())).called(1);
|
||||
});
|
||||
|
||||
test('should filter active habits correctly', () {
|
||||
// Setup
|
||||
final activeHabit = Habit(
|
||||
habitData: HabitData(
|
||||
id: 1,
|
||||
position: 0,
|
||||
title: 'Active Habit',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
archived: false,
|
||||
),
|
||||
);
|
||||
|
||||
final archivedHabit = Habit(
|
||||
habitData: HabitData(
|
||||
id: 2,
|
||||
position: 1,
|
||||
title: 'Archived Habit',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
archived: true,
|
||||
),
|
||||
);
|
||||
|
||||
// Add habits to internal state
|
||||
habitsManager.allHabits.addAll([activeHabit, archivedHabit]);
|
||||
|
||||
// Act & Assert
|
||||
final activeHabits = habitsManager.activeHabits;
|
||||
final archivedHabits = habitsManager.archivedHabits;
|
||||
|
||||
expect(activeHabits.length, 1);
|
||||
expect(activeHabits.first.habitData.title, 'Active Habit');
|
||||
expect(archivedHabits.length, 1);
|
||||
expect(archivedHabits.first.habitData.title, 'Archived Habit');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
157
test/habits/habits_manager_notifications_test.dart
Normal file
157
test/habits/habits_manager_notifications_test.dart
Normal file
@@ -0,0 +1,157 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:habo/habits/habits_manager.dart';
|
||||
import 'package:habo/repositories/habit_repository.dart';
|
||||
import 'package:habo/repositories/event_repository.dart';
|
||||
import 'package:habo/repositories/category_repository.dart';
|
||||
import 'package:habo/services/backup_service.dart';
|
||||
import 'package:habo/services/notification_service.dart';
|
||||
import 'package:habo/services/ui_feedback_service.dart';
|
||||
import 'package:habo/habits/habit.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:habo/model/habit_data.dart';
|
||||
import 'package:habo/constants.dart';
|
||||
|
||||
class MockHabitRepository extends Mock implements HabitRepository {}
|
||||
|
||||
class MockEventRepository extends Mock implements EventRepository {}
|
||||
|
||||
class MockCategoryRepository extends Mock implements CategoryRepository {}
|
||||
|
||||
class MockBackupService extends Mock implements BackupService {}
|
||||
|
||||
class MockNotificationService extends Mock implements NotificationService {}
|
||||
|
||||
class MockUIFeedbackService extends Mock implements UIFeedbackService {}
|
||||
|
||||
void main() {
|
||||
late HabitsManager habitsManager;
|
||||
late MockHabitRepository mockHabitRepository;
|
||||
late MockEventRepository mockEventRepository;
|
||||
late MockCategoryRepository mockCategoryRepository;
|
||||
late MockBackupService mockBackupService;
|
||||
late MockNotificationService mockNotificationService;
|
||||
late MockUIFeedbackService mockUIFeedbackService;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(Habit(
|
||||
habitData: HabitData(
|
||||
position: 0,
|
||||
title: '',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 0, minute: 0),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
));
|
||||
registerFallbackValue(HabitData(
|
||||
position: 0,
|
||||
title: '',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 0, minute: 0),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
));
|
||||
registerFallbackValue(const TimeOfDay(hour: 0, minute: 0));
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockHabitRepository = MockHabitRepository();
|
||||
mockEventRepository = MockEventRepository();
|
||||
mockCategoryRepository = MockCategoryRepository();
|
||||
mockBackupService = MockBackupService();
|
||||
mockNotificationService = MockNotificationService();
|
||||
mockUIFeedbackService = MockUIFeedbackService();
|
||||
|
||||
// Setup mock returns
|
||||
when(() => mockEventRepository.insertEvent(any(), any(), any()))
|
||||
.thenAnswer((_) => Future.value());
|
||||
when(() => mockEventRepository.deleteEvent(any(), any()))
|
||||
.thenAnswer((_) => Future.value());
|
||||
when(() => mockEventRepository.getEventsForHabit(any()))
|
||||
.thenAnswer((_) => Future.value([]));
|
||||
|
||||
habitsManager = HabitsManager(
|
||||
habitRepository: mockHabitRepository,
|
||||
eventRepository: mockEventRepository,
|
||||
categoryRepository: mockCategoryRepository,
|
||||
backupService: mockBackupService,
|
||||
notificationService: mockNotificationService,
|
||||
uiFeedbackService: mockUIFeedbackService,
|
||||
);
|
||||
});
|
||||
|
||||
group('Notification Tests', () {
|
||||
test('should schedule notifications for habits', () async {
|
||||
// Arrange
|
||||
final testHabit = Habit(
|
||||
habitData: HabitData(
|
||||
position: 0,
|
||||
title: 'Test Habit',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
notification: true,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
);
|
||||
|
||||
when(() => mockHabitRepository.getAllHabits())
|
||||
.thenAnswer((_) async => [testHabit]);
|
||||
|
||||
// Act
|
||||
habitsManager.resetNotifications([testHabit]);
|
||||
|
||||
// Assert
|
||||
verify(() => mockNotificationService.resetNotifications(any())).called(1);
|
||||
});
|
||||
|
||||
test('should handle habit event addition', () async {
|
||||
// Arrange
|
||||
final today = DateTime.now();
|
||||
final event = [DayType.check];
|
||||
|
||||
// Act
|
||||
habitsManager.addEvent(1, today, event);
|
||||
|
||||
// Assert
|
||||
verify(() => mockEventRepository.insertEvent(1, today, event)).called(1);
|
||||
});
|
||||
|
||||
test('should handle habit event deletion', () async {
|
||||
// Arrange
|
||||
final today = DateTime.now();
|
||||
|
||||
// Act
|
||||
habitsManager.deleteEvent(1, today);
|
||||
|
||||
// Assert
|
||||
verify(() => mockEventRepository.deleteEvent(1, today)).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
465
test/habits/habits_manager_test.dart
Normal file
465
test/habits/habits_manager_test.dart
Normal file
@@ -0,0 +1,465 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:habo/habits/habit.dart';
|
||||
import 'package:habo/habits/habits_manager.dart';
|
||||
import 'package:habo/model/habit_data.dart';
|
||||
import 'package:habo/repositories/habit_repository.dart';
|
||||
import 'package:habo/repositories/event_repository.dart';
|
||||
import 'package:habo/repositories/category_repository.dart';
|
||||
import 'package:habo/services/backup_service.dart';
|
||||
import 'package:habo/services/notification_service.dart';
|
||||
import 'package:habo/services/ui_feedback_service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockHabitRepository extends Mock implements HabitRepository {}
|
||||
|
||||
class MockEventRepository extends Mock implements EventRepository {}
|
||||
|
||||
class MockCategoryRepository extends Mock implements CategoryRepository {}
|
||||
|
||||
class MockBackupService extends Mock implements BackupService {}
|
||||
|
||||
class MockNotificationService extends Mock implements NotificationService {}
|
||||
|
||||
class MockUIFeedbackService extends Mock implements UIFeedbackService {}
|
||||
|
||||
void main() {
|
||||
late HabitsManager habitsManager;
|
||||
late MockHabitRepository mockHabitRepository;
|
||||
late MockEventRepository mockEventRepository;
|
||||
late MockCategoryRepository mockCategoryRepository;
|
||||
late MockBackupService mockBackupService;
|
||||
late MockNotificationService mockNotificationService;
|
||||
late MockUIFeedbackService mockUIFeedbackService;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(Habit(
|
||||
habitData: HabitData(
|
||||
position: 0,
|
||||
title: '',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 0, minute: 0),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
));
|
||||
registerFallbackValue(HabitData(
|
||||
position: 0,
|
||||
title: '',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 0, minute: 0),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
));
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockHabitRepository = MockHabitRepository();
|
||||
mockEventRepository = MockEventRepository();
|
||||
mockCategoryRepository = MockCategoryRepository();
|
||||
mockBackupService = MockBackupService();
|
||||
mockNotificationService = MockNotificationService();
|
||||
mockUIFeedbackService = MockUIFeedbackService();
|
||||
|
||||
habitsManager = HabitsManager(
|
||||
habitRepository: mockHabitRepository,
|
||||
eventRepository: mockEventRepository,
|
||||
categoryRepository: mockCategoryRepository,
|
||||
backupService: mockBackupService,
|
||||
notificationService: mockNotificationService,
|
||||
uiFeedbackService: mockUIFeedbackService,
|
||||
);
|
||||
});
|
||||
|
||||
group('HabitsManager Tests', () {
|
||||
test('should initialize with provided repositories', () {
|
||||
expect(habitsManager, isNotNull);
|
||||
});
|
||||
|
||||
test('should populate allHabits from repository', () async {
|
||||
// Arrange
|
||||
final mockHabits = [
|
||||
Habit(
|
||||
habitData: HabitData(
|
||||
position: 1,
|
||||
title: 'Test Habit 1',
|
||||
twoDayRule: false,
|
||||
cue: 'Test cue',
|
||||
routine: 'Test routine',
|
||||
reward: 'Test reward',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List<dynamic>>(),
|
||||
sanction: 'Test sanction',
|
||||
showSanction: false,
|
||||
accountant: 'Test accountant',
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
when(() => mockHabitRepository.getAllHabits())
|
||||
.thenAnswer((_) async => mockHabits);
|
||||
|
||||
// Act
|
||||
await habitsManager.initModel();
|
||||
|
||||
// Assert
|
||||
verify(() => mockHabitRepository.getAllHabits()).called(1);
|
||||
expect(habitsManager.allHabits.length, 1);
|
||||
expect(habitsManager.allHabits[0].habitData.title, 'Test Habit 1');
|
||||
});
|
||||
|
||||
test('should handle empty habits list', () async {
|
||||
// Arrange
|
||||
when(() => mockHabitRepository.getAllHabits())
|
||||
.thenAnswer((_) async => []);
|
||||
|
||||
// Act
|
||||
await habitsManager.initModel();
|
||||
|
||||
// Assert
|
||||
expect(habitsManager.allHabits, isEmpty);
|
||||
});
|
||||
|
||||
group('CRUD Operations', () {
|
||||
setUp(() async {
|
||||
// Setup initial state with empty habits
|
||||
when(() => mockHabitRepository.getAllHabits())
|
||||
.thenAnswer((_) async => []);
|
||||
when(() => mockHabitRepository.createHabit(any()))
|
||||
.thenAnswer((_) async => 1);
|
||||
await habitsManager.initModel();
|
||||
});
|
||||
|
||||
group('Create Operations', () {
|
||||
test('should add a new habit', () async {
|
||||
// Arrange
|
||||
const testTitle = 'Test Habit';
|
||||
const testCue = 'Test cue';
|
||||
const testRoutine = 'Test routine';
|
||||
const testReward = 'Test reward';
|
||||
const testSanction = 'Test sanction';
|
||||
const testAccountant = 'Test accountant';
|
||||
const testTime = TimeOfDay(hour: 9, minute: 0);
|
||||
|
||||
when(() => mockHabitRepository.createHabit(any()))
|
||||
.thenAnswer((_) async => 1);
|
||||
|
||||
// Act
|
||||
habitsManager.addHabit(
|
||||
testTitle,
|
||||
false, // twoDayRule
|
||||
testCue,
|
||||
testRoutine,
|
||||
testReward,
|
||||
false, // showReward
|
||||
false, // advanced
|
||||
false, // notification
|
||||
testTime,
|
||||
testSanction,
|
||||
false, // showSanction
|
||||
testAccountant,
|
||||
);
|
||||
await Future.delayed(
|
||||
Duration.zero); // Allow async operations to complete
|
||||
|
||||
// Assert
|
||||
expect(habitsManager.allHabits.length, 1);
|
||||
expect(habitsManager.allHabits[0].habitData.title, testTitle);
|
||||
expect(habitsManager.allHabits[0].habitData.cue, testCue);
|
||||
expect(habitsManager.allHabits[0].habitData.routine, testRoutine);
|
||||
verify(() => mockHabitRepository.createHabit(any())).called(1);
|
||||
});
|
||||
|
||||
test('should add habit with correct position', () async {
|
||||
// Arrange
|
||||
when(() => mockHabitRepository.createHabit(any()))
|
||||
.thenAnswer((_) async => 1);
|
||||
|
||||
// Act - Add first habit
|
||||
habitsManager.addHabit('First Habit', false, '', '', '', false, false,
|
||||
false, const TimeOfDay(hour: 9, minute: 0), '', false, '');
|
||||
await Future.delayed(Duration.zero);
|
||||
|
||||
// Manually set ID for testing
|
||||
habitsManager.allHabits[0].habitData.id = 1;
|
||||
|
||||
// Act - Add second habit
|
||||
habitsManager.addHabit('Second Habit', false, '', '', '', false,
|
||||
false, false, const TimeOfDay(hour: 9, minute: 0), '', false, '');
|
||||
await Future.delayed(Duration.zero);
|
||||
|
||||
// Manually set ID for testing
|
||||
habitsManager.allHabits[1].habitData.id = 2;
|
||||
|
||||
// Assert
|
||||
expect(habitsManager.allHabits.length, 2);
|
||||
expect(habitsManager.allHabits[0].habitData.position, 0);
|
||||
expect(habitsManager.allHabits[1].habitData.position, 1);
|
||||
});
|
||||
});
|
||||
|
||||
group('Read Operations', () {
|
||||
setUp(() async {
|
||||
// Add some test habits
|
||||
when(() => mockHabitRepository.createHabit(any()))
|
||||
.thenAnswer((_) async => 1);
|
||||
|
||||
habitsManager.addHabit('Habit 1', false, '', '', '', false, false,
|
||||
false, const TimeOfDay(hour: 9, minute: 0), '', false, '');
|
||||
await Future.delayed(Duration.zero);
|
||||
habitsManager.allHabits[0].habitData.id = 1;
|
||||
|
||||
habitsManager.addHabit('Habit 2', false, '', '', '', false, false,
|
||||
false, const TimeOfDay(hour: 9, minute: 0), '', false, '');
|
||||
await Future.delayed(Duration.zero);
|
||||
habitsManager.allHabits[1].habitData.id = 2;
|
||||
});
|
||||
|
||||
test('should find habit by id', () {
|
||||
// Act
|
||||
final habit = habitsManager.findHabitById(1);
|
||||
|
||||
// Assert
|
||||
expect(habit, isNotNull);
|
||||
expect(habit!.habitData.title, 'Habit 1');
|
||||
});
|
||||
|
||||
test('should return null for non-existent habit id', () {
|
||||
// Act
|
||||
final habit = habitsManager.findHabitById(999);
|
||||
|
||||
// Assert
|
||||
expect(habit, isNull);
|
||||
});
|
||||
|
||||
test('should get habit name by id', () {
|
||||
// Act
|
||||
final name = habitsManager.getNameOfHabit(1);
|
||||
|
||||
// Assert
|
||||
expect(name, 'Habit 1');
|
||||
});
|
||||
|
||||
test('should return empty string for non-existent habit name', () {
|
||||
// Act
|
||||
final name = habitsManager.getNameOfHabit(999);
|
||||
|
||||
// Assert
|
||||
expect(name, '');
|
||||
});
|
||||
});
|
||||
|
||||
group('Update Operations', () {
|
||||
late Habit testHabit;
|
||||
|
||||
setUp(() async {
|
||||
// Setup a test habit
|
||||
when(() => mockHabitRepository.createHabit(any()))
|
||||
.thenAnswer((_) async => 1);
|
||||
when(() => mockHabitRepository.updateHabit(any()))
|
||||
.thenAnswer((_) async {});
|
||||
when(() => mockHabitRepository.getAllHabits())
|
||||
.thenAnswer((_) async => [testHabit]);
|
||||
|
||||
habitsManager.addHabit(
|
||||
'Original Title',
|
||||
false,
|
||||
'Original cue',
|
||||
'Original routine',
|
||||
'Original reward',
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
const TimeOfDay(hour: 9, minute: 0),
|
||||
'Original sanction',
|
||||
false,
|
||||
'Original accountant');
|
||||
await Future.delayed(Duration.zero);
|
||||
|
||||
testHabit = habitsManager.allHabits[0];
|
||||
testHabit.habitData.id = 1;
|
||||
});
|
||||
|
||||
test('should edit existing habit', () async {
|
||||
// Arrange
|
||||
final updatedData = HabitData(
|
||||
position: testHabit.habitData.position,
|
||||
title: 'Updated Title',
|
||||
twoDayRule: true,
|
||||
cue: 'Updated cue',
|
||||
routine: 'Updated routine',
|
||||
reward: 'Updated reward',
|
||||
showReward: true,
|
||||
advanced: true,
|
||||
notification: true,
|
||||
notTime: const TimeOfDay(hour: 10, minute: 30),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: 'Updated sanction',
|
||||
showSanction: true,
|
||||
accountant: 'Updated accountant',
|
||||
);
|
||||
updatedData.id = 1;
|
||||
|
||||
// Act
|
||||
habitsManager.editHabit(updatedData);
|
||||
|
||||
// Assert
|
||||
expect(habitsManager.allHabits.length, 1);
|
||||
expect(habitsManager.allHabits[0].habitData.title, 'Updated Title');
|
||||
expect(habitsManager.allHabits[0].habitData.twoDayRule, true);
|
||||
expect(habitsManager.allHabits[0].habitData.cue, 'Updated cue');
|
||||
verify(() => mockHabitRepository.updateHabit(any())).called(1);
|
||||
});
|
||||
|
||||
test('should update habit notification settings', () async {
|
||||
// Arrange
|
||||
final updatedData = HabitData(
|
||||
position: testHabit.habitData.position,
|
||||
title: testHabit.habitData.title,
|
||||
twoDayRule: testHabit.habitData.twoDayRule,
|
||||
cue: testHabit.habitData.cue,
|
||||
routine: testHabit.habitData.routine,
|
||||
reward: testHabit.habitData.reward,
|
||||
showReward: testHabit.habitData.showReward,
|
||||
advanced: testHabit.habitData.advanced,
|
||||
notification: true, // Changed from false to true
|
||||
notTime: const TimeOfDay(hour: 15, minute: 45),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: testHabit.habitData.sanction,
|
||||
showSanction: testHabit.habitData.showSanction,
|
||||
accountant: testHabit.habitData.accountant,
|
||||
);
|
||||
updatedData.id = 1;
|
||||
|
||||
// Act
|
||||
habitsManager.editHabit(updatedData);
|
||||
|
||||
// Assert
|
||||
expect(habitsManager.allHabits[0].habitData.notification, true);
|
||||
expect(habitsManager.allHabits[0].habitData.notTime.hour, 15);
|
||||
expect(habitsManager.allHabits[0].habitData.notTime.minute, 45);
|
||||
});
|
||||
});
|
||||
|
||||
group('Delete Operations', () {
|
||||
late Habit testHabit;
|
||||
|
||||
setUp(() async {
|
||||
// Setup a test habit
|
||||
when(() => mockHabitRepository.createHabit(any()))
|
||||
.thenAnswer((_) async => 1);
|
||||
when(() => mockHabitRepository.deleteHabit(any()))
|
||||
.thenAnswer((_) async {});
|
||||
|
||||
habitsManager.addHabit('Test Habit', false, '', '', '', false, false,
|
||||
false, const TimeOfDay(hour: 9, minute: 0), '', false, '');
|
||||
await Future.delayed(Duration.zero);
|
||||
|
||||
testHabit = habitsManager.allHabits[0];
|
||||
testHabit.habitData.id = 1;
|
||||
});
|
||||
|
||||
test('should delete habit', () async {
|
||||
// Act - Simulate the core deletion logic
|
||||
final habitToDelete = habitsManager.findHabitById(1);
|
||||
expect(habitToDelete, isNotNull);
|
||||
|
||||
habitsManager.allHabits.remove(habitToDelete);
|
||||
habitsManager.updateOrder();
|
||||
|
||||
// Assert
|
||||
expect(habitsManager.allHabits.length, 0);
|
||||
expect(habitsManager.findHabitById(1), isNull);
|
||||
});
|
||||
|
||||
test('should undo delete habit', () async {
|
||||
// Arrange - Simulate deletion
|
||||
final deletedHabit = habitsManager.findHabitById(1);
|
||||
habitsManager.allHabits.remove(deletedHabit);
|
||||
|
||||
// Act - Undo
|
||||
habitsManager.allHabits.insert(0, deletedHabit!);
|
||||
habitsManager.updateOrder();
|
||||
|
||||
// Assert
|
||||
expect(habitsManager.allHabits.length, 1);
|
||||
expect(habitsManager.allHabits[0], deletedHabit);
|
||||
});
|
||||
});
|
||||
|
||||
group('Utility Methods', () {
|
||||
setUp(() async {
|
||||
// Add some test habits
|
||||
when(() => mockHabitRepository.createHabit(any()))
|
||||
.thenAnswer((_) async => 1);
|
||||
|
||||
habitsManager.addHabit('First Habit', false, '', '', '', false, false,
|
||||
false, const TimeOfDay(hour: 9, minute: 0), '', false, '');
|
||||
await Future.delayed(Duration.zero);
|
||||
habitsManager.allHabits[0].habitData.id = 1;
|
||||
|
||||
habitsManager.addHabit('Second Habit', false, '', '', '', false,
|
||||
false, false, const TimeOfDay(hour: 9, minute: 0), '', false, '');
|
||||
await Future.delayed(Duration.zero);
|
||||
habitsManager.allHabits[1].habitData.id = 2;
|
||||
|
||||
habitsManager.addHabit('Third Habit', false, '', '', '', false, false,
|
||||
false, const TimeOfDay(hour: 9, minute: 0), '', false, '');
|
||||
await Future.delayed(Duration.zero);
|
||||
habitsManager.allHabits[2].habitData.id = 3;
|
||||
});
|
||||
|
||||
test('should update habit positions correctly', () async {
|
||||
// Act - Simulate deletion of middle habit
|
||||
final habitToDelete = habitsManager.findHabitById(2);
|
||||
habitsManager.allHabits.remove(habitToDelete);
|
||||
habitsManager.updateOrder();
|
||||
|
||||
// Assert positions are updated
|
||||
expect(habitsManager.allHabits.length, 2);
|
||||
expect(habitsManager.allHabits[0].habitData.position, 0);
|
||||
expect(habitsManager.allHabits[1].habitData.position, 1);
|
||||
});
|
||||
|
||||
test('should maintain correct positions after undo', () async {
|
||||
// Arrange
|
||||
final deletedHabit = habitsManager.allHabits[1];
|
||||
|
||||
// Act - Simulate delete and undo
|
||||
habitsManager.allHabits.remove(deletedHabit);
|
||||
habitsManager.updateOrder();
|
||||
|
||||
habitsManager.allHabits.insert(1, deletedHabit);
|
||||
habitsManager.updateOrder();
|
||||
|
||||
// Assert positions are correct
|
||||
expect(habitsManager.allHabits.length, 3);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
expect(habitsManager.allHabits[i].habitData.position, i);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
224
test/habits/habits_manager_updated_test.dart
Normal file
224
test/habits/habits_manager_updated_test.dart
Normal file
@@ -0,0 +1,224 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:habo/habits/habit.dart';
|
||||
import 'package:habo/habits/habits_manager.dart';
|
||||
import 'package:habo/model/habit_data.dart';
|
||||
import 'package:habo/repositories/habit_repository.dart';
|
||||
import 'package:habo/repositories/event_repository.dart';
|
||||
import 'package:habo/repositories/category_repository.dart';
|
||||
import 'package:habo/services/backup_service.dart';
|
||||
import 'package:habo/services/notification_service.dart';
|
||||
import 'package:habo/services/ui_feedback_service.dart';
|
||||
import 'package:habo/generated/l10n.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockHabitRepository extends Mock implements HabitRepository {}
|
||||
|
||||
class MockEventRepository extends Mock implements EventRepository {}
|
||||
|
||||
class MockCategoryRepository extends Mock implements CategoryRepository {}
|
||||
|
||||
class MockBackupService extends Mock implements BackupService {}
|
||||
|
||||
class MockNotificationService extends Mock implements NotificationService {}
|
||||
|
||||
class MockUIFeedbackService extends Mock implements UIFeedbackService {}
|
||||
|
||||
void main() {
|
||||
late HabitsManager habitsManager;
|
||||
late MockHabitRepository mockHabitRepository;
|
||||
late MockEventRepository mockEventRepository;
|
||||
late MockCategoryRepository mockCategoryRepository;
|
||||
late MockBackupService mockBackupService;
|
||||
late MockNotificationService mockNotificationService;
|
||||
late MockUIFeedbackService mockUIFeedbackService;
|
||||
|
||||
setUp(() {
|
||||
mockHabitRepository = MockHabitRepository();
|
||||
mockEventRepository = MockEventRepository();
|
||||
mockCategoryRepository = MockCategoryRepository();
|
||||
mockBackupService = MockBackupService();
|
||||
mockNotificationService = MockNotificationService();
|
||||
mockUIFeedbackService = MockUIFeedbackService();
|
||||
|
||||
habitsManager = HabitsManager(
|
||||
habitRepository: mockHabitRepository,
|
||||
eventRepository: mockEventRepository,
|
||||
categoryRepository: mockCategoryRepository,
|
||||
backupService: mockBackupService,
|
||||
notificationService: mockNotificationService,
|
||||
// Don't pass uiFeedbackService to avoid localization
|
||||
);
|
||||
});
|
||||
|
||||
setUpAll(() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
registerFallbackValue(Habit(
|
||||
habitData: HabitData(
|
||||
position: 0,
|
||||
title: 'Fallback',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
));
|
||||
registerFallbackValue(TimeOfDay.now());
|
||||
registerFallbackValue(Colors.grey);
|
||||
|
||||
// Initialize localization for tests
|
||||
S.load(const Locale('en'));
|
||||
});
|
||||
|
||||
group('HabitsManager with Repository Pattern', () {
|
||||
test('should initialize with provided repositories', () {
|
||||
expect(habitsManager, isNotNull);
|
||||
});
|
||||
|
||||
test('should load habits from repository', () async {
|
||||
// Setup
|
||||
final testHabit = Habit(
|
||||
habitData: HabitData(
|
||||
position: 0,
|
||||
title: 'Test Habit',
|
||||
twoDayRule: false,
|
||||
cue: 'Test cue',
|
||||
routine: 'Test routine',
|
||||
reward: 'Test reward',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
);
|
||||
|
||||
when(() => mockHabitRepository.getAllHabits())
|
||||
.thenAnswer((_) async => [testHabit]);
|
||||
|
||||
// Act
|
||||
await habitsManager.initModel();
|
||||
|
||||
// Assert
|
||||
verify(() => mockHabitRepository.getAllHabits()).called(1);
|
||||
expect(habitsManager.allHabits.length, 1);
|
||||
expect(habitsManager.allHabits[0].habitData.title, 'Test Habit');
|
||||
});
|
||||
|
||||
test('should add habit through repository', () async {
|
||||
// Setup
|
||||
when(() => mockHabitRepository.createHabit(any()))
|
||||
.thenAnswer((_) async => 1);
|
||||
when(() => mockHabitRepository.getAllHabits())
|
||||
.thenAnswer((_) async => []);
|
||||
|
||||
// Act
|
||||
habitsManager.addHabit(
|
||||
'New Habit',
|
||||
false,
|
||||
'Test cue',
|
||||
'Test routine',
|
||||
'Test reward',
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
const TimeOfDay(hour: 9, minute: 0),
|
||||
'Test sanction',
|
||||
false,
|
||||
'Test accountant',
|
||||
);
|
||||
|
||||
// Assert
|
||||
verify(() => mockHabitRepository.createHabit(any())).called(1);
|
||||
});
|
||||
|
||||
test('should update habit through repository', () async {
|
||||
// Setup
|
||||
final testHabit = Habit(
|
||||
habitData: HabitData(
|
||||
id: 1,
|
||||
position: 0,
|
||||
title: 'Original',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
);
|
||||
|
||||
// Add habit to internal state
|
||||
habitsManager.allHabits.add(testHabit);
|
||||
when(() => mockHabitRepository.updateHabit(any()))
|
||||
.thenAnswer((_) async {});
|
||||
|
||||
// Act
|
||||
habitsManager.editHabit(testHabit.habitData);
|
||||
|
||||
// Assert
|
||||
verify(() => mockHabitRepository.updateHabit(any())).called(1);
|
||||
});
|
||||
|
||||
test('should delete habit through repository', () async {
|
||||
// Setup
|
||||
final testHabit = Habit(
|
||||
habitData: HabitData(
|
||||
id: 1,
|
||||
position: 0,
|
||||
title: 'Test Habit',
|
||||
twoDayRule: false,
|
||||
cue: '',
|
||||
routine: '',
|
||||
reward: '',
|
||||
showReward: false,
|
||||
advanced: false,
|
||||
notification: false,
|
||||
notTime: const TimeOfDay(hour: 9, minute: 0),
|
||||
events: SplayTreeMap<DateTime, List>(),
|
||||
sanction: '',
|
||||
showSanction: false,
|
||||
accountant: '',
|
||||
),
|
||||
);
|
||||
|
||||
// Add habit to internal state
|
||||
habitsManager.allHabits.add(testHabit);
|
||||
when(() => mockHabitRepository.deleteHabit(any()))
|
||||
.thenAnswer((_) async {});
|
||||
// Mock specific localization strings
|
||||
when(() => mockUIFeedbackService.showMessageWithAction(
|
||||
message: any(named: 'message'),
|
||||
actionLabel: any(named: 'actionLabel'),
|
||||
onActionPressed: any(named: 'onActionPressed'),
|
||||
backgroundColor: any(named: 'backgroundColor'),
|
||||
)).thenReturn(null);
|
||||
|
||||
// Act
|
||||
habitsManager.deleteHabit(1);
|
||||
|
||||
// Assert - verify internal state changes immediately
|
||||
expect(habitsManager.allHabits.length, 0);
|
||||
expect(habitsManager.toDelete.length, 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user