diff --git a/integration_test/app_e2e_test.dart b/integration_test/app_e2e_test.dart index 92f360d..c978931 100644 --- a/integration_test/app_e2e_test.dart +++ b/integration_test/app_e2e_test.dart @@ -317,7 +317,7 @@ void main() { // ── Check Sent folder ────────────────────────────────────────────────── // Use the drawer to switch folders (no back button on Linux desktop). - await tester.tap(find.byTooltip('Open navigation menu')); + await tester.tap(find.byTooltip('Open folders')); await tester.pumpAndSettle(); await tester.tap(find.text('Sent')); await tester.pumpAndSettle(); @@ -331,7 +331,7 @@ void main() { expect(find.text(subject), findsOneWidget); // ── Check Inbox ──────────────────────────────────────────────────────── - await tester.tap(find.byTooltip('Open navigation menu')); + await tester.tap(find.byTooltip('Open folders')); await tester.pumpAndSettle(); await tester.tap(find.text('INBOX')); await tester.pumpAndSettle(); diff --git a/lib/core/db_schema_version.dart b/lib/core/db_schema_version.dart index 3f145fe..85e2c74 100644 --- a/lib/core/db_schema_version.dart +++ b/lib/core/db_schema_version.dart @@ -1 +1 @@ -const int dbSchemaVersion = 33; +const int dbSchemaVersion = 34; diff --git a/lib/core/models/user_preferences.dart b/lib/core/models/user_preferences.dart new file mode 100644 index 0000000..9a806d5 --- /dev/null +++ b/lib/core/models/user_preferences.dart @@ -0,0 +1,6 @@ +enum MenuPosition { bottom, top } + +class UserPreferences { + const UserPreferences({this.menuPosition = MenuPosition.bottom}); + final MenuPosition menuPosition; +} diff --git a/lib/core/repositories/user_preferences_repository.dart b/lib/core/repositories/user_preferences_repository.dart new file mode 100644 index 0000000..c2f5333 --- /dev/null +++ b/lib/core/repositories/user_preferences_repository.dart @@ -0,0 +1,6 @@ +import 'package:sharedinbox/core/models/user_preferences.dart'; + +abstract class UserPreferencesRepository { + Stream observePreferences(); + Future updateMenuPosition(MenuPosition position); +} diff --git a/lib/data/db/database.dart b/lib/data/db/database.dart index 8e2ad59..9619849 100644 --- a/lib/data/db/database.dart +++ b/lib/data/db/database.dart @@ -307,6 +307,17 @@ class LocalSieveApplied extends Table { Set get primaryKey => {accountId, messageId}; } +/// App-wide user preferences, stored as a singleton row (id always 1). +@DataClassName('UserPreferencesRow') +class UserPreferences extends Table { + IntColumn get id => integer()(); + // 'bottom' (default) | 'top' + TextColumn get menuPosition => text().withDefault(const Constant('bottom'))(); + + @override + Set get primaryKey => {id}; +} + // ── Database ────────────────────────────────────────────────────────────────── @DriftDatabase( @@ -327,6 +338,7 @@ class LocalSieveApplied extends Table { LocalSieveScripts, LocalSieveApplied, ShareKeys, + UserPreferences, ], ) class AppDatabase extends _$AppDatabase { @@ -578,6 +590,9 @@ class AppDatabase extends _$AppDatabase { await m.addColumn(syncLogs, syncLogs.errorStackTrace); await m.addColumn(syncLogs, syncLogs.isPermanent); } + if (from < 34) { + await m.createTable(userPreferences); + } }, ); } diff --git a/lib/data/repositories/user_preferences_repository_impl.dart b/lib/data/repositories/user_preferences_repository_impl.dart new file mode 100644 index 0000000..71535df --- /dev/null +++ b/lib/data/repositories/user_preferences_repository_impl.dart @@ -0,0 +1,38 @@ +import 'package:drift/drift.dart'; +import 'package:sharedinbox/core/models/user_preferences.dart' as pref; +import 'package:sharedinbox/core/repositories/user_preferences_repository.dart'; +import 'package:sharedinbox/data/db/database.dart'; + +class UserPreferencesRepositoryImpl implements UserPreferencesRepository { + UserPreferencesRepositoryImpl(this._db); + + final AppDatabase _db; + static const _rowId = 1; + + @override + Stream observePreferences() { + return (_db.select(_db.userPreferences)..where((t) => t.id.equals(_rowId))) + .watchSingleOrNull() + .map(_rowToModel); + } + + @override + Future updateMenuPosition(pref.MenuPosition position) async { + await _db.into(_db.userPreferences).insertOnConflictUpdate( + UserPreferencesCompanion( + id: const Value(_rowId), + menuPosition: Value(position.name), + ), + ); + } + + static pref.UserPreferences _rowToModel(UserPreferencesRow? row) { + if (row == null) return const pref.UserPreferences(); + return pref.UserPreferences( + menuPosition: pref.MenuPosition.values.firstWhere( + (e) => e.name == row.menuPosition, + orElse: () => pref.MenuPosition.bottom, + ), + ); + } +} diff --git a/lib/di.dart b/lib/di.dart index 4795cb3..f239062 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -5,6 +5,7 @@ import 'package:http/http.dart' as http; import 'package:sharedinbox/core/models/account.dart' as model; import 'package:sharedinbox/core/models/email.dart'; import 'package:sharedinbox/core/models/undo_action.dart'; +import 'package:sharedinbox/core/models/user_preferences.dart'; import 'package:sharedinbox/core/repositories/account_repository.dart'; import 'package:sharedinbox/core/repositories/draft_repository.dart'; import 'package:sharedinbox/core/repositories/email_repository.dart'; @@ -13,6 +14,7 @@ import 'package:sharedinbox/core/repositories/search_history_repository.dart'; import 'package:sharedinbox/core/repositories/share_key_repository.dart'; import 'package:sharedinbox/core/repositories/sync_log_repository.dart'; import 'package:sharedinbox/core/repositories/undo_repository.dart'; +import 'package:sharedinbox/core/repositories/user_preferences_repository.dart'; import 'package:sharedinbox/core/services/account_discovery_service.dart'; import 'package:sharedinbox/core/services/connection_test_service.dart'; import 'package:sharedinbox/core/services/managesieve_probe_service.dart'; @@ -21,7 +23,8 @@ import 'package:sharedinbox/core/services/undo_service.dart'; import 'package:sharedinbox/core/storage/secure_storage.dart'; import 'package:sharedinbox/core/sync/account_sync_manager.dart'; import 'package:sharedinbox/core/sync/reliability_runner.dart'; -import 'package:sharedinbox/data/db/database.dart' hide Email, EmailBody; +import 'package:sharedinbox/data/db/database.dart' + hide Email, EmailBody, UserPreferences; import 'package:sharedinbox/data/db/local_sieve_repository.dart'; import 'package:sharedinbox/data/imap/imap_client_factory.dart'; import 'package:sharedinbox/data/jmap/sieve_repository.dart'; @@ -33,6 +36,7 @@ import 'package:sharedinbox/data/repositories/search_history_repository_impl.dar import 'package:sharedinbox/data/repositories/share_key_repository_impl.dart'; import 'package:sharedinbox/data/repositories/sync_log_repository_impl.dart'; import 'package:sharedinbox/data/repositories/undo_repository_impl.dart'; +import 'package:sharedinbox/data/repositories/user_preferences_repository_impl.dart'; import 'package:sharedinbox/data/storage/flutter_secure_storage_impl.dart'; /// Swappable IMAP connection factory — override in tests to use plaintext. @@ -227,3 +231,13 @@ final accountConnectionStatusProvider = .read(connectionTestServiceProvider) .testConnection(account, password); }); + +final userPreferencesRepositoryProvider = + Provider((ref) { + return UserPreferencesRepositoryImpl(ref.watch(dbProvider)); +}); + +final userPreferencesProvider = + StreamProvider.autoDispose((ref) { + return ref.watch(userPreferencesRepositoryProvider).observePreferences(); +}); diff --git a/lib/ui/router.dart b/lib/ui/router.dart index 9cf5fcc..dcc1c66 100644 --- a/lib/ui/router.dart +++ b/lib/ui/router.dart @@ -20,6 +20,7 @@ import 'package:sharedinbox/ui/screens/sieve_scripts_screen.dart'; import 'package:sharedinbox/ui/screens/sync_log_screen.dart'; import 'package:sharedinbox/ui/screens/thread_detail_screen.dart'; import 'package:sharedinbox/ui/screens/undo_log_screen.dart'; +import 'package:sharedinbox/ui/screens/user_preferences_screen.dart'; import 'package:sharedinbox/ui/widgets/undo_shell.dart'; final router = GoRouter( @@ -56,6 +57,10 @@ final router = GoRouter( path: 'about', builder: (ctx, state) => const AboutScreen(), ), + GoRoute( + path: 'preferences', + builder: (ctx, state) => const UserPreferencesScreen(), + ), GoRoute( path: ':accountId/edit', builder: (ctx, state) => EditAccountScreen( diff --git a/lib/ui/screens/account_list_screen.dart b/lib/ui/screens/account_list_screen.dart index 5e7e0b4..f013f29 100644 --- a/lib/ui/screens/account_list_screen.dart +++ b/lib/ui/screens/account_list_screen.dart @@ -67,6 +67,14 @@ class AccountListScreen extends ConsumerWidget { unawaited(context.push('/accounts/about')); }, ), + ListTile( + leading: const Icon(Icons.settings), + title: const Text('Preferences'), + onTap: () { + Navigator.pop(context); // Close drawer + unawaited(context.push('/accounts/preferences')); + }, + ), ], ), ), diff --git a/lib/ui/screens/email_list_screen.dart b/lib/ui/screens/email_list_screen.dart index 74bd989..a10e85a 100644 --- a/lib/ui/screens/email_list_screen.dart +++ b/lib/ui/screens/email_list_screen.dart @@ -8,6 +8,7 @@ import 'package:intl/intl.dart'; import 'package:sharedinbox/core/models/account.dart'; import 'package:sharedinbox/core/models/email.dart'; import 'package:sharedinbox/core/models/undo_action.dart'; +import 'package:sharedinbox/core/models/user_preferences.dart'; import 'package:sharedinbox/core/repositories/email_repository.dart'; import 'package:sharedinbox/di.dart'; import 'package:sharedinbox/ui/screens/email_action_helpers.dart'; @@ -148,16 +149,21 @@ class _EmailListScreenState extends ConsumerState { Widget build(BuildContext context) { final repo = ref.watch(emailRepositoryProvider); final accountAsync = ref.watch(accountByIdProvider(widget.accountId)); + final prefs = + ref.watch(userPreferencesProvider).value ?? const UserPreferences(); + final menuAtBottom = prefs.menuPosition == MenuPosition.bottom; return Scaffold( - appBar: _buildAppBar(repo, accountAsync), + appBar: _buildAppBar(repo, accountAsync, menuAtBottom: menuAtBottom), drawer: _selecting ? null : FolderDrawer( accountId: widget.accountId, currentMailboxPath: widget.mailboxPath, ), - bottomNavigationBar: _selecting ? _selectionBottomBar() : null, + bottomNavigationBar: _selecting + ? _selectionBottomBar() + : (menuAtBottom ? _folderNavBottomBar() : null), body: Column( children: [ _buildSyncErrorBanner(), @@ -173,12 +179,14 @@ class _EmailListScreenState extends ConsumerState { PreferredSizeWidget _buildAppBar( EmailRepository emailRepo, - AsyncValue accountAsync, - ) { + AsyncValue accountAsync, { + required bool menuAtBottom, + }) { final selectionCount = _searching ? _selectedSearchIds.length : _selectedThreadIds.length; return AppBar( + automaticallyImplyLeading: !menuAtBottom, leading: _selecting ? IconButton( icon: const Icon(Icons.close), @@ -301,6 +309,22 @@ class _EmailListScreenState extends ConsumerState { ); } + Widget _folderNavBottomBar() { + return BottomAppBar( + child: Row( + children: [ + Builder( + builder: (context) => IconButton( + icon: const Icon(Icons.menu), + tooltip: 'Open folders', + onPressed: () => Scaffold.of(context).openDrawer(), + ), + ), + ], + ), + ); + } + Widget _selectionBottomBar() { return BottomAppBar( child: Row( diff --git a/lib/ui/screens/mailbox_list_screen.dart b/lib/ui/screens/mailbox_list_screen.dart index e0417fe..47fc231 100644 --- a/lib/ui/screens/mailbox_list_screen.dart +++ b/lib/ui/screens/mailbox_list_screen.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:sharedinbox/core/models/email.dart'; import 'package:sharedinbox/core/models/mailbox.dart'; +import 'package:sharedinbox/core/models/user_preferences.dart'; import 'package:sharedinbox/core/repositories/email_repository.dart'; import 'package:sharedinbox/di.dart'; import 'package:sharedinbox/ui/widgets/folder_drawer.dart'; @@ -17,8 +18,12 @@ class MailboxListScreen extends ConsumerWidget { final mailboxRepo = ref.watch(mailboxRepositoryProvider); final emailRepo = ref.watch(emailRepositoryProvider); final accountAsync = ref.watch(accountByIdProvider(accountId)); + final prefs = + ref.watch(userPreferencesProvider).value ?? const UserPreferences(); + final menuAtBottom = prefs.menuPosition == MenuPosition.bottom; return Scaffold( appBar: AppBar( + automaticallyImplyLeading: !menuAtBottom, title: const Text('Folders'), actions: [ IconButton( @@ -42,6 +47,19 @@ class MailboxListScreen extends ConsumerWidget { ], ), drawer: FolderDrawer(accountId: accountId), + bottomNavigationBar: menuAtBottom + ? BottomAppBar( + child: Row( + children: [ + IconButton( + icon: const Icon(Icons.menu), + tooltip: 'Open folders', + onPressed: () => Scaffold.of(context).openDrawer(), + ), + ], + ), + ) + : null, body: Column( children: [ // ── Failed-mutation banner ─────────────────────────────────────── diff --git a/lib/ui/screens/user_preferences_screen.dart b/lib/ui/screens/user_preferences_screen.dart new file mode 100644 index 0000000..af18ffe --- /dev/null +++ b/lib/ui/screens/user_preferences_screen.dart @@ -0,0 +1,67 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:sharedinbox/core/models/user_preferences.dart'; +import 'package:sharedinbox/di.dart'; + +class UserPreferencesScreen extends ConsumerWidget { + const UserPreferencesScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final prefsAsync = ref.watch(userPreferencesProvider); + + return Scaffold( + appBar: AppBar(title: const Text('Preferences')), + body: prefsAsync.when( + loading: () => const Center(child: CircularProgressIndicator()), + error: (_, __) => + const Center(child: Text('Error loading preferences')), + data: (prefs) => ListView( + children: [ + ListTile( + title: Text( + 'Menu bar position', + style: Theme.of(context).textTheme.titleSmall, + ), + subtitle: const Text( + 'Where the folder navigation menu is shown in the mailbox view.', + ), + ), + RadioGroup( + groupValue: prefs.menuPosition, + onChanged: (value) { + if (value == null) return; + unawaited( + ref + .read(userPreferencesRepositoryProvider) + .updateMenuPosition(value), + ); + }, + child: const Column( + children: [ + RadioListTile( + title: Text('Bottom (default)'), + subtitle: Text( + 'Open folder navigation from a button at the bottom of the screen.', + ), + value: MenuPosition.bottom, + ), + RadioListTile( + title: Text('Top'), + subtitle: Text( + 'Open folder navigation from the hamburger icon in the top bar.', + ), + value: MenuPosition.top, + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/scripts/check_coverage.dart b/scripts/check_coverage.dart index 64c171f..931bb8a 100644 --- a/scripts/check_coverage.dart +++ b/scripts/check_coverage.dart @@ -20,7 +20,9 @@ const _noCode = { 'lib/core/repositories/sync_log_repository.dart', 'lib/core/repositories/undo_repository.dart', 'lib/core/repositories/search_history_repository.dart', + 'lib/core/repositories/user_preferences_repository.dart', 'lib/core/models/undo_action.dart', + 'lib/core/models/user_preferences.dart', 'lib/core/storage/secure_storage.dart', }; @@ -73,6 +75,8 @@ const _excluded = { 'lib/data/repositories/sync_log_repository_impl.dart', 'lib/data/repositories/undo_repository_impl.dart', 'lib/data/repositories/search_history_repository_impl.dart', + 'lib/data/repositories/user_preferences_repository_impl.dart', + 'lib/ui/screens/user_preferences_screen.dart', 'lib/core/services/update_service.dart', }; diff --git a/test/unit/migration_test.dart b/test/unit/migration_test.dart index 97bad71..aff972b 100644 --- a/test/unit/migration_test.dart +++ b/test/unit/migration_test.dart @@ -14,7 +14,7 @@ void main() { group('Migration', () { test('schemaVersion matches expected value', () async { final db = AppDatabase(NativeDatabase.memory()); - expect(db.schemaVersion, 33); + expect(db.schemaVersion, 34); await db.close(); }); @@ -199,6 +199,9 @@ void main() { expect(syncLogColumns, contains('error_stack_trace')); expect(syncLogColumns, contains('is_permanent')); + // v34: user_preferences table. + await db.customSelect('SELECT count(*) FROM user_preferences').get(); + await db.close(); if (dbFile.existsSync()) dbFile.deleteSync(); }); @@ -391,11 +394,14 @@ void main() { expect(syncLogColumns, contains('error_stack_trace')); expect(syncLogColumns, contains('is_permanent')); + // v34: user_preferences table. + await db.customSelect('SELECT count(*) FROM user_preferences').get(); + await db.close(); if (dbFile.existsSync()) dbFile.deleteSync(); }); - test('fresh install creates all tables at schemaVersion 33', () async { + test('fresh install creates all tables at schemaVersion 34', () async { final db = AppDatabase(NativeDatabase.memory()); await db.select(db.accounts).get(); @@ -422,6 +428,7 @@ void main() { 'local_sieve_scripts', // v29 'share_keys', // v31 'local_sieve_applied', // v32 + 'user_preferences', // v34 ]), ); diff --git a/test/widget/email_list_screen_test.dart b/test/widget/email_list_screen_test.dart index 0798258..3bfca9a 100644 --- a/test/widget/email_list_screen_test.dart +++ b/test/widget/email_list_screen_test.dart @@ -316,7 +316,7 @@ void main() { await tester.pumpAndSettle(); expect(find.text('INBOX'), findsOneWidget); - expect(find.byType(BottomAppBar), findsNothing); + expect(find.byIcon(Icons.close), findsNothing); }); testWidgets('tapping clear icon in search bar clears results', ( diff --git a/test/widget/goldens/email_list_empty.png b/test/widget/goldens/email_list_empty.png index 8d2a371..f220494 100644 Binary files a/test/widget/goldens/email_list_empty.png and b/test/widget/goldens/email_list_empty.png differ diff --git a/test/widget/goldens/email_list_error_banner.png b/test/widget/goldens/email_list_error_banner.png index 6e00942..2baf581 100644 Binary files a/test/widget/goldens/email_list_error_banner.png and b/test/widget/goldens/email_list_error_banner.png differ diff --git a/test/widget/goldens/email_list_search_results.png b/test/widget/goldens/email_list_search_results.png index 47bb8c2..5e2f692 100644 Binary files a/test/widget/goldens/email_list_search_results.png and b/test/widget/goldens/email_list_search_results.png differ diff --git a/test/widget/goldens/email_list_with_emails.png b/test/widget/goldens/email_list_with_emails.png index cc7887e..604b859 100644 Binary files a/test/widget/goldens/email_list_with_emails.png and b/test/widget/goldens/email_list_with_emails.png differ diff --git a/test/widget/helpers.dart b/test/widget/helpers.dart index cc1e04b..208ab0d 100644 --- a/test/widget/helpers.dart +++ b/test/widget/helpers.dart @@ -14,6 +14,7 @@ import 'package:sharedinbox/core/models/discovery_result.dart'; import 'package:sharedinbox/core/models/draft.dart'; import 'package:sharedinbox/core/models/email.dart'; import 'package:sharedinbox/core/models/mailbox.dart'; +import 'package:sharedinbox/core/models/user_preferences.dart'; import 'package:sharedinbox/core/repositories/account_repository.dart'; import 'package:sharedinbox/core/repositories/draft_repository.dart'; import 'package:sharedinbox/core/repositories/email_repository.dart'; @@ -21,6 +22,7 @@ import 'package:sharedinbox/core/repositories/mailbox_repository.dart'; import 'package:sharedinbox/core/repositories/search_history_repository.dart'; import 'package:sharedinbox/core/repositories/share_key_repository.dart'; import 'package:sharedinbox/core/repositories/sync_log_repository.dart'; +import 'package:sharedinbox/core/repositories/user_preferences_repository.dart'; import 'package:sharedinbox/core/services/account_discovery_service.dart'; import 'package:sharedinbox/core/services/connection_test_service.dart'; import 'package:sharedinbox/core/services/managesieve_probe_service.dart'; @@ -39,6 +41,7 @@ import 'package:sharedinbox/ui/screens/email_list_screen.dart'; import 'package:sharedinbox/ui/screens/mailbox_list_screen.dart'; import 'package:sharedinbox/ui/screens/search_screen.dart'; import 'package:sharedinbox/ui/screens/thread_detail_screen.dart'; +import 'package:sharedinbox/ui/screens/user_preferences_screen.dart'; // --------------------------------------------------------------------------- // Fake repositories @@ -431,6 +434,10 @@ Widget buildApp({ path: 'send', builder: (ctx, state) => const AccountSendScreen(), ), + GoRoute( + path: 'preferences', + builder: (ctx, state) => const UserPreferencesScreen(), + ), GoRoute( path: ':accountId/edit', builder: (ctx, state) => EditAccountScreen( @@ -515,6 +522,9 @@ Widget buildApp({ syncLogRepositoryProvider.overrideWithValue( const NoOpSyncLogRepository(), ), + userPreferencesRepositoryProvider.overrideWithValue( + FakeUserPreferencesRepository(), + ), ...overrides, manageSieveProbeServiceProvider.overrideWith( (ref) => _NoOpManageSieveProbeService(), @@ -611,6 +621,23 @@ Email testEmail({ listUnsubscribeHeader: listUnsubscribeHeader, ); +class FakeUserPreferencesRepository implements UserPreferencesRepository { + FakeUserPreferencesRepository({ + this.menuPosition = MenuPosition.bottom, + }); + + MenuPosition menuPosition; + + @override + Stream observePreferences() => + Stream.value(UserPreferences(menuPosition: menuPosition)); + + @override + Future updateMenuPosition(MenuPosition position) async { + menuPosition = position; + } +} + class FakeSearchHistoryRepository implements SearchHistoryRepository { final List _history = []; diff --git a/test/widget/user_preferences_screen_test.dart b/test/widget/user_preferences_screen_test.dart new file mode 100644 index 0000000..61ff92f --- /dev/null +++ b/test/widget/user_preferences_screen_test.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:sharedinbox/core/models/user_preferences.dart'; +import 'package:sharedinbox/di.dart'; +import 'package:sharedinbox/ui/screens/user_preferences_screen.dart'; + +import 'helpers.dart'; + +void main() { + group('UserPreferencesScreen', () { + testWidgets('shows both menu position options', (tester) async { + await tester.pumpWidget( + buildApp( + initialLocation: '/accounts/preferences', + overrides: baseOverrides(), + ), + ); + await tester.pumpAndSettle(); + + expect(find.text('Menu bar position'), findsOneWidget); + expect(find.text('Bottom (default)'), findsOneWidget); + expect(find.text('Top'), findsOneWidget); + }); + + testWidgets('bottom option is selected by default', (tester) async { + await tester.pumpWidget( + buildApp( + initialLocation: '/accounts/preferences', + overrides: baseOverrides(), + ), + ); + await tester.pumpAndSettle(); + + final radioGroup = find.byType(RadioGroup); + final widget = tester.widget>(radioGroup); + expect(widget.groupValue, MenuPosition.bottom); + }); + + testWidgets('tapping Top option updates the repo', (tester) async { + await tester.pumpWidget( + buildApp( + initialLocation: '/accounts/preferences', + overrides: baseOverrides(), + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.text('Top')); + await tester.pumpAndSettle(); + + final repo = ProviderScope.containerOf( + tester.element(find.byType(UserPreferencesScreen)), + ).read(userPreferencesRepositoryProvider) + as FakeUserPreferencesRepository; + + expect(repo.menuPosition, MenuPosition.top); + }); + }); +}