feat: theme chrome by palette, selectable text, wap fallback, and library pages
This commit is contained in:
parent
c104c2026b
commit
56f3d959fa
12 changed files with 534 additions and 51 deletions
|
|
@ -91,19 +91,21 @@ String _page(String title, String body) => '''
|
|||
void main() {
|
||||
late MewsDatabase db;
|
||||
late AppDependencies deps;
|
||||
late Strings strings;
|
||||
|
||||
setUp(() async {
|
||||
db = MewsDatabase.test(NativeDatabase.memory());
|
||||
deps = AppDependencies(database: db);
|
||||
strings = Strings();
|
||||
strings.loadSource(File('locales/en-US/main.ftl').readAsStringSync());
|
||||
deps = AppDependencies(database: db, strings: strings);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await db.close();
|
||||
});
|
||||
|
||||
/// Pumps the full app for [deps]; library pages get real strings.
|
||||
Future<void> pumpApp(WidgetTester tester) async {
|
||||
final strings = Strings();
|
||||
strings.loadSource(File('locales/en-US/main.ftl').readAsStringSync());
|
||||
await tester.pumpWidget(MewsApp(deps: deps, strings: strings));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
|
@ -152,13 +154,16 @@ void main() {
|
|||
showReaderSettingsSheet(tester.element(find.byType(ReaderScreen)));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Typeface, size, theme, colors.
|
||||
// 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();
|
||||
|
||||
|
|
@ -168,7 +173,6 @@ void main() {
|
|||
|
||||
// Bookmark and subscribe the current page; both tiles sit at the bottom
|
||||
// of the sheet, so scroll the sheet down to the end first.
|
||||
final sheet = find.byType(Scrollable).last;
|
||||
await tester.drag(sheet, const Offset(0, -1000));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Add bookmark'));
|
||||
|
|
@ -224,9 +228,8 @@ void main() {
|
|||
final stubDeps = AppDependencies(
|
||||
database: MewsDatabase.test(NativeDatabase.memory()),
|
||||
fetch: _FakeFetch(sources),
|
||||
strings: strings,
|
||||
);
|
||||
final strings = Strings();
|
||||
strings.loadSource(File('locales/en-US/main.ftl').readAsStringSync());
|
||||
await tester.pumpWidget(MewsApp(deps: stubDeps, strings: strings));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
|
@ -235,10 +238,14 @@ void main() {
|
|||
// The swipe neighbor is parsed in the background.
|
||||
await _pumpUntil(tester, () => stubDeps.browser.backPage.value != null);
|
||||
|
||||
// Swipes begin at the screen edge; mid-page drags select text. Small
|
||||
// steps keep the drag recognizer's update pacing in the test environment.
|
||||
final center = tester.getCenter(find.byType(SwipeNavigator));
|
||||
final gesture = await tester.startGesture(center);
|
||||
await gesture.moveBy(const Offset(400, 0));
|
||||
await tester.pump();
|
||||
final gesture = await tester.startGesture(Offset(10, center.dy));
|
||||
for (var i = 0; i < 6; i++) {
|
||||
await gesture.moveBy(const Offset(70, 0));
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
// The real previous page is on screen mid-swipe, not a placeholder.
|
||||
expect(find.text('Page A'), findsOneWidget);
|
||||
|
|
@ -253,6 +260,132 @@ 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(
|
||||
database: MewsDatabase.test(NativeDatabase.memory()),
|
||||
fetch: _FakeFetch({
|
||||
'https://wap.example.com/':
|
||||
_page('Wap site', '<h1>Wap site</h1><p>Content lives here.</p>'),
|
||||
}),
|
||||
strings: strings,
|
||||
);
|
||||
await tester.pumpWidget(MewsApp(deps: stubDeps, strings: strings));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The address as typed has no page; the wap. equivalent does.
|
||||
await stubDeps.browser.open('https://example.com/');
|
||||
expect(stubDeps.browser.currentPage.value?.url, 'https://wap.example.com/');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.textContaining('Content lives here'), findsOneWidget);
|
||||
|
||||
// Addresses that work nowhere keep showing the error.
|
||||
await stubDeps.browser.open('https://missing.example.org/');
|
||||
expect(stubDeps.browser.currentPage.value, isNull);
|
||||
expect(stubDeps.browser.error.value, isA<LoadFailed>());
|
||||
});
|
||||
|
||||
testWidgets('library pages render as Mews pages and join the walk history',
|
||||
(tester) async {
|
||||
final stubDeps = AppDependencies(
|
||||
database: MewsDatabase.test(NativeDatabase.memory()),
|
||||
fetch: _FakeFetch({
|
||||
'https://example.com/a.html':
|
||||
_page('Page A', '<h1>Page A</h1><p>The first page.</p>'),
|
||||
'https://example.com/b.html':
|
||||
_page('Page B', '<h1>Page B</h1><p>The second page.</p>'),
|
||||
}),
|
||||
strings: strings,
|
||||
);
|
||||
await tester.pumpWidget(MewsApp(deps: stubDeps, strings: strings));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await stubDeps.browser.open('https://example.com/a.html');
|
||||
await stubDeps.browser.open('https://example.com/b.html');
|
||||
await stubDeps.browser.open('mews:history');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Rendered like any Mews page, with dated links to both visits.
|
||||
expect(stubDeps.browser.currentPage.value?.url, 'mews:history');
|
||||
expect(find.text('History'), findsOneWidget);
|
||||
expect(find.textContaining('Page A'), findsOneWidget);
|
||||
expect(find.textContaining('Page B'), findsOneWidget);
|
||||
|
||||
// Part of the walk: back returns to page B, forward to history again.
|
||||
await stubDeps.browser.back();
|
||||
await tester.pumpAndSettle();
|
||||
expect(stubDeps.browser.currentPage.value?.url, 'https://example.com/b.html');
|
||||
|
||||
await stubDeps.browser.forward();
|
||||
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();
|
||||
expect(stubDeps.browser.currentPage.value?.url, 'https://example.com/a.html');
|
||||
|
||||
// Bookmarks and subscriptions get the same treatment.
|
||||
await stubDeps.library.load();
|
||||
await stubDeps.library.toggleBookmark('https://example.com/b.html', 'Page B');
|
||||
await stubDeps.library.toggleSubscription(
|
||||
'https://example.com/a.html', 'Page A', const []);
|
||||
await stubDeps.browser.open('mews:bookmarks');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Bookmarks'), findsOneWidget);
|
||||
expect(find.textContaining('Page B'), findsOneWidget);
|
||||
|
||||
await stubDeps.browser.open('mews:subscriptions');
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Subscriptions'), findsOneWidget);
|
||||
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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue