feat: retire FOB and sheet; settings as a Mews page and actions on the address bar
This commit is contained in:
parent
6f9c0e587a
commit
28ff7ca327
10 changed files with 324 additions and 361 deletions
|
|
@ -8,17 +8,13 @@ import 'package:flutter_test/flutter_test.dart';
|
|||
import 'package:mews/application/actors/fetch_actor.dart';
|
||||
import 'package:mews/application/controllers/browser_controller.dart'
|
||||
show BrokenPage, LoadFailed;
|
||||
import 'package:mews/application/controllers/reader_settings_controller.dart'
|
||||
show ReaderSettingsController;
|
||||
import 'package:mews/database/database.dart';
|
||||
import 'package:mews/l10n/strings.dart';
|
||||
import 'package:mews/mews/document.dart';
|
||||
import 'package:mews/mews/parser.dart';
|
||||
import 'package:mews/application/internal_pages.dart';
|
||||
import 'package:mews/presentation/app.dart';
|
||||
import 'package:mews/presentation/reader/address_bar.dart';
|
||||
import 'package:mews/presentation/reader/fob.dart';
|
||||
import 'package:mews/presentation/reader/reader_screen.dart';
|
||||
import 'package:mews/presentation/reader/settings_sheet.dart';
|
||||
import 'package:mews/presentation/reader/swipe_navigator.dart';
|
||||
|
||||
const pageSource = '''
|
||||
|
|
@ -143,53 +139,102 @@ void main() {
|
|||
expect(find.textContaining('Try again'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('settings sheet changes presentation and toggles library',
|
||||
testWidgets('the settings page changes presentation and keeps its place',
|
||||
(tester) async {
|
||||
await pumpApp(tester);
|
||||
|
||||
// Settings opens from the left address bar icon.
|
||||
await tester.tap(find.byIcon(Icons.tune));
|
||||
await tester.pumpAndSettle();
|
||||
expect(deps.browser.currentPage.value?.url, InternalPages.settingsUrl);
|
||||
|
||||
// Rendered like a Mews page with the option groups.
|
||||
expect(find.text('Settings'), findsOneWidget);
|
||||
expect(find.text('Typeface'), findsOneWidget);
|
||||
expect(find.text('Theme'), findsOneWidget);
|
||||
expect(find.text('Colors'), findsOneWidget);
|
||||
expect(find.text('Library'), findsOneWidget);
|
||||
|
||||
// Tapping a theme link applies it and stays on the settings page; the
|
||||
// lower sections scroll into view first.
|
||||
Future<void> scrollTo(Finder target) async {
|
||||
// Search down the page, then back up for targets above.
|
||||
for (var i = 0; i < 20 && target.evaluate().isEmpty; i++) {
|
||||
await tester.drag(find.byType(ListView), const Offset(0, -200));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
for (var i = 0; i < 40 && target.evaluate().isEmpty; i++) {
|
||||
await tester.drag(find.byType(ListView), const Offset(0, 200));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.ensureVisible(target);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
// The option rows stretch the full column width, so tap on the glyphs
|
||||
// themselves rather than the widget's center.
|
||||
Future<void> tapLink(String label) async {
|
||||
final rect = tester.getRect(find.text(label));
|
||||
await tester.tapAt(rect.topLeft + const Offset(30, 13));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
await scrollTo(find.text('Dark'));
|
||||
await tapLink('Dark');
|
||||
expect(deps.settings.themeMode.value, 'dark');
|
||||
expect(deps.browser.currentPage.value?.url, InternalPages.settingsUrl);
|
||||
expect(deps.browser.canGoBack, isFalse);
|
||||
|
||||
await scrollTo(find.text('Warm'));
|
||||
await tapLink('Warm');
|
||||
expect(deps.settings.variant.value, 'warm');
|
||||
|
||||
await scrollTo(find.text('Larger text'));
|
||||
await tapLink('Larger text');
|
||||
expect(deps.settings.fontScale.value, 1.125);
|
||||
|
||||
// The library links navigate and join the walk history.
|
||||
await scrollTo(find.text('History'));
|
||||
await tapLink('History');
|
||||
await tester.pumpAndSettle();
|
||||
expect(deps.browser.currentPage.value?.url, InternalPages.historyUrl);
|
||||
expect(deps.browser.canGoBack, isTrue);
|
||||
|
||||
await deps.browser.back();
|
||||
await tester.pumpAndSettle();
|
||||
expect(deps.browser.currentPage.value?.url, InternalPages.settingsUrl);
|
||||
});
|
||||
|
||||
testWidgets('bookmark and subscribe live on the address bar', (tester) async {
|
||||
await pumpApp(tester);
|
||||
|
||||
// A page with dated entries can be subscribed to.
|
||||
final page = parseSource(pageSource, 'https://example.com/index.html');
|
||||
deps.browser.currentPage.value = page;
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
showReaderSettingsSheet(tester.element(find.byType(ReaderScreen)));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Typeface, size, theme, colors; the lower sections need scrolling.
|
||||
final sheet = find.byType(Scrollable).last;
|
||||
await tester.tap(find.text('Atkinson Hyperlegible'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.scrollUntilVisible(find.text('Dark'), 200, scrollable: sheet);
|
||||
await tester.tap(find.text('Dark'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.scrollUntilVisible(find.text('Warm'), 200, scrollable: sheet);
|
||||
await tester.tap(find.text('Warm'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(deps.settings.fontScale.value, 1.125);
|
||||
expect(deps.settings.themeMode.value, 'dark');
|
||||
expect(deps.settings.variant.value, 'warm');
|
||||
|
||||
// Bookmark and subscribe the current page; both tiles sit at the bottom
|
||||
// of the sheet, so scroll the sheet down to the end first.
|
||||
await tester.drag(sheet, const Offset(0, -1000));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Add bookmark'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(deps.library.isBookmarked('https://example.com/index.html'), isTrue);
|
||||
|
||||
await tester.tap(find.text('Subscribe to this site'));
|
||||
expect(find.byIcon(Icons.notifications_outlined), findsOneWidget);
|
||||
await tester.tap(find.byIcon(Icons.notifications_outlined));
|
||||
await tester.pumpAndSettle();
|
||||
expect(deps.library.isSubscribed('https://example.com/index.html'), isTrue);
|
||||
expect(find.byIcon(Icons.notifications_active), findsOneWidget);
|
||||
|
||||
// Settings survive a reload of the controller.
|
||||
final reloaded = ReaderSettingsController(db);
|
||||
await reloaded.load();
|
||||
expect(reloaded.themeMode.value, 'dark');
|
||||
expect(find.byIcon(Icons.star_border), findsOneWidget);
|
||||
await tester.tap(find.byIcon(Icons.star_border));
|
||||
await tester.pumpAndSettle();
|
||||
expect(deps.library.isBookmarked('https://example.com/index.html'), isTrue);
|
||||
expect(find.byIcon(Icons.star), findsOneWidget);
|
||||
|
||||
// Pages with neither Atom feed nor dated links offer no subscription.
|
||||
final plain = parseSource(
|
||||
_page('Plain', '<h1>Plain</h1><p>No entries here.</p>'),
|
||||
'https://example.com/plain.html');
|
||||
deps.browser.currentPage.value = plain;
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byIcon(Icons.notifications_outlined), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('the FOB hides on scroll down and returns on scroll up',
|
||||
testWidgets('the address bar hides on scroll down and returns on scroll up',
|
||||
(tester) async {
|
||||
await pumpApp(tester);
|
||||
|
||||
|
|
@ -201,19 +246,16 @@ void main() {
|
|||
deps.browser.currentPage.value = page;
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
Fob fob() => tester.widget<Fob>(find.byType(Fob));
|
||||
expect(fob().visible, isTrue);
|
||||
expect(find.byType(AddressBar), findsOneWidget);
|
||||
|
||||
// Scrolling down hides the address bar and the FOB.
|
||||
// Scrolling down hides the address bar.
|
||||
await tester.drag(find.byType(ListView), const Offset(0, -300));
|
||||
await tester.pumpAndSettle();
|
||||
expect(fob().visible, isFalse);
|
||||
expect(find.byType(AddressBar), findsNothing);
|
||||
|
||||
// Scrolling up brings both back.
|
||||
// Scrolling up brings it back.
|
||||
await tester.drag(find.byType(ListView), const Offset(0, 300));
|
||||
await tester.pumpAndSettle();
|
||||
expect(fob().visible, isTrue);
|
||||
expect(find.byType(AddressBar), findsOneWidget);
|
||||
});
|
||||
|
||||
|
|
@ -260,40 +302,6 @@ void main() {
|
|||
expect(find.text('https://example.com/a.html'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('the chrome follows the selected colors', (tester) async {
|
||||
await pumpApp(tester);
|
||||
await deps.settings.setThemeMode('light');
|
||||
await deps.settings.setVariant('warm');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
MaterialApp app() => tester.widget<MaterialApp>(find.byType(MaterialApp));
|
||||
// Warm light palette on the light theme, FOB included.
|
||||
expect(app().theme!.colorScheme.surface, const Color(0xfffbf4e8));
|
||||
expect(app().theme!.colorScheme.primary, const Color(0xff8a3d14));
|
||||
expect(app().theme!.floatingActionButtonTheme.backgroundColor,
|
||||
const Color(0xff8a3d14));
|
||||
expect(app().theme!.textSelectionTheme.selectionColor,
|
||||
const Color(0xff8a3d14).withValues(alpha: 0.3));
|
||||
|
||||
await deps.settings.setThemeMode('dark');
|
||||
await deps.settings.setVariant('green');
|
||||
await tester.pumpAndSettle();
|
||||
// Green dark palette on the dark theme.
|
||||
expect(app().darkTheme!.colorScheme.surface, const Color(0xff161b16));
|
||||
expect(app().darkTheme!.colorScheme.primary, const Color(0xff9fd39a));
|
||||
expect(app().darkTheme!.floatingActionButtonTheme.backgroundColor,
|
||||
const Color(0xff9fd39a));
|
||||
});
|
||||
|
||||
testWidgets('page text is selectable', (tester) async {
|
||||
await pumpApp(tester);
|
||||
final page = parseSource(pageSource, 'https://example.com/index.html');
|
||||
deps.browser.currentPage.value = page;
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SelectionArea), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('falls back to the wap subdomain before showing an error',
|
||||
(tester) async {
|
||||
final stubDeps = AppDependencies(
|
||||
|
|
@ -375,17 +383,6 @@ void main() {
|
|||
expect(find.textContaining('Page A'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('the settings sheet opens the library pages', (tester) async {
|
||||
await pumpApp(tester);
|
||||
showReaderSettingsSheet(tester.element(find.byType(ReaderScreen)));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('History'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(deps.browser.currentPage.value?.url, 'mews:history');
|
||||
expect(find.text('History'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('reports new posts once per page', (tester) async {
|
||||
await pumpApp(tester);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import 'package:mews/database/database.dart';
|
|||
import 'package:mews/l10n/strings.dart';
|
||||
import 'package:mews/mews/document.dart';
|
||||
import 'package:mews/presentation/app.dart';
|
||||
import 'package:mews/presentation/reader/fob.dart';
|
||||
import 'package:mews/presentation/reader/mews_page_view.dart';
|
||||
import 'package:mews/presentation/reader/palettes.dart';
|
||||
|
||||
|
|
@ -20,16 +19,15 @@ Future<MewsApp> _app() async {
|
|||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('starts with the address bar and the FOB on screen', (tester) async {
|
||||
testWidgets('starts with the address bar and its icons', (tester) async {
|
||||
await tester.pumpWidget(await _app());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(TextField), findsOneWidget);
|
||||
expect(find.text('Enter a web address'), findsOneWidget);
|
||||
// The FOB is on screen from the beginning and hides only while the
|
||||
// reader scrolls down.
|
||||
final fob = tester.widget<Fob>(find.byType(Fob));
|
||||
expect(fob.visible, isTrue);
|
||||
// Settings opens from the left icon; no floating button remains.
|
||||
expect(find.byIcon(Icons.tune), findsOneWidget);
|
||||
expect(find.byType(FloatingActionButton), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('renders a page and follows links', (tester) async {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue