feat: search, filter and act on items from the library pages

This commit is contained in:
randogoth 2026-10-11 13:25:57 +03:00
parent 1b426ccd8b
commit 5cd053c8a9
11 changed files with 401 additions and 43 deletions

View file

@ -422,9 +422,9 @@ void main() {
await tester.pumpAndSettle();
expect(stubDeps.browser.currentPage.value?.url, 'mews:history');
// A history link navigates like any other link.
await tester.tap(find.textContaining('Page A'));
await tester.pumpAndSettle();
// A history link navigates like any other link. Rows carry their own
// actions now, so tap the title itself rather than the row's middle.
await tapRun(tester, 'Page A');
expect(stubDeps.browser.currentPage.value?.url, 'https://example.com/a.html');
// Bookmarks and subscriptions get the same treatment.
@ -484,6 +484,102 @@ void main() {
debugDefaultTargetPlatformOverride = null;
});
testWidgets('bookmarks can be searched and removed from their own page',
(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();
expect(find.textContaining('Retro phones'), findsOneWidget);
expect(find.textContaining('Signal bars'), findsOneWidget);
// Searching narrows the list and the choice lands in the address.
await tester.enterText(find.byType(TextFormField), 'retro');
await tester.tap(find.widgetWithText(FilledButton, 'Search'));
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);
// A search with no matches says so rather than looking empty.
await deps.browser.open('mews:bookmarks?q=nothinghere');
await tester.pumpAndSettle();
expect(find.textContaining('No bookmarks match'), findsOneWidget);
// Clearing the search brings the whole list back.
await deps.browser.open('mews:bookmarks?q=retro');
await tester.pumpAndSettle();
await tapRun(tester, 'Clear search');
expect(deps.browser.currentPage.value?.url, MewsUrls.bookmarks);
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');
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(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('clearing history takes a second step', (tester) async {
await pumpApp(tester);
await deps.db.recordVisit('https://a.example/one.html', 'Page one');
await deps.db.recordVisit('https://a.example/one.html', 'Page one');
await deps.db.recordVisit('https://b.example/two.html', 'Page two');
await deps.browser.open(MewsUrls.history);
await tester.pumpAndSettle();
// A page visited twice is listed once.
expect(find.textContaining('Page one'), findsOneWidget);
expect(find.textContaining('Page two'), findsOneWidget);
// Backing out of the confirmation leaves history alone.
await tapRun(tester, 'Clear history');
expect(deps.browser.currentPage.value?.url, 'mews:history?confirm=clear');
expect(find.textContaining("can't undo"), findsOneWidget);
await tapRun(tester, 'Keep my history');
expect(deps.browser.currentPage.value?.url, MewsUrls.history);
expect(find.textContaining('Page one'), findsOneWidget);
// Removing one page leaves the other. The most recent visit heads the
// list, so the first row is page two.
await tapRun(tester, 'Remove from history');
await tester.pumpAndSettle();
expect(find.textContaining('Page two'), findsNothing);
expect(find.textContaining('Page one'), findsOneWidget);
// Confirming clears the rest.
await tapRun(tester, 'Clear history');
await tapRun(tester, 'Clear history now');
expect(find.textContaining('Pages you visit appear here'), findsOneWidget);
});
testWidgets('a site can be left from the subscriptions page', (tester) async {
await pumpApp(tester);
await deps.subscriptions.load();
await deps.subscriptions
.toggle(parseSource(pageSource, 'https://example.com/index.html'));
await deps.browser.open(MewsUrls.subscriptions);
await tester.pumpAndSettle();
expect(find.textContaining('Signal Bars'), findsOneWidget);
await tapRun(tester, 'Stop subscribing');
expect(deps.browser.currentPage.value?.url, MewsUrls.subscriptions);
expect(deps.subscriptions.isSubscribed('https://example.com/index.html'),
isFalse);
expect(find.textContaining('Sites you subscribe to'), findsOneWidget);
});
testWidgets('the posts page lists, orders and filters posts',
(tester) async {
const siteA = 'https://a.example/';