- Tabs: Meine Musik, Suchen, Download, Favoriten (vorher Download vor Suchen) - "Meine Musik": Schnellzugriffe (Favoriten/Wiedergabelisten/Zuletzt) einzeln über die Einstellungen abschaltbar - Audio- und Benachrichtigungs-Berechtigung werden jetzt beim ersten Start gemeinsam angefragt statt erst bei Bedarf; Hinweisbanner + Einstellungen- Button bei dauerhafter Verweigerung (auch für Benachrichtigungen neu) - Cover-Extraktion beim Android-Scan von 512px auf 1024px erhöht, für ein schärferes Cover im Sperrbildschirm - Fix: notifyListeners() in LibraryService nur noch bei echter Wertänderung (verhinderte einen Test-Hang durch den neuen Start-Berechtigungs-Aufruf) 557 Tests grün, flutter analyze ohne Befund. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VM2JK5mV7AL1g2Rt6H6h9w
203 lines
6.8 KiB
Dart
203 lines
6.8 KiB
Dart
import 'package:drift/drift.dart';
|
||
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/favorites_screen.dart';
|
||
import 'package:melo/library/library_service.dart';
|
||
import 'package:melo/library/my_music_screen.dart';
|
||
import 'package:melo/library/playlist_service.dart';
|
||
import 'package:melo/player/audio_handler.dart';
|
||
import 'package:melo/settings/app_settings.dart';
|
||
import 'package:provider/provider.dart';
|
||
import 'package:shared_preferences/shared_preferences.dart';
|
||
|
||
Widget _wrap(MeloDb db, LibraryService lib, PlaylistService playlists,
|
||
MeloAudioHandler handler, Widget child, {AppSettings? settings}) {
|
||
return MultiProvider(
|
||
providers: [
|
||
Provider<MeloDb>.value(value: db),
|
||
ChangeNotifierProvider<LibraryService>.value(value: lib),
|
||
ChangeNotifierProvider<PlaylistService>.value(value: playlists),
|
||
Provider<MeloAudioHandler>.value(value: handler),
|
||
ChangeNotifierProvider<CategoryService>(create: (_) => CategoryService(db)),
|
||
settings == null
|
||
? ChangeNotifierProvider<AppSettings>(create: (_) => AppSettings())
|
||
: ChangeNotifierProvider<AppSettings>.value(value: settings),
|
||
],
|
||
child: MaterialApp(home: Scaffold(body: child)),
|
||
);
|
||
}
|
||
|
||
Future<void> _insert(
|
||
MeloDb db, {
|
||
required String id,
|
||
required String title,
|
||
int dateAddedMs = 0,
|
||
int playCount = 0,
|
||
}) {
|
||
return db.into(db.songs).insert(SongsCompanion.insert(
|
||
id: id,
|
||
path: '/$id.mp3',
|
||
title: title,
|
||
dateAddedMs: dateAddedMs,
|
||
updatedAtMs: 0,
|
||
playCount: Value(playCount),
|
||
));
|
||
}
|
||
|
||
void main() {
|
||
setUp(() => SharedPreferences.setMockInitialValues({}));
|
||
|
||
testWidgets('Meine Musik zeigt Kopfzeile, Schnellzugriffe und Shuffle',
|
||
(tester) async {
|
||
final db = MeloDb(NativeDatabase.memory());
|
||
final lib = LibraryService(db);
|
||
final playlists = PlaylistService(db);
|
||
final handler = MeloAudioHandler(db: db);
|
||
await _insert(db, id: '1', title: 'Song A');
|
||
|
||
await tester.pumpWidget(
|
||
_wrap(db, lib, playlists, handler, const MyMusicScreen()));
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(find.text('Titel, Künstler und Alben suchen'), findsOneWidget);
|
||
expect(find.text('Favoriten'), findsOneWidget);
|
||
expect(find.text('Wiedergabelisten'), findsOneWidget);
|
||
expect(find.text('Shuffle-Wiedergabe'), findsOneWidget);
|
||
expect(find.text('Song A'), findsOneWidget);
|
||
|
||
handler.dispose();
|
||
await db.close();
|
||
});
|
||
|
||
testWidgets('Sortier-Menü bietet alle drei Kriterien und sortiert die Liste',
|
||
(tester) async {
|
||
final db = MeloDb(NativeDatabase.memory());
|
||
final lib = LibraryService(db);
|
||
final playlists = PlaylistService(db);
|
||
final handler = MeloAudioHandler(db: db);
|
||
await _insert(db, id: '1', title: 'Zenit', dateAddedMs: 300, playCount: 9);
|
||
await _insert(db, id: '2', title: 'Anfang', dateAddedMs: 100, playCount: 1);
|
||
|
||
await tester.pumpWidget(
|
||
_wrap(db, lib, playlists, handler, const MyMusicScreen()));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Standard: zuletzt hinzugefügt zuerst.
|
||
var titles = tester.widgetList<Text>(find.byType(Text))
|
||
.map((t) => t.data)
|
||
.where((t) => t == 'Zenit' || t == 'Anfang')
|
||
.toList();
|
||
expect(titles.first, 'Zenit');
|
||
|
||
await tester.tap(find.byIcon(Icons.swap_vert));
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(find.text('Zuletzt hinzugefügt'), findsOneWidget);
|
||
expect(find.text('Name (A–Z–#)'), findsOneWidget);
|
||
expect(find.text('Wie oft abgespielt'), findsOneWidget);
|
||
|
||
await tester.tap(find.text('Name (A–Z–#)'));
|
||
await tester.pumpAndSettle();
|
||
await tester.tap(find.text('Übernehmen'));
|
||
await tester.pumpAndSettle();
|
||
|
||
titles = tester.widgetList<Text>(find.byType(Text))
|
||
.map((t) => t.data)
|
||
.where((t) => t == 'Zenit' || t == 'Anfang')
|
||
.toList();
|
||
expect(titles.first, 'Anfang');
|
||
|
||
handler.dispose();
|
||
await db.close();
|
||
});
|
||
|
||
testWidgets(
|
||
'abgewählte Sektionen fehlen in den Schnellzugriffen, aktive bleiben',
|
||
(tester) async {
|
||
final db = MeloDb(NativeDatabase.memory());
|
||
final lib = LibraryService(db);
|
||
final playlists = PlaylistService(db);
|
||
final handler = MeloAudioHandler(db: db);
|
||
final settings = AppSettings();
|
||
await settings.init();
|
||
await settings.setMeineMusikSektionAktiv(
|
||
AppSettings.sektionWiedergabelisten, false);
|
||
|
||
await tester.pumpWidget(_wrap(
|
||
db, lib, playlists, handler, const MyMusicScreen(),
|
||
settings: settings));
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(find.text('Favoriten'), findsOneWidget);
|
||
expect(find.text('Zuletzt'), findsOneWidget);
|
||
expect(find.text('Wiedergabelisten'), findsNothing);
|
||
|
||
handler.dispose();
|
||
await db.close();
|
||
});
|
||
|
||
testWidgets('Favoriten-Tab zeigt nur favorisierte Songs mit Shuffle',
|
||
(tester) async {
|
||
final db = MeloDb(NativeDatabase.memory());
|
||
final lib = LibraryService(db);
|
||
final playlists = PlaylistService(db);
|
||
final handler = MeloAudioHandler(db: db);
|
||
await _insert(db, id: '1', title: 'Geliebt');
|
||
await _insert(db, id: '2', title: 'Egal');
|
||
await db.setFavorite('1', true);
|
||
|
||
await tester.pumpWidget(
|
||
_wrap(db, lib, playlists, handler, const FavoritesScreen()));
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(find.text('Shuffle-Wiedergabe'), findsOneWidget);
|
||
expect(find.text('Geliebt'), findsOneWidget);
|
||
expect(find.text('Egal'), findsNothing);
|
||
|
||
handler.dispose();
|
||
await db.close();
|
||
});
|
||
|
||
testWidgets('Favoriten-Tab zeigt einen Hinweis, wenn nichts favorisiert ist',
|
||
(tester) async {
|
||
final db = MeloDb(NativeDatabase.memory());
|
||
final lib = LibraryService(db);
|
||
final playlists = PlaylistService(db);
|
||
final handler = MeloAudioHandler(db: db);
|
||
|
||
await tester.pumpWidget(
|
||
_wrap(db, lib, playlists, handler, const FavoritesScreen()));
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(find.text('Noch keine Favoriten'), findsOneWidget);
|
||
|
||
handler.dispose();
|
||
await db.close();
|
||
});
|
||
|
||
testWidgets(
|
||
'Banner mit "Einstellungen"-Button, wenn die Benachrichtigung verweigert ist',
|
||
(tester) async {
|
||
final db = MeloDb(NativeDatabase.memory());
|
||
final lib = LibraryService(db)..notificationPermissionDenied = true;
|
||
final playlists = PlaylistService(db);
|
||
final handler = MeloAudioHandler(db: db);
|
||
|
||
await tester.pumpWidget(
|
||
_wrap(db, lib, playlists, handler, const MyMusicScreen()));
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(
|
||
find.textContaining('Benachrichtigungen für die Wiedergabe'),
|
||
findsOneWidget,
|
||
);
|
||
expect(find.text('Einstellungen'), findsOneWidget);
|
||
|
||
handler.dispose();
|
||
await db.close();
|
||
});
|
||
}
|