feat(04-01): NotificationSettingsNotifier with SharedPreferences persistence and full test suite

- Fix NotificationService API calls to use flutter_local_notifications v21 named parameters
- Expose nextInstanceOf as @visibleForTesting public method for unit testing
- Create NotificationSettingsNotifier with @Riverpod(keepAlive: true)
- NotificationSettings data class with enabled bool + TimeOfDay fields
- Persist notifications_enabled, notifications_hour, notifications_minute to SharedPreferences
- Sync default state in build(), async _load() overrides on hydration
- Update tests to use correct Riverpod 3 provider name (notificationSettingsProvider)
- Add makeContainer() helper to await initial _load() before asserting mutations
- All 84 tests pass (72 existing + 12 new notification tests)
This commit is contained in:
2026-03-16 14:57:04 +01:00
parent 0f6789becd
commit 4f72eac933
4 changed files with 151 additions and 31 deletions

View File

@@ -5,6 +5,16 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:household_keeper/core/notifications/notification_settings_notifier.dart';
/// Helper: create a container and wait for the initial async _load() to finish.
Future<ProviderContainer> makeContainer() async {
final container = ProviderContainer();
// Trigger build
container.read(notificationSettingsProvider);
// Allow the async _load() to complete
await Future<void>.delayed(Duration.zero);
return container;
}
void main() {
setUp(() {
SharedPreferences.setMockInitialValues({});
@@ -15,32 +25,32 @@ void main() {
final container = ProviderContainer();
addTearDown(container.dispose);
final state = container.read(notificationSettingsNotifierProvider);
final state = container.read(notificationSettingsProvider);
expect(state.enabled, isFalse);
expect(state.time, const TimeOfDay(hour: 7, minute: 0));
});
test('setEnabled(true) updates state.enabled to true', () async {
final container = ProviderContainer();
final container = await makeContainer();
addTearDown(container.dispose);
await container
.read(notificationSettingsNotifierProvider.notifier)
.read(notificationSettingsProvider.notifier)
.setEnabled(true);
expect(
container.read(notificationSettingsNotifierProvider).enabled,
container.read(notificationSettingsProvider).enabled,
isTrue,
);
});
test('setEnabled(true) persists to SharedPreferences', () async {
final container = ProviderContainer();
final container = await makeContainer();
addTearDown(container.dispose);
await container
.read(notificationSettingsNotifierProvider.notifier)
.read(notificationSettingsProvider.notifier)
.setEnabled(true);
final prefs = await SharedPreferences.getInstance();
@@ -48,32 +58,32 @@ void main() {
});
test('setEnabled(false) updates state.enabled to false', () async {
final container = ProviderContainer();
final container = await makeContainer();
addTearDown(container.dispose);
// First enable, then disable
await container
.read(notificationSettingsNotifierProvider.notifier)
.read(notificationSettingsProvider.notifier)
.setEnabled(true);
await container
.read(notificationSettingsNotifierProvider.notifier)
.read(notificationSettingsProvider.notifier)
.setEnabled(false);
expect(
container.read(notificationSettingsNotifierProvider).enabled,
container.read(notificationSettingsProvider).enabled,
isFalse,
);
});
test('setEnabled(false) persists to SharedPreferences', () async {
final container = ProviderContainer();
final container = await makeContainer();
addTearDown(container.dispose);
await container
.read(notificationSettingsNotifierProvider.notifier)
.read(notificationSettingsProvider.notifier)
.setEnabled(true);
await container
.read(notificationSettingsNotifierProvider.notifier)
.read(notificationSettingsProvider.notifier)
.setEnabled(false);
final prefs = await SharedPreferences.getInstance();
@@ -83,15 +93,15 @@ void main() {
test(
'setTime(09:30) updates state.time and persists hour+minute',
() async {
final container = ProviderContainer();
final container = await makeContainer();
addTearDown(container.dispose);
await container
.read(notificationSettingsNotifierProvider.notifier)
.read(notificationSettingsProvider.notifier)
.setTime(const TimeOfDay(hour: 9, minute: 30));
expect(
container.read(notificationSettingsNotifierProvider).time,
container.read(notificationSettingsProvider).time,
const TimeOfDay(hour: 9, minute: 30),
);
@@ -110,16 +120,10 @@ void main() {
'notifications_minute': 15,
});
final container = ProviderContainer();
final container = await makeContainer();
addTearDown(container.dispose);
// Trigger build
container.read(notificationSettingsNotifierProvider);
// Wait for async _load() to complete
await Future<void>.delayed(Duration.zero);
final state = container.read(notificationSettingsNotifierProvider);
final state = container.read(notificationSettingsProvider);
expect(state.enabled, isTrue);
expect(state.time, const TimeOfDay(hour: 8, minute: 15));
},