Politur: Wischen, wandernde Cover, Alphabet-Sprung, Mehrfachauswahl
Die P2-Liste aus dem Review — Kleinigkeiten, die man einzeln nicht vermisst und zusammen sofort merkt. - Das Coverbild wandert beim Oeffnen der Vollbild-Wiedergabe an seinen Platz, statt dass ein neuer Bildschirm aufspringt. - Wischen am Mini-Player: nach oben oeffnen, seitwaerts Titelwechsel. Mit Schwelle, weil der Mini-Player zugleich ein Knopf ist und der Finger beim Tippen fast immer ein paar Pixel wandert. Nach unten bewusst unbelegt, damit niemand die Wiedergabe versehentlich wegschiebt. - Kuenstlerliste mit Bildern und Albenzahl; dahinter eine Kuenstlerseite mit seinen Alben und darunter allen Titeln (dafuer neu: groupByAlbum). Bei nur einem Album entfaellt die Zwischenstufe. - Alphabet-Sprung am rechten Rand, nur bei Namenssortierung und ab 30 Titeln. Umlaute zaehlen zu ihrem Grundbuchstaben, Ziffern und Zeichen unter "#" — dieselbe Regel wie die Sortierung. - Mehrfachauswahl per langem Druck, mit "Zur Warteschlange" und "Zu Wiedergabeliste". - Abgeschaltete Systemanimationen gelten jetzt ueberall, nicht nur bei den Lauf-Balken. - Querformat und Tablet: der Player legt Cover und Bedienung nebeneinander und begrenzt die Textbreite. Beim Bauen gefunden: der Alphabet-Sprung landete bei absteigender Sortierung am Ende eines Buchstaben-Blocks statt an seinem Anfang. Aus dem Code-Review nachgebessert, sieben Punkte: die Alphabet-Leiste wurde breiter, je weniger Buchstaben sie hatte (bei einem einzigen 528 dp — die Songliste waere verschwunden); die neue Kuenstlerseite baute alle Titel sofort und oeffnete damit bei 300 Titeln ebenso viele Abos; die Bildlauf-Animation im Songtext stuerzte bei abgeschalteten Systemanimationen ab; die feste Zeilenhoehe war 64 statt 72 und die Schriftskalierung an 1.0 statt an einer echten Groesse gemessen; bereits enthaltene Titel wurden in Wiedergabelisten verschoben statt uebersprungen und trotzdem mitgezaehlt; ein Fehler im Stapel brach still ab; und die Zurueck-Geste verliess den Bildschirm, statt die Auswahl zu beenden. Selbst gefunden: in der Kuenstlerliste hing der Datenbank-Strom an CategoryService und wurde bei jeder Kategorie-Meldung neu aufgesetzt. Nebenbei aufgeklaert, warum ein neuer Widget-Test endlos hing: Aufbau in setUp laeuft ausserhalb der Testzone, die drift-Stroeme liefen dort in echter Zeit, waehrend pumpAndSettle in Testzeit auf sie wartete. Aufbau in den Testkoerper verschoben. Der aeltere Haenger im BACKLOG hat eine andere Ursache (db.close() dort nachweislich ausgeschlossen); die Spur ist notiert. 500 Tests gruen (vorher 457), flutter analyze ohne Befund, Release-APK gebaut. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xAHJTJM6UUqmjUgk1PUEd
This commit is contained in:
co-authored by
Claude Opus 5
parent
0d76d8cc58
commit
490e73ce41
@@ -0,0 +1,154 @@
|
||||
import 'package:drift/drift.dart' show Value, driftRuntimeOptions;
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:flutter/material.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/settings/app_settings.dart';
|
||||
import 'package:melo/shared/sort_store.dart';
|
||||
import 'package:melo/shared/sortable_song_list.dart';
|
||||
|
||||
/// Langes Drücken schaltet in die Mehrfachauswahl. Geprüft wird, dass dabei
|
||||
/// nichts abspielt und die Kopfzeile wechselt.
|
||||
///
|
||||
/// **Aufbau bewusst im Testkörper, nicht in `setUp`.** `setUp` läuft außerhalb
|
||||
/// der Testzone: Datenbank und Dienste entstünden dort in echter Zeit,
|
||||
/// während `pumpAndSettle` in Testzeit auf ihre Ströme wartet — der Test hängt
|
||||
/// dann endlos. Dieselbe Klasse Hänger beschreibt BACKLOG.md.
|
||||
void main() {
|
||||
final lieder = [
|
||||
for (var i = 0; i < 3; i++)
|
||||
Song(
|
||||
id: 'song-$i',
|
||||
path: '/music/$i.mp3',
|
||||
title: 'Titel $i',
|
||||
artist: 'Neoni',
|
||||
dateAddedMs: i,
|
||||
updatedAtMs: 0,
|
||||
deleted: false,
|
||||
playCount: 0,
|
||||
categoriesEdited: false,
|
||||
metadataEdited: false,
|
||||
),
|
||||
];
|
||||
|
||||
/// Begrenzt statt unbegrenzt: hängt doch einmal etwas, scheitert der Test
|
||||
/// in Sekunden, statt den ganzen Lauf zu blockieren.
|
||||
Future<void> beruhige(WidgetTester tester) => tester.pumpAndSettle(
|
||||
const Duration(milliseconds: 100),
|
||||
EnginePhase.sendSemanticsUpdate,
|
||||
const Duration(seconds: 5),
|
||||
);
|
||||
|
||||
Future<void> pumpe(WidgetTester tester) async {
|
||||
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
final db = MeloDb(NativeDatabase.memory());
|
||||
final einstellungen = AppSettings();
|
||||
await einstellungen.init();
|
||||
for (final song in lieder) {
|
||||
await db.into(db.songs).insert(SongsCompanion.insert(
|
||||
id: song.id,
|
||||
path: song.path,
|
||||
title: song.title,
|
||||
artist: Value(song.artist),
|
||||
dateAddedMs: song.dateAddedMs,
|
||||
updatedAtMs: 0,
|
||||
));
|
||||
}
|
||||
await tester.pumpWidget(
|
||||
MultiProvider(
|
||||
providers: [
|
||||
Provider<MeloDb>.value(value: db),
|
||||
ChangeNotifierProvider<CategoryService>.value(
|
||||
value: CategoryService(db)),
|
||||
ChangeNotifierProvider<PlaylistService>.value(
|
||||
value: PlaylistService(db)),
|
||||
ChangeNotifierProvider<AppSettings>.value(value: einstellungen),
|
||||
],
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: SortableSongList(
|
||||
songs: lieder,
|
||||
storeKey: SortStore.meineMusik,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await beruhige(tester);
|
||||
}
|
||||
|
||||
Future<void> aufraeumen(WidgetTester tester) async {
|
||||
// Kein db.close(): drift plant dabei eine Aufräum-Aufgabe ein, die der
|
||||
// Testrahmen nicht mehr abarbeitet (siehe BACKLOG.md).
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
await tester.pump(const Duration(milliseconds: 1));
|
||||
}
|
||||
|
||||
testWidgets('ohne Auswahl steht die Shuffle-Zeile da', (tester) async {
|
||||
await pumpe(tester);
|
||||
|
||||
expect(find.text('Shuffle-Wiedergabe'), findsOneWidget);
|
||||
expect(find.text('1 Titel ausgewählt'), findsNothing);
|
||||
|
||||
await aufraeumen(tester);
|
||||
});
|
||||
|
||||
testWidgets('langes Drücken beginnt die Auswahl', (tester) async {
|
||||
await pumpe(tester);
|
||||
|
||||
await tester.longPress(find.text('Titel 0'));
|
||||
await beruhige(tester);
|
||||
|
||||
expect(find.text('1 Titel ausgewählt'), findsOneWidget);
|
||||
// Die Shuffle-Zeile weicht, statt darüber zu wachsen — sonst rutschte
|
||||
// die Liste beim Auswählen nach unten weg.
|
||||
expect(find.text('Shuffle-Wiedergabe'), findsNothing);
|
||||
|
||||
await aufraeumen(tester);
|
||||
});
|
||||
|
||||
testWidgets('im Auswahl-Modus wählt ein Tipp aus, statt abzuspielen',
|
||||
(tester) async {
|
||||
await pumpe(tester);
|
||||
await tester.longPress(find.text('Titel 0'));
|
||||
await beruhige(tester);
|
||||
|
||||
await tester.tap(find.text('Titel 1'));
|
||||
await beruhige(tester);
|
||||
|
||||
expect(find.text('2 ausgewählt'), findsOneWidget);
|
||||
|
||||
await aufraeumen(tester);
|
||||
});
|
||||
|
||||
testWidgets('den letzten abwählen beendet die Auswahl', (tester) async {
|
||||
await pumpe(tester);
|
||||
await tester.longPress(find.text('Titel 0'));
|
||||
await beruhige(tester);
|
||||
|
||||
await tester.tap(find.text('Titel 0'));
|
||||
await beruhige(tester);
|
||||
|
||||
expect(find.text('Shuffle-Wiedergabe'), findsOneWidget);
|
||||
|
||||
await aufraeumen(tester);
|
||||
});
|
||||
|
||||
testWidgets('das Kreuz beendet die Auswahl', (tester) async {
|
||||
await pumpe(tester);
|
||||
await tester.longPress(find.text('Titel 0'));
|
||||
await beruhige(tester);
|
||||
|
||||
await tester.tap(find.byTooltip('Auswahl beenden'));
|
||||
await beruhige(tester);
|
||||
|
||||
expect(find.text('Shuffle-Wiedergabe'), findsOneWidget);
|
||||
|
||||
await aufraeumen(tester);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user