bible-tracker/test/services/tracker_service_test.dart
2026-04-20 15:13:58 -04:00

155 lines
5.6 KiB
Dart

import 'package:bible_tracker/data/bible.dart';
import 'package:bible_tracker/models/tracker.dart';
import 'package:bible_tracker/services/tracker_service.dart';
import 'package:bible_tracker/services/tracker_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<ProviderContainer> _container() async {
SharedPreferences.setMockInitialValues(<String, Object>{});
final prefs = await SharedPreferences.getInstance();
return ProviderContainer(
overrides: <Override>[sharedPreferencesProvider.overrideWithValue(prefs)],
);
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
test('starts empty when nothing is persisted', () async {
final container = await _container();
addTearDown(container.dispose);
expect(container.read(trackerServiceProvider), isEmpty);
});
test('create adds a tracker with the given name and color', () async {
final container = await _container();
addTearDown(container.dispose);
final tracker = await container
.read(trackerServiceProvider.notifier)
.create(name: 'My Plan', color: const Color(0xFF123456));
expect(tracker.name, 'My Plan');
expect(tracker.colorValue, const Color(0xFF123456).toARGB32());
expect(container.read(trackerServiceProvider), <Tracker>[tracker]);
});
test('toggleChapter adds and then removes chapter', () async {
final container = await _container();
addTearDown(container.dispose);
final service = container.read(trackerServiceProvider.notifier);
final tracker = await service.create(
name: 'Plan',
color: const Color(0xFF000000),
);
await service.toggleChapter(tracker.id, 0, 1);
Tracker current = container.read(trackerServiceProvider).single;
expect(current.readChapters, <String>{Bible.chapterKey(0, 1)});
await service.toggleChapter(tracker.id, 0, 1);
current = container.read(trackerServiceProvider).single;
expect(current.readChapters, isEmpty);
});
test('setBookRead marks all chapters of the book', () async {
final container = await _container();
addTearDown(container.dispose);
final service = container.read(trackerServiceProvider.notifier);
final tracker = await service.create(
name: 'Plan',
color: const Color(0xFF000000),
);
await service.setBookRead(tracker.id, 0, read: true);
final Tracker current = container.read(trackerServiceProvider).single;
final Book genesis = Bible.books[0];
expect(current.readChapters.length, genesis.chapterCount);
for (int chapter = 1; chapter <= genesis.chapterCount; chapter++) {
expect(current.readChapters.contains(Bible.chapterKey(0, chapter)), true);
}
});
test('reset clears only the target tracker', () async {
final container = await _container();
addTearDown(container.dispose);
final service = container.read(trackerServiceProvider.notifier);
final a = await service.create(name: 'A', color: const Color(0xFF111111));
final b = await service.create(name: 'B', color: const Color(0xFF222222));
await service.toggleChapter(a.id, 0, 1);
await service.toggleChapter(b.id, 0, 1);
await service.reset(a.id);
final List<Tracker> current = container.read(trackerServiceProvider);
expect(
current.firstWhere((Tracker t) => t.id == a.id).readChapters,
isEmpty,
);
expect(
current.firstWhere((Tracker t) => t.id == b.id).readChapters,
<String>{Bible.chapterKey(0, 1)},
);
});
test('delete removes the tracker', () async {
final container = await _container();
addTearDown(container.dispose);
final service = container.read(trackerServiceProvider.notifier);
final a = await service.create(name: 'A', color: const Color(0xFF111111));
await service.create(name: 'B', color: const Color(0xFF222222));
await service.delete(a.id);
final List<Tracker> current = container.read(trackerServiceProvider);
expect(current.length, 1);
expect(current.single.name, 'B');
});
test('state is persisted across container instances', () async {
SharedPreferences.setMockInitialValues(<String, Object>{});
final prefs = await SharedPreferences.getInstance();
final first = ProviderContainer(
overrides: <Override>[sharedPreferencesProvider.overrideWithValue(prefs)],
);
final created = await first
.read(trackerServiceProvider.notifier)
.create(name: 'Persisted', color: const Color(0xFFAABBCC));
await first
.read(trackerServiceProvider.notifier)
.toggleChapter(created.id, 5, 3);
first.dispose();
final second = ProviderContainer(
overrides: <Override>[sharedPreferencesProvider.overrideWithValue(prefs)],
);
addTearDown(second.dispose);
final List<Tracker> loaded = second.read(trackerServiceProvider);
expect(loaded.length, 1);
expect(loaded.single.name, 'Persisted');
expect(loaded.single.readChapters, <String>{Bible.chapterKey(5, 3)});
});
test('updateMeta changes name and color', () async {
final container = await _container();
addTearDown(container.dispose);
final service = container.read(trackerServiceProvider.notifier);
final tracker = await service.create(
name: 'Old',
color: const Color(0xFF111111),
);
await service.updateMeta(
tracker.id,
name: 'New',
color: const Color(0xFF222222),
);
final Tracker current = container.read(trackerServiceProvider).single;
expect(current.name, 'New');
expect(current.colorValue, const Color(0xFF222222).toARGB32());
});
}