Files
plezy/test/screens/music/album_detail_screen_test.dart
T
edde746 e7f97cc090 refactor(music): move the playback stub beside the tests that use it
StubMusicPlaybackService is a base for test doubles with no production
caller, so `check-unused-code lib` flagged it and the analysis job
failed. Moves it to test/test_helpers/, where shared fakes belong.
2026-07-26 23:05:53 +02:00

162 lines
5.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:plezy/i18n/strings.g.dart';
import 'package:plezy/media/ids.dart';
import 'package:plezy/media/media_backend.dart';
import 'package:plezy/media/media_item.dart';
import 'package:plezy/media/media_kind.dart';
import 'package:plezy/media/media_server_client.dart';
import 'package:plezy/media/server_capabilities.dart';
import 'package:plezy/providers/multi_server_provider.dart';
import 'package:plezy/screens/music/album_detail_screen.dart';
import 'package:plezy/services/multi_server_manager.dart';
import 'package:plezy/services/music/music_playback_service.dart';
import 'package:plezy/services/settings_service.dart';
import 'package:plezy/theme/mono_theme.dart';
import 'package:plezy/widgets/music/track_row.dart';
import 'package:provider/provider.dart';
import '../../test_helpers/prefs.dart';
import '../../test_helpers/media_items.dart';
import '../../test_helpers/multi_server_fixtures.dart';
import '../../test_helpers/stub_music_playback_service.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
resetSharedPreferencesForTest();
SettingsService.resetForTesting();
LocaleSettings.setLocaleSync(AppLocale.en);
});
testWidgets('renders header and track rows with disc headers for a multi-disc album', (tester) async {
final harness = await _createHarness(_multiDiscTracks());
await tester.pumpWidget(harness.wrap(const AlbumDetailScreen(album: _album)));
await tester.pumpAndSettle();
// Header: album title (app bar + header), tappable artist line, metadata.
expect(find.text('Test Album'), findsWidgets);
expect(find.text('Test Artist'), findsOneWidget);
expect(find.textContaining('2001'), findsOneWidget);
expect(find.textContaining(t.music.trackCount(n: 3)), findsOneWidget);
// Track rows, grouped under per-disc headers.
expect(find.byType(TrackRow), findsNWidgets(3));
expect(find.text(t.music.discNumber(n: 1)), findsOneWidget);
expect(find.text(t.music.discNumber(n: 2)), findsOneWidget);
expect(find.text('Track One'), findsOneWidget);
expect(find.text('Track Two'), findsOneWidget);
expect(find.text('Track Three'), findsOneWidget);
// Track numbers restart per disc.
expect(find.text('1'), findsNWidgets(2));
});
}
const _album = MediaItem.plex(
id: 'album_1',
kind: MediaKind.album,
title: 'Test Album',
parentId: 'artist_1',
parentTitle: 'Test Artist',
year: 2001,
serverId: 'server_1',
serverName: 'Server',
);
List<MediaItem> _multiDiscTracks() {
MediaItem track({required String id, required String title, required int disc, required int number}) {
return testMediaItem(
id: id,
backend: MediaBackend.plex,
kind: MediaKind.track,
title: title,
parentId: 'album_1',
parentTitle: 'Test Album',
grandparentId: 'artist_1',
grandparentTitle: 'Test Artist',
parentIndex: disc,
index: number,
durationMs: 200000,
serverId: 'server_1',
serverName: 'Server',
);
}
return [
track(id: 'track_1', title: 'Track One', disc: 1, number: 1),
track(id: 'track_2', title: 'Track Two', disc: 1, number: 2),
track(id: 'track_3', title: 'Track Three', disc: 2, number: 1),
];
}
Future<_AlbumHarness> _createHarness(List<MediaItem> tracks) async {
await SettingsService.getInstance();
final client = _FakeMusicClient(tracks);
final manager = MultiServerManager()..debugRegisterClientForTesting(client);
final multiServerProvider = testMultiServerProvider(manager);
addTearDown(multiServerProvider.dispose);
return _AlbumHarness(client: client, multiServerProvider: multiServerProvider);
}
class _AlbumHarness {
final _FakeMusicClient client;
final MultiServerProvider multiServerProvider;
const _AlbumHarness({required this.client, required this.multiServerProvider});
Widget wrap(Widget child) {
return TranslationProvider(
child: MultiProvider(
providers: [
ChangeNotifierProvider<MultiServerProvider>.value(value: multiServerProvider),
ChangeNotifierProvider<MusicPlaybackService>(create: (_) => StubMusicPlaybackService()),
],
child: MaterialApp(
theme: monoTheme(dark: true),
home: SizedBox(width: 1280, height: 720, child: child),
),
),
);
}
}
class _FakeMusicClient implements MediaServerClient {
final List<MediaItem> tracks;
final List<String> fetchedAlbumIds = [];
_FakeMusicClient(this.tracks);
@override
ServerId get serverId => ServerId('server_1');
@override
String? get serverName => 'Server';
@override
MediaBackend get backend => MediaBackend.plex;
@override
ServerCapabilities get capabilities => ServerCapabilities.plex;
@override
Future<List<MediaItem>> fetchAlbumTracks(String albumId) async {
fetchedAlbumIds.add(albumId);
return tracks;
}
@override
String thumbnailUrl(String? path, {int? width, int? height, bool cover = true}) => '';
@override
void close() {}
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}