199 lines
7.0 KiB
Dart
199 lines
7.0 KiB
Dart
import 'package:audio_service/audio_service.dart';
|
|
import 'package:drift/drift.dart' show Value, 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/library/category_service.dart';
|
|
import 'package:melo/library/database.dart';
|
|
import 'package:melo/library/playlist_service.dart';
|
|
import 'package:melo/library/song_list.dart';
|
|
import 'package:melo/player/audio_handler.dart';
|
|
import 'package:melo/player/now_playing_screen.dart';
|
|
import 'package:melo/player/player_expansion_controller.dart';
|
|
import 'package:melo/settings/app_settings.dart';
|
|
import 'package:melo/shared/favorite_button.dart';
|
|
|
|
/// Ersetzt in [MeloAudioHandler.loadPlaylist] den echten just_audio-Zugriff —
|
|
/// dieselbe Klasse wie in song_list_navigation_test.dart, dort erprobt.
|
|
class _FakeAudioHandler extends MeloAudioHandler {
|
|
_FakeAudioHandler(MeloDb db) : super(db: db);
|
|
|
|
@override
|
|
Future<int> loadPlaylist(List<MediaItem> items, {int startIndex = 0}) async {
|
|
queue.add(items);
|
|
if (items.isNotEmpty) mediaItem.add(items[startIndex]);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
late MeloDb db;
|
|
late CategoryService kategorien;
|
|
late PlaylistService playlists;
|
|
late AppSettings einstellungen;
|
|
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());
|
|
kategorien = CategoryService(db);
|
|
playlists = PlaylistService(db);
|
|
einstellungen = AppSettings();
|
|
await einstellungen.init();
|
|
handler = _FakeAudioHandler(db);
|
|
await db.into(db.songs).insert(SongsCompanion.insert(
|
|
id: 'song-1',
|
|
path: '/music/roses.mp3',
|
|
title: 'Roses',
|
|
artist: const Value('Neoni'),
|
|
dateAddedMs: 0,
|
|
updatedAtMs: 0,
|
|
));
|
|
});
|
|
|
|
tearDown(() {
|
|
handler.dispose();
|
|
kategorien.dispose();
|
|
playlists.dispose();
|
|
});
|
|
|
|
Song derSong() => Song(
|
|
id: 'song-1',
|
|
path: '/music/roses.mp3',
|
|
title: 'Roses',
|
|
artist: 'Neoni',
|
|
dateAddedMs: 0,
|
|
updatedAtMs: 0,
|
|
deleted: false,
|
|
playCount: 0,
|
|
categoriesEdited: false,
|
|
metadataEdited: false,
|
|
);
|
|
|
|
// Startet die Wiedergabe über denselben Weg wie in
|
|
// song_list_navigation_test.dart (Tap auf eine SongZeile) — dieser Weg ist
|
|
// dort nachweislich unproblematisch und übt die echte SongZeile→MediaItem
|
|
// -Umwandlung. Seit dem Umbau auf PlayerExpansionController (Plan
|
|
// 2026-08-29-blur-oeffnen-transition, Task 3/5) ist NowPlayingScreen kein
|
|
// per Navigator.push geöffneter Screen mehr, sondern reiner Inhalt — hier
|
|
// deshalb direkt in einen eigenen Scaffold gepumpt (Task 5 Step 3), statt
|
|
// eine Route zu pushen. Der Handler behält den zuvor gestarteten Titel in
|
|
// seinem mediaItem-Stream, den NowPlayingScreen darüber sofort sieht.
|
|
Future<PlayerExpansionController> oeffnePlayer(WidgetTester tester) async {
|
|
final expansion = PlayerExpansionController(vsync: tester);
|
|
addTearDown(expansion.dispose);
|
|
final providers = [
|
|
Provider<MeloDb>.value(value: db),
|
|
Provider<MeloAudioHandler>.value(value: handler),
|
|
ChangeNotifierProvider<CategoryService>.value(value: kategorien),
|
|
ChangeNotifierProvider<PlaylistService>.value(value: playlists),
|
|
ChangeNotifierProvider<AppSettings>.value(value: einstellungen),
|
|
ChangeNotifierProvider<PlayerExpansionController>.value(
|
|
value: expansion),
|
|
];
|
|
await tester.pumpWidget(
|
|
MultiProvider(
|
|
providers: providers,
|
|
child: MaterialApp(
|
|
home: Scaffold(
|
|
body: SongZeile(
|
|
song: derSong(),
|
|
warteschlange: [derSong()],
|
|
index: 0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.text('Roses'));
|
|
await tester.pumpAndSettle();
|
|
expect(expansion.progress, 1.0);
|
|
|
|
await tester.pumpWidget(
|
|
MultiProvider(
|
|
providers: providers,
|
|
child: MaterialApp(
|
|
home: Scaffold(body: const NowPlayingScreen()),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
return expansion;
|
|
}
|
|
|
|
Future<void> raeumeAuf(WidgetTester tester) async {
|
|
// Ohne diesen Abschluss bleibt ein drift-Stream-Abo (Favoriten-Herz)
|
|
// beim Abbau des Baums als offener Fake-Timer stehen — dieselbe
|
|
// Aufräum-Zeile wie in song_list_navigation_test.dart.
|
|
await tester.pumpWidget(const SizedBox());
|
|
await tester.pump(const Duration(milliseconds: 1));
|
|
}
|
|
|
|
testWidgets(
|
|
'Herz sitzt neben dem Songnamen, nicht mehr in der AppBar',
|
|
(tester) async {
|
|
await oeffnePlayer(tester);
|
|
|
|
expect(find.byType(NowPlayingScreen), findsOneWidget);
|
|
|
|
// NowPlayingScreen ist seit dem Overlay-Umbau (Task 5) kein Scaffold/
|
|
// keine Route mehr und hat gar keine AppBar mehr — die frühere
|
|
// "nicht mehr in der AppBar"-Prüfung ist jetzt trivial durch den Umbau
|
|
// selbst erfüllt.
|
|
expect(find.byType(AppBar), findsNothing);
|
|
|
|
// Aber sichtbar auf dem Bildschirm, in derselben Row wie der Titel.
|
|
final titelRow = find
|
|
.ancestor(of: find.text('Roses'), matching: find.byType(Row))
|
|
.first;
|
|
final herzBeimTitel = find.descendant(
|
|
of: titelRow,
|
|
matching: find.byType(FavoriteButton),
|
|
);
|
|
expect(herzBeimTitel, findsOneWidget);
|
|
|
|
await raeumeAuf(tester);
|
|
});
|
|
|
|
testWidgets('Ziehen nach unten im Vollbild-Player verringert progress live',
|
|
(tester) async {
|
|
// _TestShell (home_shell_expansion_test.dart) hat bewusst keinen echten
|
|
// MeloAudioHandler-Provider (siehe dortiger Kommentar) — ein echter
|
|
// NowPlayingScreen bräuchte den aber für context.read<MeloAudioHandler>().
|
|
// Dieser Test läuft deshalb hier, wo oeffnePlayer() bereits alle nötigen
|
|
// Provider bereitstellt (Plan Task 8 nennt now_playing_screen_test.dart
|
|
// selbst als Alternative zu home_shell_expansion_test.dart).
|
|
final expansion = await oeffnePlayer(tester);
|
|
expect(expansion.progress, 1.0);
|
|
|
|
await tester.drag(find.byType(NowPlayingScreen), const Offset(0, 150));
|
|
await tester.pump();
|
|
expect(expansion.progress, lessThan(1.0));
|
|
|
|
// dragEnd() (ausgelöst durch tester.drag) startet ein fling() — ohne
|
|
// pumpAndSettle bliebe dessen Ticker beim Testende aktiv.
|
|
await tester.pumpAndSettle();
|
|
await raeumeAuf(tester);
|
|
});
|
|
}
|