import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:plasmoid/main.dart'; import 'package:plasmoid/preferences.dart'; import 'package:plasmoid/settings.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; Map _app(String name) => { 'app_name': name, 'package_name': 'com.example.${name.toLowerCase()}', 'apk_file_path': '', 'version_name': '1', 'version_code': 1, 'data_dir': '', 'system_app': false, 'install_time': 0, 'update_time': 0, 'is_enabled': true, 'category': -1, }; void main() { final launched = []; var installed = ['Alpha', 'Bravo', 'Charlie']; setUp(() async { launched.clear(); installed = ['Alpha', 'Bravo', 'Charlie']; GoogleFonts.config.allowRuntimeFetching = false; SharedPreferences.setMockInitialValues({'sortLastUsed': false}); await SharedPreferencesService().initialize(); final messenger = TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; messenger.setMockMethodCallHandler(const MethodChannel('g123k/device_apps'), (call) async { switch (call.method) { case 'getInstalledApps': return installed.map(_app).toList(); case 'openApp': launched.add((call.arguments as Map)['package_name'] as String); return true; } return null; }); messenger.setMockStreamHandler(const EventChannel('g123k/device_apps_events'), MockStreamHandler.inline(onListen: (_, __) {})); }); Future pumpLauncher(WidgetTester tester) async { await tester.pumpWidget(ChangeNotifierProvider( create: (_) => AppSettings(), child: MaterialApp(home: AppLauncherScreen()), )); await tester.pumpAndSettle(); } Future press(WidgetTester tester, LogicalKeyboardKey key) async { await tester.sendKeyEvent(key); await tester.pumpAndSettle(); } Future hold(WidgetTester tester, LogicalKeyboardKey key) async { await tester.sendKeyDownEvent(key); await tester.pump(const Duration(seconds: 1)); await tester.sendKeyUpEvent(key); await tester.pumpAndSettle(); } ListTile tile(WidgetTester tester, String name) => tester.widget(find.ancestor(of: find.text(name), matching: find.byType(ListTile))); EditableText searchField(WidgetTester tester) => tester.widget(find.byType(EditableText)); testWidgets('down enters the list and up/down move the selection', (tester) async { await pumpLauncher(tester); expect(searchField(tester).focusNode.hasFocus, isTrue); await press(tester, LogicalKeyboardKey.arrowDown); expect(searchField(tester).focusNode.hasFocus, isFalse); expect(tile(tester, 'Alpha').selected, isTrue); await press(tester, LogicalKeyboardKey.arrowDown); await press(tester, LogicalKeyboardKey.arrowDown); await press(tester, LogicalKeyboardKey.arrowDown); expect(tile(tester, 'Charlie').selected, isTrue); await press(tester, LogicalKeyboardKey.arrowUp); expect(tile(tester, 'Bravo').selected, isTrue); await press(tester, LogicalKeyboardKey.select); expect(launched, ['com.example.bravo']); }); testWidgets('left returns focus to the search field', (tester) async { await pumpLauncher(tester); await press(tester, LogicalKeyboardKey.arrowDown); await press(tester, LogicalKeyboardKey.arrowLeft); expect(searchField(tester).focusNode.hasFocus, isTrue); expect(tile(tester, 'Alpha').selected, isFalse); }); testWidgets('right clears the search text, then opens and focuses the menu', (tester) async { await pumpLauncher(tester); await tester.enterText(find.byType(TextField), 'al'); await tester.pumpAndSettle(); await press(tester, LogicalKeyboardKey.arrowRight); expect(searchField(tester).controller.text, isEmpty); await press(tester, LogicalKeyboardKey.arrowRight); expect(find.text('Refresh App List'), findsOneWidget); final focused = FocusManager.instance.primaryFocus!.context!; expect(find.descendant(of: find.byWidget(focused.widget), matching: find.text('Refresh App List')), findsOneWidget); }); testWidgets('right in the list opens the menu', (tester) async { await pumpLauncher(tester); await press(tester, LogicalKeyboardKey.arrowDown); await press(tester, LogicalKeyboardKey.arrowRight); expect(find.text('Refresh App List'), findsOneWidget); }); testWidgets('long left pins and long right demotes the selected app', (tester) async { await pumpLauncher(tester); await press(tester, LogicalKeyboardKey.arrowDown); await press(tester, LogicalKeyboardKey.arrowDown); await press(tester, LogicalKeyboardKey.arrowDown); await hold(tester, LogicalKeyboardKey.arrowLeft); expect(SharedPreferencesService().getBool('isPinned_com.example.charlie', false), isTrue); expect(tile(tester, 'Charlie').selected, isTrue); expect(searchField(tester).focusNode.hasFocus, isFalse); await hold(tester, LogicalKeyboardKey.arrowRight); expect(SharedPreferencesService().getBool('isPinned_com.example.charlie', false), isFalse); await hold(tester, LogicalKeyboardKey.arrowRight); expect(SharedPreferencesService().getBool('isDemoted_com.example.charlie', false), isTrue); expect(tile(tester, 'Charlie').selected, isTrue); expect(find.text('Refresh App List'), findsNothing); }); testWidgets('long center toggles the context menu with focus on it', (tester) async { await pumpLauncher(tester); await press(tester, LogicalKeyboardKey.arrowDown); await hold(tester, LogicalKeyboardKey.select); expect(find.text('Edit Tags'), findsOneWidget); expect(find.text('Edit Tags for Alpha'), findsNothing); final focused = FocusManager.instance.primaryFocus!.context!; expect(find.descendant(of: find.byWidget(focused.widget), matching: find.text('Edit Tags')), findsOneWidget); await hold(tester, LogicalKeyboardKey.select); expect(find.text('Edit Tags'), findsNothing); expect(launched, isEmpty); }); testWidgets('moving the selection scrolls it into view', (tester) async { installed = [for (var i = 10; i < 70; i++) 'App$i']; await pumpLauncher(tester); await press(tester, LogicalKeyboardKey.arrowDown); for (var i = 0; i < 40; i++) { await press(tester, LogicalKeyboardKey.arrowDown); } expect(tile(tester, 'App50').selected, isTrue); expect(tester.getRect(find.text('App50')).bottom, lessThanOrEqualTo(tester.view.physicalSize.height / tester.view.devicePixelRatio)); await press(tester, LogicalKeyboardKey.arrowLeft); await press(tester, LogicalKeyboardKey.arrowDown); expect(tile(tester, 'App10').selected, isTrue); }); }