import 'package:drift/native.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:melo/library/category_service.dart'; import 'package:melo/library/database.dart'; import 'package:melo/library/library_service.dart'; import 'package:melo/library/playlist_service.dart'; import 'package:melo/main.dart'; import 'package:melo/player/audio_handler.dart'; import 'package:melo/services/baka_auth.dart'; import 'package:melo/services/download_service.dart'; import 'package:melo/services/melo_cloud_service.dart'; import 'package:melo/services/navidrome_service.dart'; import 'package:melo/services/offline_mode.dart'; import 'package:melo/services/sync_service.dart'; import 'package:melo/services/yt_download_service.dart'; import 'package:melo/services/yt_search_service.dart'; import 'package:melo/settings/app_settings.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; class _MemorySpeicher implements TokenSpeicher { final Map werte = {}; @override Future lesen(String key) async => werte[key]; @override Future schreiben(String key, String wert) async => werte[key] = wert; @override Future loeschen(String key) async => werte.remove(key); } void main() { setUp(() => SharedPreferences.setMockInitialValues({})); testWidgets('Untere Navigation folgt der Referenz-UI: ' 'Meine Musik, Suchen, Download, Favoriten, YT-Suche', (tester) async { final db = MeloDb(NativeDatabase.memory()); final lib = LibraryService(db); final playlists = PlaylistService(db); final handler = MeloAudioHandler(db: db); final auth = BakaAuth(speicher: _MemorySpeicher()); await tester.pumpWidget(MultiProvider( providers: [ Provider.value(value: db), ChangeNotifierProvider.value(value: lib), ChangeNotifierProvider.value(value: playlists), Provider.value(value: handler), ChangeNotifierProvider(create: (_) => CategoryService(db)), ChangeNotifierProvider(create: (_) => AppSettings()), ChangeNotifierProvider(create: (_) => OfflineMode()), ChangeNotifierProvider(create: (_) => auth), ChangeNotifierProvider( create: (_) => YtDownloadService(auth: auth)), ChangeNotifierProvider( create: (_) => YtSearchService(auth: auth)), ChangeNotifierProvider( create: (_) => DownloadService(db: db, navidrome: NavidromeService())), ChangeNotifierProvider( create: (_) => SyncService( db: db, cloud: MeloCloudService(auth: auth))), ], child: const MaterialApp(home: HomeShell()), )); await tester.pump(); final bar = tester.widget( find.byType(BottomNavigationBar)); expect(bar.items.map((i) => i.label).toList(), ['Meine Musik', 'Suchen', 'Download', 'Favoriten', 'YT-Suche']); handler.dispose(); await db.close(); }); testWidgets('Zurück-Geste auf einem Nicht-Start-Tab wechselt zu ' '"Meine Musik" statt die App zu minimieren', (tester) async { final db = MeloDb(NativeDatabase.memory()); final lib = LibraryService(db); final playlists = PlaylistService(db); final handler = MeloAudioHandler(db: db); final auth = BakaAuth(speicher: _MemorySpeicher()); await tester.pumpWidget(MultiProvider( providers: [ Provider.value(value: db), ChangeNotifierProvider.value(value: lib), ChangeNotifierProvider.value(value: playlists), Provider.value(value: handler), ChangeNotifierProvider(create: (_) => CategoryService(db)), ChangeNotifierProvider(create: (_) => AppSettings()), ChangeNotifierProvider(create: (_) => OfflineMode()), ChangeNotifierProvider(create: (_) => auth), ChangeNotifierProvider( create: (_) => YtDownloadService(auth: auth)), ChangeNotifierProvider( create: (_) => YtSearchService(auth: auth)), ChangeNotifierProvider( create: (_) => DownloadService(db: db, navidrome: NavidromeService())), ChangeNotifierProvider( create: (_) => SyncService( db: db, cloud: MeloCloudService(auth: auth))), ], child: const MaterialApp(home: HomeShell()), )); await tester.pump(); // Auf "Favoriten" wechseln (Index 3) — kein Navigator.push, nur der Tab. await tester.tap(find.descendant( of: find.byType(BottomNavigationBar), matching: find.text('Favoriten'))); await tester.pump(); expect( tester .widget(find.byType(BottomNavigationBar)) .currentIndex, 3); // System-Zurück-Geste simulieren — derselbe Weg, über den auch das // echte Android-Zurück-Gesten-Signal bei WidgetsApp ankommt. final widgetsAppState = tester.state(find.byType(WidgetsApp)); // ignore: avoid_dynamic_calls await (widgetsAppState as dynamic).didPopRoute(); await tester.pump(); // Ohne Abfangen bliebe der Tab unverändert (Flutter minimiert dann die // App statt zu navigieren) — die Geste muss stattdessen zu Tab 0 führen. expect( tester .widget(find.byType(BottomNavigationBar)) .currentIndex, 0); // Baum abbauen, bevor der Test endet: der Positions-Timer des Audio- // Handlers läuft sonst über das Testende hinaus weiter, und der // Test-Rahmen meldet einen offenen Timer (wie in hauptmenue_test.dart // dokumentiert). Aus demselben Grund bewusst kein db.close() nach // einem tester.tap. await tester.pumpWidget(const SizedBox()); handler.dispose(); await tester.pump(const Duration(milliseconds: 1)); }); }