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:
@@ -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));
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user