feat: narrow a library page as the reader types in its search box

This commit is contained in:
randogoth 2026-10-11 22:56:56 +03:00
parent 9d9f09ebc7
commit 95602ac623
5 changed files with 157 additions and 8 deletions

View file

@ -16,6 +16,7 @@ import 'package:mews/mews/parser.dart';
import 'package:mews/application/pages/mews_urls.dart';
import 'package:mews/presentation/app.dart';
import 'package:mews/presentation/reader/address_bar.dart';
import 'package:mews/presentation/reader/mews_page_view.dart' show searchTypingPause;
import 'package:mews/presentation/reader/swipe_navigator.dart';
import 'fake_fetch.dart';
@ -51,6 +52,9 @@ String _page(String title, String body) => '''
</html>
''';
/// Long enough for a search box to have narrowed its page.
final pastTypingPause = searchTypingPause + const Duration(milliseconds: 50);
void main() {
late MewsDatabase db;
late AppDependencies deps;
@ -663,19 +667,64 @@ void main() {
expect(find.textContaining('Signal bars'), findsOneWidget);
// Removing one stays on the page and adds no walk history entry. What the
// reader has typed survives, because the renderer matches form state by
// position and the search box keeps its place in the page.
await tester.enterText(find.byType(TextFormField), 'half typed');
// reader has typed survives, because the renderer matches the search box
// of the rebuilt page with the one they are typing in. The search here
// matches both addresses, so the list is still the whole of it.
await tester.enterText(find.byType(TextFormField), 'exam');
await tester.pump(pastTypingPause);
await tester.pumpAndSettle();
final stepsBack = deps.browser.canGoBack;
await tapRun(tester, 'Remove bookmark');
expect(find.widgetWithText(TextFormField, 'half typed'), findsOneWidget);
expect(deps.browser.currentPage.value?.url, MewsUrls.bookmarks);
expect(find.widgetWithText(TextFormField, 'exam'), findsOneWidget);
expect(deps.browser.currentPage.value?.url, 'mews:bookmarks?q=exam');
expect(deps.browser.canGoBack, stepsBack);
expect(find.textContaining('Retro phones'), findsNothing);
expect(find.textContaining('Signal bars'), findsOneWidget);
expect(deps.bookmarks.isBookmarked('https://a.example/retro.html'), isFalse);
});
testWidgets('a search box narrows its page as the reader types',
(tester) async {
await pumpApp(tester);
await deps.bookmarks.load();
await deps.bookmarks.toggle('https://a.example/retro.html', 'Retro phones');
await deps.bookmarks.toggle('https://b.example/signal.html', 'Signal bars');
await deps.browser.open(MewsUrls.bookmarks);
await tester.pumpAndSettle();
final stepsBack = deps.browser.canGoBack;
// Typing narrows the list without the box being submitted, and the query
// lands in the address as it does on a submit.
await tester.enterText(find.byType(TextFormField), 'retro');
await tester.pump(pastTypingPause);
await tester.pumpAndSettle();
expect(deps.browser.currentPage.value?.url, 'mews:bookmarks?q=retro');
expect(find.textContaining('Retro phones'), findsOneWidget);
expect(find.textContaining('Signal bars'), findsNothing);
// The box keeps what was typed, and the cursor with it, so the next
// letter carries on from where the reader left off.
expect(find.widgetWithText(TextFormField, 'retro'), findsOneWidget);
await tester.enterText(find.byType(TextFormField), 'signal');
await tester.pump(pastTypingPause);
await tester.pumpAndSettle();
expect(deps.browser.currentPage.value?.url, 'mews:bookmarks?q=signal');
expect(find.textContaining('Signal bars'), findsOneWidget);
// A narrowed search replaces the step of the walk the reader stands on,
// so two searches are not two steps back.
expect(deps.browser.canGoBack, stepsBack);
// Clearing the search empties the box too, since the page set it.
await tapRun(tester, 'Clear search');
await tester.pumpAndSettle();
expect(deps.browser.currentPage.value?.url, MewsUrls.bookmarks);
expect(find.widgetWithText(TextFormField, 'signal'), findsNothing);
expect(find.textContaining('Retro phones'), findsOneWidget);
expect(find.textContaining('Signal bars'), findsOneWidget);
});
testWidgets('tags are made by using them and filter the list',
(tester) async {
await pumpApp(tester);