import 'package:audio_service/audio_service.dart'; import 'package:drift/drift.dart' show driftRuntimeOptions; import 'package:drift/native.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:melo/downloads/server_titel_screen.dart'; import 'package:melo/library/database.dart'; import 'package:melo/player/audio_handler.dart'; import 'package:melo/player/player_expansion_controller.dart'; import 'package:melo/services/navidrome_service.dart'; /// Ersetzt in [MeloAudioHandler.loadPlaylist] den echten just_audio-Zugriff: /// der spricht über Plattform-Kanäle, die im Widget-Test nicht existieren /// und mit `MissingPluginException` scheitern würden. Alles andere /// (Warteschlange, Mediendaten) bleibt echtes Handler-Verhalten. class _FakeAudioHandler extends MeloAudioHandler { _FakeAudioHandler(MeloDb db) : super(db: db); @override Future loadPlaylist(List items, {int startIndex = 0}) async { queue.add(items); if (items.isNotEmpty) mediaItem.add(items[startIndex]); return 0; } } void main() { late MeloDb db; late _FakeAudioHandler handler; void stelleKanaeleRuhig() { final messenger = TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; messenger.setMockMethodCallHandler( const MethodChannel('plugins.flutter.io/path_provider'), (call) async => '/tmp/melo-test', ); messenger.setMockMethodCallHandler( const MethodChannel('plugins.it_nomads.com/flutter_secure_storage'), (call) async => null, ); } setUp(() async { TestWidgetsFlutterBinding.ensureInitialized(); driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; SharedPreferences.setMockInitialValues({}); stelleKanaeleRuhig(); db = MeloDb(NativeDatabase.memory()); handler = _FakeAudioHandler(db); }); tearDown(() { handler.dispose(); }); Widget wrap(Widget child, PlayerExpansionController expansion) => MultiProvider( providers: [ Provider.value(value: db), Provider.value(value: handler), ChangeNotifierProvider.value( value: expansion), ], child: MaterialApp(home: child), ); testWidgets('Titel antippen öffnet den Vollbild-Player', (tester) async { final navidrome = NavidromeService() ..setCredentials('https://musik.baka-net.de', 'Baka', 'geheim'); final expansion = PlayerExpansionController(vsync: tester); addTearDown(expansion.dispose); await tester.pumpWidget(wrap( ServerTitelScreen( titel: 'Ablaze', navidrome: navidrome, holeTitel: () async => const [ SubsonicSong(id: 's1', titel: 'Feuer', kuenstler: 'X', dauerSekunden: 120), ], ), expansion, )); await tester.pumpAndSettle(); expect(expansion.progress, 0.0); await tester.tap(find.text('Feuer')); await tester.pumpAndSettle(); expect(expansion.progress, 1.0); // Ohne diesen Abschluss bleibt ein drift-Stream-Abo beim Abbau des // Baums als offener Fake-Timer stehen. await tester.pumpWidget(const SizedBox()); await tester.pump(const Duration(milliseconds: 1)); }); }