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 _container() async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); return ProviderContainer( overrides: [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]); }); 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, {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 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, {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 current = container.read(trackerServiceProvider); expect(current.length, 1); expect(current.single.name, 'B'); }); test('state is persisted across container instances', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final first = ProviderContainer( overrides: [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: [sharedPreferencesProvider.overrideWithValue(prefs)], ); addTearDown(second.dispose); final List loaded = second.read(trackerServiceProvider); expect(loaded.length, 1); expect(loaded.single.name, 'Persisted'); expect(loaded.single.readChapters, {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()); }); }