jellyfin_album_screen_test.dart 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import 'package:cardash_launcher/core/theme/app_theme.dart';
  2. import 'package:cardash_launcher/data/repositories/settings_repository.dart';
  3. import 'package:cardash_launcher/data/services/jellyfin_library_service.dart';
  4. import 'package:cardash_launcher/features/home/gps_speed_controller.dart';
  5. import 'package:cardash_launcher/features/home/obd_controller.dart';
  6. import 'package:cardash_launcher/features/home/system_controller.dart';
  7. import 'package:cardash_launcher/features/home/telemetry_settings_controller.dart';
  8. import 'package:cardash_launcher/features/home/weather_controller.dart';
  9. import 'package:cardash_launcher/features/jellyfin/jellyfin_album_screen.dart';
  10. import 'package:cardash_launcher/features/jellyfin/jellyfin_playback_controller.dart';
  11. import 'package:cardash_launcher/features/settings/jellyfin_controller.dart';
  12. import 'package:flutter/material.dart';
  13. import 'package:flutter_test/flutter_test.dart';
  14. import 'package:provider/provider.dart';
  15. import 'package:shared_preferences/shared_preferences.dart';
  16. import 'jellyfin_fakes.dart';
  17. void main() {
  18. testWidgets('shows the album, track list and transport, and changes tracks', (
  19. tester,
  20. ) async {
  21. await tester.binding.setSurfaceSize(const Size(1280, 800));
  22. addTearDown(() => tester.binding.setSurfaceSize(null));
  23. final jellyfin = await configuredJellyfinController();
  24. final gateway = FakeJellyfinLibraryGateway()
  25. ..albumFavorite = true
  26. ..tracks = {
  27. 'album-1': [
  28. track('t1', albumId: 'album-1', albumName: 'Album 1', index: 1),
  29. track('t2', albumId: 'album-1', albumName: 'Album 1', index: 2),
  30. track('t3', albumId: 'album-1', albumName: 'Album 1', index: 3),
  31. ],
  32. };
  33. final library = JellyfinLibraryService(
  34. connectionProvider: () => jellyfin.connection,
  35. gateway: gateway,
  36. );
  37. final platform = FakeJellyfinPlaybackPlatform();
  38. final playback = JellyfinPlaybackController(
  39. platform,
  40. library,
  41. jellyfin,
  42. pollInterval: const Duration(hours: 1),
  43. );
  44. final albumOne = album('album-1', year: 2025);
  45. await playback.playAlbum(albumOne);
  46. final system = SystemController(FakeSystemPlatform());
  47. final prefs = await SharedPreferences.getInstance();
  48. final settings = SettingsRepository(prefs);
  49. final telemetry = FakeTelemetryPlatform();
  50. final obd = ObdController(telemetry, settings)..initialize();
  51. final gps = GpsSpeedController(telemetry);
  52. final weather = WeatherController(telemetry);
  53. final telemetrySettings = TelemetrySettingsController(
  54. settings,
  55. weather,
  56. gps,
  57. )..initialize();
  58. await tester.pumpWidget(
  59. MultiProvider(
  60. providers: [
  61. Provider<JellyfinLibraryService>.value(value: library),
  62. ChangeNotifierProvider<JellyfinController>.value(value: jellyfin),
  63. ChangeNotifierProvider<JellyfinPlaybackController>.value(
  64. value: playback,
  65. ),
  66. ChangeNotifierProvider<SystemController>.value(value: system),
  67. ChangeNotifierProvider<ObdController>.value(value: obd),
  68. ChangeNotifierProvider<GpsSpeedController>.value(value: gps),
  69. ChangeNotifierProvider<WeatherController>.value(value: weather),
  70. ChangeNotifierProvider<TelemetrySettingsController>.value(
  71. value: telemetrySettings,
  72. ),
  73. ],
  74. child: MaterialApp(
  75. theme: AppTheme.light(),
  76. home: JellyfinAlbumScreen(album: albumOne),
  77. ),
  78. ),
  79. );
  80. await tester.pumpAndSettle();
  81. expect(find.text('Album album-1 (2025)'), findsOneWidget);
  82. expect(find.text('Tracks'), findsOneWidget);
  83. expect(find.text('Track t1'), findsWidgets);
  84. expect(find.text('Track t2'), findsOneWidget);
  85. expect(find.text('Track t3'), findsOneWidget);
  86. expect(find.text('Now playing'), findsOneWidget);
  87. expect(find.byType(Slider), findsOneWidget);
  88. expect(find.bySemanticsLabel('Previous track'), findsOneWidget);
  89. expect(find.bySemanticsLabel('Pause music'), findsOneWidget);
  90. expect(find.bySemanticsLabel('Next track'), findsOneWidget);
  91. expect(find.bySemanticsLabel('Volume down'), findsOneWidget);
  92. expect(find.bySemanticsLabel('Volume up'), findsOneWidget);
  93. expect(find.bySemanticsLabel('Remove from favorites'), findsOneWidget);
  94. expect(find.byKey(const ValueKey('jellyfin-status-strip')), findsOneWidget);
  95. expect(find.bySemanticsLabel('Go to home'), findsOneWidget);
  96. final coverRect = tester.getRect(
  97. find.byKey(const ValueKey('jellyfin-album-cover')),
  98. );
  99. final cardRect = tester.getRect(
  100. find.byKey(const ValueKey('jellyfin-album-progress-card')),
  101. );
  102. expect(cardRect.bottom, moreOrLessEquals(coverRect.bottom, epsilon: 0.5));
  103. final trackListRect = tester.getRect(
  104. find.byKey(const ValueKey('jellyfin-album-track-list')),
  105. );
  106. expect(trackListRect.top, greaterThan(coverRect.bottom));
  107. expect(tester.takeException(), isNull);
  108. await tester.drag(find.byType(Slider), const Offset(120, 0));
  109. await tester.pumpAndSettle();
  110. expect(
  111. platform.commands.any((command) => command.startsWith('seek:')),
  112. isTrue,
  113. );
  114. await tester.tap(find.bySemanticsLabel('Remove from favorites'));
  115. await tester.pumpAndSettle();
  116. expect(gateway.setFavoriteCalls, 1);
  117. expect(gateway.lastFavoriteValue, isFalse);
  118. expect(find.bySemanticsLabel('Add to favorites'), findsOneWidget);
  119. await tester.tap(find.bySemanticsLabel('Next track'));
  120. await tester.pumpAndSettle();
  121. expect(platform.commands, contains('next'));
  122. expect(playback.currentItem?.trackId, 't2');
  123. expect(find.text('Track t2'), findsWidgets);
  124. await tester.tap(find.text('Track t3'));
  125. await tester.pumpAndSettle();
  126. expect(platform.commands, contains('seekToIndex:2'));
  127. expect(playback.currentItem?.trackId, 't3');
  128. await tester.tap(find.bySemanticsLabel('Previous track'));
  129. await tester.pumpAndSettle();
  130. expect(platform.commands, contains('previous'));
  131. expect(playback.currentItem?.trackId, 't2');
  132. await tester.binding.setSurfaceSize(const Size(1024, 600));
  133. await tester.pumpAndSettle();
  134. final narrowCover = tester.getRect(
  135. find.byKey(const ValueKey('jellyfin-album-cover')),
  136. );
  137. final narrowCard = tester.getRect(
  138. find.byKey(const ValueKey('jellyfin-album-progress-card')),
  139. );
  140. expect(narrowCard.bottom, greaterThanOrEqualTo(narrowCover.bottom));
  141. expect(tester.takeException(), isNull);
  142. await tester.pumpWidget(const SizedBox.shrink());
  143. playback.dispose();
  144. jellyfin.dispose();
  145. system.dispose();
  146. obd.dispose();
  147. gps.dispose();
  148. weather.dispose();
  149. telemetrySettings.dispose();
  150. });
  151. }