| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- import 'package:cardash_launcher/core/theme/app_theme.dart';
- import 'package:cardash_launcher/data/models/jellyfin_library.dart';
- import 'package:cardash_launcher/data/models/jellyfin_models.dart';
- import 'package:cardash_launcher/data/repositories/settings_repository.dart';
- import 'package:cardash_launcher/data/services/jellyfin_library_service.dart';
- import 'package:cardash_launcher/features/home/gps_speed_controller.dart';
- import 'package:cardash_launcher/features/home/obd_controller.dart';
- import 'package:cardash_launcher/features/home/system_controller.dart';
- import 'package:cardash_launcher/features/home/telemetry_settings_controller.dart';
- import 'package:cardash_launcher/features/home/weather_controller.dart';
- import 'package:cardash_launcher/features/jellyfin/jellyfin_dashboard_screen.dart';
- import 'package:cardash_launcher/features/jellyfin/jellyfin_playback_controller.dart';
- import 'package:cardash_launcher/features/settings/jellyfin_controller.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_test/flutter_test.dart';
- import 'package:provider/provider.dart';
- import 'package:shared_preferences/shared_preferences.dart';
- import 'jellyfin_fakes.dart';
- class _Fixture {
- _Fixture({
- required this.jellyfin,
- required this.gateway,
- required this.library,
- required this.playback,
- });
- final JellyfinController jellyfin;
- final FakeJellyfinLibraryGateway gateway;
- final JellyfinLibraryService library;
- final JellyfinPlaybackController playback;
- }
- Future<_Fixture> _fixture({List<String> genres = const ['metal']}) async {
- final jellyfin = await configuredJellyfinController();
- await jellyfin.setGenreSelections([
- for (final id in genres) JellyfinGenreSelection(id: id, label: id),
- ]);
- final gateway = FakeJellyfinLibraryGateway()
- ..played = [
- track('a2', albumId: 'album-a', albumName: 'Album A'),
- track('a1', albumId: 'album-a', albumName: 'Album A'),
- track('b1', albumId: 'album-b', albumName: 'Album B'),
- track('c1', albumId: 'album-c', albumName: 'Album C'),
- track('d1', albumId: 'album-d', albumName: 'Album D'),
- track('e1', albumId: 'album-e', albumName: 'Album E'),
- track('f1', albumId: 'album-f', albumName: 'Album F'),
- ]
- ..added = [
- album('new-1'),
- album('new-2'),
- album('new-3'),
- album('new-4'),
- album('new-5'),
- album('new-6'),
- ]
- ..random = [
- album('r1'),
- album('r2'),
- album('r3'),
- album('r4'),
- album('r5'),
- album('r6'),
- ]
- ..genres = const ['Metal', 'Jazz'];
- final library = testLibraryService(jellyfin, gateway);
- final playback = JellyfinPlaybackController(
- FakeJellyfinPlaybackPlatform(),
- library,
- jellyfin,
- pollInterval: const Duration(hours: 1),
- );
- return _Fixture(
- jellyfin: jellyfin,
- gateway: gateway,
- library: library,
- playback: playback,
- );
- }
- Future<void> _pump(
- WidgetTester tester,
- _Fixture fixture, {
- JellyfinAlbum? openAlbum,
- }) async {
- await tester.binding.setSurfaceSize(const Size(1280, 800));
- addTearDown(() => tester.binding.setSurfaceSize(null));
- addTearDown(fixture.playback.dispose);
- addTearDown(fixture.jellyfin.dispose);
- final system = SystemController(FakeSystemPlatform());
- addTearDown(system.dispose);
- final prefs = await SharedPreferences.getInstance();
- final settings = SettingsRepository(prefs);
- final telemetry = FakeTelemetryPlatform();
- final obd = ObdController(telemetry, settings)..initialize();
- final gps = GpsSpeedController(telemetry);
- final weather = WeatherController(telemetry);
- final telemetrySettings = TelemetrySettingsController(settings, weather, gps)
- ..initialize();
- addTearDown(obd.dispose);
- addTearDown(gps.dispose);
- addTearDown(weather.dispose);
- addTearDown(telemetrySettings.dispose);
- await tester.pumpWidget(
- MultiProvider(
- providers: [
- Provider<JellyfinLibraryService>.value(value: fixture.library),
- ChangeNotifierProvider<JellyfinController>.value(
- value: fixture.jellyfin,
- ),
- ChangeNotifierProvider<JellyfinPlaybackController>.value(
- value: fixture.playback,
- ),
- ChangeNotifierProvider<SystemController>.value(value: system),
- ChangeNotifierProvider<ObdController>.value(value: obd),
- ChangeNotifierProvider<GpsSpeedController>.value(value: gps),
- ChangeNotifierProvider<WeatherController>.value(value: weather),
- ChangeNotifierProvider<TelemetrySettingsController>.value(
- value: telemetrySettings,
- ),
- ],
- child: MaterialApp(
- theme: AppTheme.light(),
- home: JellyfinDashboardScreen(openAlbum: openAlbum),
- ),
- ),
- );
- await tester.pumpAndSettle();
- }
- void main() {
- testWidgets('renders six cards per row on a wide screen', (tester) async {
- final fixture = await _fixture();
- await _pump(tester, fixture);
- expect(find.text('Recently played'), findsOneWidget);
- expect(find.text('Recently added'), findsOneWidget);
- expect(find.text('Random'), findsOneWidget);
- expect(find.text('Artists'), findsOneWidget);
- expect(find.text('Album A'), findsWidgets);
- expect(find.text('Album F'), findsOneWidget);
- expect(find.text('Album new-1'), findsOneWidget);
- expect(find.text('Album new-6'), findsOneWidget);
- expect(find.text('Album r1'), findsOneWidget);
- expect(find.text('Album r6'), findsOneWidget);
- expect(find.bySemanticsLabel('Shuffle random albums'), findsOneWidget);
- expect(find.byKey(const ValueKey('jellyfin-status-strip')), findsOneWidget);
- expect(find.bySemanticsLabel('Go to home'), findsOneWidget);
- expect(tester.takeException(), isNull);
- });
- testWidgets('shows the genre prompt when no genres are selected', (
- tester,
- ) async {
- final fixture = await _fixture(genres: const []);
- await _pump(tester, fixture);
- expect(find.text('Choose up to 5 genres in Settings.'), findsOneWidget);
- expect(find.bySemanticsLabel('Shuffle random albums'), findsNothing);
- expect(fixture.gateway.randomCalls, 0);
- });
- testWidgets('shows an inline error with retry when the server fails', (
- tester,
- ) async {
- final fixture = await _fixture();
- fixture.gateway.error = JellyfinUnreachable('offline');
- await _pump(tester, fixture);
- expect(
- find.text('Jellyfin is unreachable. Check the server and try again.'),
- findsOneWidget,
- );
- expect(find.text('Retry'), findsOneWidget);
- expect(find.bySemanticsLabel('Shuffle random albums'), findsNothing);
- });
- testWidgets('favorites tab shows a scrollable album grid', (tester) async {
- final fixture = await _fixture();
- fixture.gateway.favorites = [
- album('fav-1'),
- album('fav-2'),
- album('fav-3'),
- ];
- await _pump(tester, fixture);
- await tester.tap(find.text('Favorites'));
- await tester.pumpAndSettle();
- expect(find.text('Album fav-1'), findsOneWidget);
- expect(find.text('Album fav-2'), findsOneWidget);
- expect(find.text('Album fav-3'), findsOneWidget);
- expect(find.text('Recently played'), findsNothing);
- expect(fixture.gateway.favoriteCalls, 1);
- expect(tester.takeException(), isNull);
- });
- testWidgets('favorites tab shows No favorites when empty', (tester) async {
- final fixture = await _fixture();
- await _pump(tester, fixture);
- await tester.tap(find.text('Favorites'));
- await tester.pumpAndSettle();
- expect(find.text('No favorites'), findsOneWidget);
- expect(find.text('Recently played'), findsNothing);
- expect(tester.takeException(), isNull);
- });
- testWidgets('openAlbum opens the album page and Back returns to Albums', (
- tester,
- ) async {
- final fixture = await _fixture();
- fixture.gateway.tracks = {
- 'album-a': [track('a1', albumId: 'album-a', albumName: 'Album A')],
- };
- await _pump(tester, fixture, openAlbum: album('album-a'));
- expect(find.text('Album album-a'), findsWidgets);
- expect(find.text('Tracks'), findsOneWidget);
- await tester.tap(find.bySemanticsLabel('Back').last);
- await tester.pumpAndSettle();
- expect(find.text('Recently played'), findsOneWidget);
- expect(find.text('Albums'), findsOneWidget);
- expect(find.text('Favorites'), findsOneWidget);
- expect(tester.takeException(), isNull);
- });
- }
|