launcher_controller_test.dart 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import 'package:cardash_launcher/data/models/app_entry.dart';
  2. import 'package:cardash_launcher/data/models/slot_type.dart';
  3. import 'package:cardash_launcher/data/models/system_status.dart';
  4. import 'package:cardash_launcher/data/repositories/settings_repository.dart';
  5. import 'package:cardash_launcher/data/services/native_bridge.dart';
  6. import 'package:cardash_launcher/features/home/launcher_controller.dart';
  7. import 'package:flutter_test/flutter_test.dart';
  8. import 'package:shared_preferences/shared_preferences.dart';
  9. class FakeLauncherPlatform implements LauncherPlatform {
  10. FakeLauncherPlatform(this.apps);
  11. final List<AppEntry> apps;
  12. @override
  13. Future<List<AppEntry>> listApps() async => apps;
  14. @override
  15. Future<void> launchApp({
  16. required String packageName,
  17. required String activityName,
  18. }) async {}
  19. @override
  20. Future<bool> isDefaultHome() async => true;
  21. @override
  22. Future<bool> requestDefaultHome() async => true;
  23. @override
  24. Future<SystemStatus> getStatus() async => const SystemStatus();
  25. @override
  26. Future<VolumeInfo> getVolume() async => const VolumeInfo();
  27. @override
  28. Future<void> setVolume(int index) async {}
  29. @override
  30. Future<bool> isMusicPlaying() async => false;
  31. }
  32. List<AppEntry> _apps(int count) {
  33. return List.generate(
  34. count,
  35. (index) => AppEntry(
  36. packageName: 'com.example.app$index',
  37. activityName: 'com.example.app$index.Main',
  38. label: 'App $index',
  39. ),
  40. );
  41. }
  42. Future<LauncherController> _controller({
  43. int appCount = 20,
  44. FakeLauncherPlatform? platform,
  45. }) async {
  46. SharedPreferences.setMockInitialValues({});
  47. final prefs = await SharedPreferences.getInstance();
  48. final controller = LauncherController(
  49. platform ?? FakeLauncherPlatform(_apps(appCount)),
  50. SettingsRepository(prefs),
  51. );
  52. controller.initialize();
  53. return controller;
  54. }
  55. void main() {
  56. TestWidgetsFlutterBinding.ensureInitialized();
  57. test('parses the native system-app flag', () {
  58. final system = AppEntry.fromMap({
  59. 'package': 'com.example.system',
  60. 'activity': 'com.example.system.Main',
  61. 'label': 'System',
  62. 'system': true,
  63. });
  64. final user = AppEntry.fromMap({
  65. 'package': 'com.example.user',
  66. 'activity': 'com.example.user.Main',
  67. 'label': 'User',
  68. 'system': false,
  69. });
  70. expect(system.isSystemApp, isTrue);
  71. expect(user.isSystemApp, isFalse);
  72. });
  73. test('home buttons default to enabled and count the fixed buttons', () async {
  74. final controller = await _controller();
  75. addTearDown(controller.dispose);
  76. expect(controller.isSlotEnabled(SlotType.navigation), isTrue);
  77. expect(controller.isSlotEnabled(SlotType.music), isTrue);
  78. expect(controller.isSlotEnabled(SlotType.radio), isTrue);
  79. expect(controller.homeButtonCount, 5);
  80. expect(controller.pinnedApps, isEmpty);
  81. });
  82. test('disabling a slot hides it and keeps the assignment', () async {
  83. final controller = await _controller();
  84. addTearDown(controller.dispose);
  85. final app = _apps(1).first;
  86. await controller.assign(SlotType.navigation, app);
  87. expect(await controller.setSlotEnabled(SlotType.navigation, false), isNull);
  88. expect(controller.isSlotEnabled(SlotType.navigation), isFalse);
  89. expect(controller.homeButtonCount, 4);
  90. expect(controller.assignmentFor(SlotType.navigation)?.label, 'App 0');
  91. expect(await controller.setSlotEnabled(SlotType.navigation, true), isNull);
  92. expect(controller.homeButtonCount, 5);
  93. });
  94. test('pinned apps can be added once and removed again', () async {
  95. final controller = await _controller();
  96. addTearDown(controller.dispose);
  97. final apps = _apps(3);
  98. expect(await controller.addPinnedApp(apps[0]), isNull);
  99. expect(controller.pinnedApps.length, 1);
  100. expect(controller.homeButtonCount, 6);
  101. expect(await controller.addPinnedApp(apps[0]), contains('already'));
  102. await controller.removePinnedApp(apps[0].toAssignment());
  103. expect(controller.pinnedApps, isEmpty);
  104. expect(controller.homeButtonCount, 5);
  105. });
  106. test('the home screen stops at 16 buttons', () async {
  107. final controller = await _controller();
  108. addTearDown(controller.dispose);
  109. final apps = _apps(20);
  110. for (var index = 0; index < 11; index++) {
  111. expect(await controller.addPinnedApp(apps[index]), isNull);
  112. }
  113. expect(controller.homeButtonCount, 16);
  114. expect(await controller.addPinnedApp(apps[11]), contains('full'));
  115. expect(controller.pinnedApps.length, 11);
  116. await controller.removePinnedApp(apps[0].toAssignment());
  117. expect(controller.homeButtonCount, 15);
  118. expect(await controller.addPinnedApp(apps[11]), isNull);
  119. expect(controller.homeButtonCount, 16);
  120. });
  121. test('pinned labels and custom icons persist', () async {
  122. SharedPreferences.setMockInitialValues({});
  123. final prefs = await SharedPreferences.getInstance();
  124. final apps = _apps(3);
  125. final platform = FakeLauncherPlatform(apps);
  126. final first = LauncherController(platform, SettingsRepository(prefs));
  127. first.initialize();
  128. addTearDown(first.dispose);
  129. expect(await first.addPinnedApp(apps[0]), isNull);
  130. final pinned = first.pinnedApps.first;
  131. await first.renamePinnedApp(pinned, 'My shortcut');
  132. await first.setPinnedIcon(pinned, apps[1]);
  133. final second = LauncherController(platform, SettingsRepository(prefs));
  134. second.initialize();
  135. addTearDown(second.dispose);
  136. expect(second.pinnedApps.length, 1);
  137. expect(second.pinnedApps.first.label, 'My shortcut');
  138. expect(second.pinnedApps.first.iconPackageName, apps[1].packageName);
  139. expect(second.pinnedApps.first.iconActivityName, apps[1].activityName);
  140. });
  141. test('pinned apps and slot visibility survive a new controller', () async {
  142. SharedPreferences.setMockInitialValues({});
  143. final prefs = await SharedPreferences.getInstance();
  144. final platform = FakeLauncherPlatform(_apps(3));
  145. final settings = SettingsRepository(prefs);
  146. final first = LauncherController(platform, settings);
  147. first.initialize();
  148. addTearDown(first.dispose);
  149. await first.setSlotEnabled(SlotType.radio, false);
  150. expect(await first.addPinnedApp(_apps(1).first), isNull);
  151. final second = LauncherController(platform, SettingsRepository(prefs));
  152. second.initialize();
  153. addTearDown(second.dispose);
  154. expect(second.isSlotEnabled(SlotType.radio), isFalse);
  155. expect(second.pinnedApps.length, 1);
  156. expect(second.pinnedApps.first.label, 'App 0');
  157. expect(second.homeButtonCount, 5);
  158. });
  159. }