jellyfin_dashboard_screen_test.dart 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import 'package:cardash_launcher/core/theme/app_theme.dart';
  2. import 'package:cardash_launcher/data/models/jellyfin_library.dart';
  3. import 'package:cardash_launcher/data/models/jellyfin_models.dart';
  4. import 'package:cardash_launcher/data/services/jellyfin_library_service.dart';
  5. import 'package:cardash_launcher/features/jellyfin/jellyfin_dashboard_screen.dart';
  6. import 'package:cardash_launcher/features/jellyfin/jellyfin_playback_controller.dart';
  7. import 'package:cardash_launcher/features/settings/jellyfin_controller.dart';
  8. import 'package:flutter/material.dart';
  9. import 'package:flutter_test/flutter_test.dart';
  10. import 'package:provider/provider.dart';
  11. import 'jellyfin_fakes.dart';
  12. class _Fixture {
  13. _Fixture({
  14. required this.jellyfin,
  15. required this.gateway,
  16. required this.library,
  17. required this.playback,
  18. });
  19. final JellyfinController jellyfin;
  20. final FakeJellyfinLibraryGateway gateway;
  21. final JellyfinLibraryService library;
  22. final JellyfinPlaybackController playback;
  23. }
  24. Future<_Fixture> _fixture({List<String> genres = const ['metal']}) async {
  25. final jellyfin = await configuredJellyfinController();
  26. await jellyfin.setGenreSelections([
  27. for (final id in genres) JellyfinGenreSelection(id: id, label: id),
  28. ]);
  29. final gateway = FakeJellyfinLibraryGateway()
  30. ..played = [
  31. track('a2', albumId: 'album-a', albumName: 'Album A'),
  32. track('a1', albumId: 'album-a', albumName: 'Album A'),
  33. track('b1', albumId: 'album-b', albumName: 'Album B'),
  34. track('c1', albumId: 'album-c', albumName: 'Album C'),
  35. track('d1', albumId: 'album-d', albumName: 'Album D'),
  36. track('e1', albumId: 'album-e', albumName: 'Album E'),
  37. track('f1', albumId: 'album-f', albumName: 'Album F'),
  38. ]
  39. ..added = [
  40. album('new-1'),
  41. album('new-2'),
  42. album('new-3'),
  43. album('new-4'),
  44. album('new-5'),
  45. album('new-6'),
  46. ]
  47. ..random = [
  48. album('r1'),
  49. album('r2'),
  50. album('r3'),
  51. album('r4'),
  52. album('r5'),
  53. album('r6'),
  54. ]
  55. ..genres = const ['Metal', 'Jazz'];
  56. final library = testLibraryService(jellyfin, gateway);
  57. final playback = JellyfinPlaybackController(
  58. FakeJellyfinPlaybackPlatform(),
  59. library,
  60. jellyfin,
  61. pollInterval: const Duration(hours: 1),
  62. );
  63. return _Fixture(
  64. jellyfin: jellyfin,
  65. gateway: gateway,
  66. library: library,
  67. playback: playback,
  68. );
  69. }
  70. Future<void> _pump(WidgetTester tester, _Fixture fixture) async {
  71. await tester.binding.setSurfaceSize(const Size(1280, 800));
  72. addTearDown(() => tester.binding.setSurfaceSize(null));
  73. addTearDown(fixture.playback.dispose);
  74. addTearDown(fixture.jellyfin.dispose);
  75. await tester.pumpWidget(
  76. MultiProvider(
  77. providers: [
  78. Provider<JellyfinLibraryService>.value(value: fixture.library),
  79. ChangeNotifierProvider<JellyfinController>.value(
  80. value: fixture.jellyfin,
  81. ),
  82. ChangeNotifierProvider<JellyfinPlaybackController>.value(
  83. value: fixture.playback,
  84. ),
  85. ],
  86. child: MaterialApp(
  87. theme: AppTheme.light(),
  88. home: const JellyfinDashboardScreen(),
  89. ),
  90. ),
  91. );
  92. await tester.pumpAndSettle();
  93. }
  94. void main() {
  95. testWidgets('renders six cards per row on a wide screen', (tester) async {
  96. final fixture = await _fixture();
  97. await _pump(tester, fixture);
  98. expect(find.text('Recently played'), findsOneWidget);
  99. expect(find.text('Recently added'), findsOneWidget);
  100. expect(find.text('Random'), findsOneWidget);
  101. expect(find.text('Artists'), findsOneWidget);
  102. expect(find.text('Album A'), findsWidgets);
  103. expect(find.text('Album F'), findsOneWidget);
  104. expect(find.text('Album new-1'), findsOneWidget);
  105. expect(find.text('Album new-6'), findsOneWidget);
  106. expect(find.text('Album r1'), findsOneWidget);
  107. expect(find.text('Album r6'), findsOneWidget);
  108. expect(find.bySemanticsLabel('Shuffle random albums'), findsOneWidget);
  109. expect(tester.takeException(), isNull);
  110. });
  111. testWidgets('shows the genre prompt when no genres are selected', (
  112. tester,
  113. ) async {
  114. final fixture = await _fixture(genres: const []);
  115. await _pump(tester, fixture);
  116. expect(find.text('Choose up to 5 genres in Settings.'), findsOneWidget);
  117. expect(find.bySemanticsLabel('Shuffle random albums'), findsNothing);
  118. expect(fixture.gateway.randomCalls, 0);
  119. });
  120. testWidgets('shows an inline error with retry when the server fails', (
  121. tester,
  122. ) async {
  123. final fixture = await _fixture();
  124. fixture.gateway.error = JellyfinUnreachable('offline');
  125. await _pump(tester, fixture);
  126. expect(
  127. find.text('Jellyfin is unreachable. Check the server and try again.'),
  128. findsOneWidget,
  129. );
  130. expect(find.text('Retry'), findsOneWidget);
  131. expect(find.bySemanticsLabel('Shuffle random albums'), findsNothing);
  132. });
  133. }