199 lines
6.3 KiB
Dart
199 lines
6.3 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:bible_tracker/data/bible.dart';
|
|
import 'package:bible_tracker/models/tracker.dart';
|
|
import 'package:bible_tracker/screens/book_screen.dart';
|
|
import 'package:bible_tracker/screens/home_screen.dart';
|
|
import 'package:bible_tracker/screens/tracker_screen.dart';
|
|
import 'package:bible_tracker/services/last_location.dart';
|
|
import 'package:bible_tracker/services/tracker_service.dart';
|
|
import 'package:bible_tracker/services/tracker_storage.dart';
|
|
import 'package:bible_tracker/widgets/theme.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<Widget> _pumpApp({
|
|
Map<String, Object>? initialPrefs,
|
|
LastLocation? pending,
|
|
}) async {
|
|
SharedPreferences.setMockInitialValues(initialPrefs ?? <String, Object>{});
|
|
final prefs = await SharedPreferences.getInstance();
|
|
return ProviderScope(
|
|
overrides: <Override>[
|
|
sharedPreferencesProvider.overrideWithValue(prefs),
|
|
pendingRestoreProvider.overrideWithValue(pending),
|
|
],
|
|
child: MaterialApp(
|
|
theme: AppTheme.light(),
|
|
home: const HomeScreen(),
|
|
navigatorObservers: <NavigatorObserver>[
|
|
LastLocationObserver(
|
|
(LastLocation? loc) => LastLocationService(prefs).save(loc),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
String _seedTrackerJson(Tracker tracker) =>
|
|
jsonEncode(<Map<String, dynamic>>[tracker.toJson()]);
|
|
|
|
void main() {
|
|
testWidgets('shows empty state when no trackers exist', (tester) async {
|
|
await tester.pumpWidget(await _pumpApp());
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Start your first tracker'), findsOneWidget);
|
|
expect(find.text('New Tracker'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('creating a tracker shows it on the list', (tester) async {
|
|
await tester.pumpWidget(await _pumpApp());
|
|
await tester.pumpAndSettle();
|
|
|
|
final BuildContext context = tester.element(find.byType(HomeScreen));
|
|
final ProviderContainer container = ProviderScope.containerOf(context);
|
|
await container
|
|
.read(trackerServiceProvider.notifier)
|
|
.create(name: 'My Plan', color: const Color(0xFF3F51B5));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('My Plan'), findsOneWidget);
|
|
expect(find.text('0%'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets(
|
|
'restores tracker + book on launch when pending location matches',
|
|
(tester) async {
|
|
final tracker = Tracker(
|
|
id: 'seed-1',
|
|
name: 'Persisted',
|
|
colorValue: 0xFF3F51B5,
|
|
startDate: DateTime(2024),
|
|
);
|
|
const int bookIndex = 42; // John
|
|
|
|
await tester.pumpWidget(
|
|
await _pumpApp(
|
|
initialPrefs: <String, Object>{
|
|
'bible_tracker.trackers.v1': _seedTrackerJson(tracker),
|
|
},
|
|
pending: LastLocation(trackerId: tracker.id, bookIndex: bookIndex),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
// TrackerScreen sits underneath BookScreen and is offstage.
|
|
expect(
|
|
find.byType(TrackerScreen, skipOffstage: false),
|
|
findsOneWidget,
|
|
);
|
|
expect(find.byType(BookScreen), findsOneWidget);
|
|
expect(find.text(Bible.books[bookIndex].name), findsWidgets);
|
|
},
|
|
);
|
|
|
|
testWidgets('restores tracker only when no book index is saved', (
|
|
tester,
|
|
) async {
|
|
final tracker = Tracker(
|
|
id: 'seed-2',
|
|
name: 'Resumed',
|
|
colorValue: 0xFF3F51B5,
|
|
startDate: DateTime(2024),
|
|
);
|
|
await tester.pumpWidget(
|
|
await _pumpApp(
|
|
initialPrefs: <String, Object>{
|
|
'bible_tracker.trackers.v1': _seedTrackerJson(tracker),
|
|
},
|
|
pending: LastLocation(trackerId: tracker.id),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(TrackerScreen), findsOneWidget);
|
|
expect(find.byType(BookScreen), findsNothing);
|
|
expect(find.text('Resumed'), findsWidgets);
|
|
});
|
|
|
|
testWidgets('skips restore when the saved tracker no longer exists', (
|
|
tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
await _pumpApp(pending: const LastLocation(trackerId: 'missing')),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(TrackerScreen), findsNothing);
|
|
expect(find.byType(BookScreen), findsNothing);
|
|
expect(find.text('Start your first tracker'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('navigating into a book persists the location via the observer', (
|
|
tester,
|
|
) async {
|
|
SharedPreferences.setMockInitialValues(<String, Object>{});
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await tester.pumpWidget(
|
|
ProviderScope(
|
|
overrides: <Override>[
|
|
sharedPreferencesProvider.overrideWithValue(prefs),
|
|
],
|
|
child: MaterialApp(
|
|
theme: AppTheme.light(),
|
|
home: const HomeScreen(),
|
|
navigatorObservers: <NavigatorObserver>[
|
|
LastLocationObserver(
|
|
(LastLocation? loc) => LastLocationService(prefs).save(loc),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
final BuildContext context = tester.element(find.byType(HomeScreen));
|
|
final ProviderContainer container = ProviderScope.containerOf(context);
|
|
final tracker = await container
|
|
.read(trackerServiceProvider.notifier)
|
|
.create(name: 'Plan', color: const Color(0xFF3F51B5));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.tap(find.text('Plan'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
LastLocationService(prefs).load(),
|
|
LastLocation(trackerId: tracker.id),
|
|
);
|
|
|
|
await tester.tap(find.text('Genesis'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
LastLocationService(prefs).load(),
|
|
LastLocation(trackerId: tracker.id, bookIndex: 0),
|
|
);
|
|
|
|
// Pop back to the tracker screen — the persisted location should
|
|
// shrink back to "tracker only".
|
|
final NavigatorState navigator = tester.state<NavigatorState>(
|
|
find.byType(Navigator).first,
|
|
);
|
|
navigator.pop();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
LastLocationService(prefs).load(),
|
|
LastLocation(trackerId: tracker.id),
|
|
);
|
|
|
|
// Pop all the way to home — location should clear.
|
|
navigator.pop();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(LastLocationService(prefs).load(), isNull);
|
|
});
|
|
}
|