only-bible-app v3.7.2+24
git clone https://git.pyrossh.dev/only-bible-app
The only bible app you will ever need. No ads. No in-app purchases. No distractions.
test/navigation_integration_test.dart
// Integration-style tests: these mount the real App widget and drive it
// through actual taps (find + tap real keys/text), rather than dispatching
// actions programmatically against a bare store/router. That distinction
// matters: an earlier bug (App._syncCurrentChapter clobbering
// savedBook/savedChapter right after a push()) went unnoticed by unit tests
// that dispatched actions directly, because they never mounted the widget
// tree that wires the router's listener up in the first place. Driving the
// same flows through the UI is what actually would have caught it.
import "package:flutter/material.dart";
import "package:flutter_test/flutter_test.dart";
import "package:async_redux/async_redux.dart";
import "package:only_bible_app/app.dart";
import "package:only_bible_app/store/app_state.dart";
import "package:only_bible_app/widgets/book_select_sheet.dart";
import "package:only_bible_app/widgets/book_tile.dart";
import "app_logic_test.dart" show buildTestBible;
/// Taps the [label] tile inside whichever [BookSelectSheet] is currently on
/// screen - scoped there (rather than a bare find.text(label)) because plain
/// text like a chapter number ("1", "2") can otherwise collide with the
/// current-chapter number shown in the app bar behind the sheet.
Future<void> tapSheetTile(WidgetTester tester, String label) async {
final sheet = find.byType(BookSelectSheet);
await tester.tap(find.descendant(of: sheet, matching: find.text(label)));
await tester.pumpAndSettle();
}
bool _isSelectedTile(Widget w, String label) =>
w is BookTile && w.label == label && w.isSelected;
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
testWidgets(
"navigating via the book+chapter selector updates the displayed chapter, and "
"reopening the selector highlights that new book/chapter - not the one "
"navigated away from (regression: App._syncCurrentChapter used to clobber "
"savedBook/savedChapter right after the push, so the selector kept "
"highlighting the pre-navigation position)",
(tester) async {
final bible = buildTestBible();
final store = Store<AppState>(
initialState: AppState(bible: bible, savedBook: 0, savedChapter: 0),
);
await tester.pumpWidget(
App(globalNavigatorKey: GlobalKey<NavigatorState>(), store: store),
);
await tester.pumpAndSettle();
// Sanity check: Genesis 1 is on screen to start with. Verse text
// renders as rich text with a "1 " verse-number span prefixed, so this
// is a substring match rather than find.text's exact match.
expect(find.textContaining("In the beginning..."), findsOneWidget);
// Open the book selector via the app bar's book title.
await tester.tap(find.byKey(const Key("bookTitle")));
await tester.pumpAndSettle();
// Pick Revelation (a multi-chapter book, so this animates to the
// chapter grid rather than closing immediately).
await tapSheetTile(tester, "Rev");
// Pick chapter 2.
await tapSheetTile(tester, "2");
// The displayed chapter should now be Revelation 2.
expect(find.textContaining("Rev 2:1"), findsOneWidget);
expect(store.state.savedBook, 2);
expect(store.state.savedChapter, 1);
// Reopen the book selector - this is the exact regression.
await tester.tap(find.byKey(const Key("bookTitle")));
await tester.pumpAndSettle();
final sheet = find.byType(BookSelectSheet);
expect(
find.descendant(
of: sheet,
matching: find.byWidgetPredicate((w) => _isSelectedTile(w, "Rev")),
),
findsOneWidget,
);
expect(
find.descendant(
of: sheet,
matching: find.byWidgetPredicate((w) => _isSelectedTile(w, "Gen")),
),
findsNothing,
);
},
);
testWidgets(
"swiping to the next chapter after using the picker keeps the app bar's chapter number in sync",
(tester) async {
final bible = buildTestBible();
final store = Store<AppState>(
initialState: AppState(bible: bible, savedBook: 0, savedChapter: 0),
);
await tester.pumpWidget(
App(globalNavigatorKey: GlobalKey<NavigatorState>(), store: store),
);
await tester.pumpAndSettle();
// Jump to Revelation 1 via the picker (push).
await tester.tap(find.byKey(const Key("bookTitle")));
await tester.pumpAndSettle();
await tapSheetTile(tester, "Rev");
await tapSheetTile(tester, "1");
expect(find.textContaining("Rev 1:1"), findsOneWidget);
// Swipe left to advance to chapter 2 (pushReplacement on top of the
// still-2-entry stack the picker's push left behind).
await tester.fling(
find.byType(SingleChildScrollView),
const Offset(-400, 0),
800,
);
await tester.pumpAndSettle();
expect(find.textContaining("Rev 2:1"), findsOneWidget);
expect(store.state.savedBook, 2);
expect(store.state.savedChapter, 1);
// The app bar's chapter number (built from the same live state) must
// agree with what's actually on screen.
expect(
find.descendant(
of: find.byKey(const Key("chapterTitle")),
matching: find.text("2"),
),
findsOneWidget,
);
},
);
}