Auswahl-Modus: Auf den Server laden (nur in Meine Musik)
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
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:http/http.dart' as http;
|
||||
import 'package:http/testing.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/services/baka_auth.dart';
|
||||
import 'package:melo/services/melo_cloud_service.dart';
|
||||
import 'package:melo/services/sync_service.dart';
|
||||
import 'package:melo/settings/app_settings.dart';
|
||||
import 'package:melo/shared/sort_store.dart';
|
||||
import 'package:melo/shared/sortable_song_list.dart';
|
||||
|
||||
class _MemorySpeicher implements TokenSpeicher {
|
||||
_MemorySpeicher(this.werte);
|
||||
final Map<String, String> werte;
|
||||
@override
|
||||
Future<String?> lesen(String key) async => werte[key];
|
||||
@override
|
||||
Future<void> schreiben(String key, String wert) async => werte[key] = wert;
|
||||
@override
|
||||
Future<void> loeschen(String key) async => werte.remove(key);
|
||||
}
|
||||
|
||||
/// Merkt sich nur, was hochgeladen werden sollte. Der echte Upload braucht
|
||||
/// Dateien und einen Server — hier geht es um den Weg vom Knopf zum Dienst.
|
||||
class _FakeSync extends SyncService {
|
||||
_FakeSync(MeloDb db)
|
||||
: super(
|
||||
db: db,
|
||||
cloud: MeloCloudService(
|
||||
auth: BakaAuth(speicher: _MemorySpeicher({})),
|
||||
client: MockClient(
|
||||
(_) async => http.Response('{"status":"ok"}', 200)),
|
||||
),
|
||||
);
|
||||
|
||||
final hochgeladen = <String>[];
|
||||
|
||||
@override
|
||||
Future<UploadErgebnis> ladeAusgewaehlteHoch(List<Song> songs) async {
|
||||
hochgeladen.addAll([for (final s in songs) s.id]);
|
||||
return const UploadErgebnis(hochgeladen: 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// „Auf den Server laden" gehört in „Meine Musik" — und **nur** dorthin.
|
||||
/// [SortableSongList] wird in fünf Ansichten benutzt; ohne Scoping erschiene
|
||||
/// die Aktion auch bei Favoriten, Wiedergabelisten und Titellisten.
|
||||
///
|
||||
/// Aufbau bewusst im Testkörper, nicht in `setUp` (siehe auswahl_modus_test).
|
||||
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,
|
||||
),
|
||||
];
|
||||
|
||||
Future<void> beruhige(WidgetTester tester) => tester.pumpAndSettle(
|
||||
const Duration(milliseconds: 100),
|
||||
EnginePhase.sendSemanticsUpdate,
|
||||
const Duration(seconds: 5),
|
||||
);
|
||||
|
||||
/// Räumt den Widget-Baum kontrolliert ab, statt es dem Testrahmen am
|
||||
/// Testende zu überlassen: sonst bleibt ein von drift/SnackBar beim
|
||||
/// Aufräumen geplanter Timer offen (siehe auswahl_modus_test.dart /
|
||||
/// BACKLOG.md — derselbe Hänger, dieselbe Ursache).
|
||||
Future<void> aufraeumen(WidgetTester tester) async {
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
await tester.pump(const Duration(milliseconds: 1));
|
||||
}
|
||||
|
||||
Future<_FakeSync> pumpe(WidgetTester tester,
|
||||
{required bool serverUpload}) async {
|
||||
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
final db = MeloDb(NativeDatabase.memory());
|
||||
// Kein db.close(): drift plant dabei eine Aufräum-Aufgabe (Timer) ein,
|
||||
// die der Testrahmen nicht mehr abarbeitet (siehe BACKLOG.md und
|
||||
// auswahl_modus_test.dart — derselbe Hänger, dieselbe Ursache).
|
||||
final sync = _FakeSync(db);
|
||||
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),
|
||||
// Ohne diesen Provider stürbe schon das erste Antippen der neuen
|
||||
// Aktion in ladeAufServer mit ProviderNotFoundException.
|
||||
ChangeNotifierProvider<SyncService>.value(value: sync),
|
||||
],
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: SortableSongList(
|
||||
songs: lieder,
|
||||
storeKey: SortStore.meineMusik,
|
||||
serverUpload: serverUpload,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await beruhige(tester);
|
||||
await tester.longPress(find.text('Titel 0'));
|
||||
await beruhige(tester);
|
||||
return sync;
|
||||
}
|
||||
|
||||
testWidgets('in „Meine Musik" erscheint die Server-Aktion', (tester) async {
|
||||
await pumpe(tester, serverUpload: true);
|
||||
|
||||
expect(find.byTooltip('Auf den Server laden'), findsOneWidget);
|
||||
|
||||
await aufraeumen(tester);
|
||||
});
|
||||
|
||||
testWidgets('in den übrigen Ansichten erscheint sie nicht', (tester) async {
|
||||
await pumpe(tester, serverUpload: false);
|
||||
|
||||
// Der Auswahl-Modus läuft, die beiden Bestands-Aktionen sind da …
|
||||
expect(find.byTooltip('Zur Warteschlange hinzufügen'), findsOneWidget);
|
||||
// … die neue nicht.
|
||||
expect(find.byTooltip('Auf den Server laden'), findsNothing);
|
||||
|
||||
await aufraeumen(tester);
|
||||
});
|
||||
|
||||
testWidgets('das Antippen reicht die Auswahl an den Upload weiter',
|
||||
(tester) async {
|
||||
final sync = await pumpe(tester, serverUpload: true);
|
||||
|
||||
await tester.tap(find.byTooltip('Auf den Server laden'));
|
||||
await beruhige(tester);
|
||||
|
||||
// Ein sichtbarer Knopf ist noch keine Funktion: geprüft wird, dass genau
|
||||
// der lang gedrückte Titel bei ladeAusgewaehlteHoch ankommt.
|
||||
expect(sync.hochgeladen, ['song-0']);
|
||||
expect(find.text('1 hochgeladen'), findsOneWidget);
|
||||
|
||||
await aufraeumen(tester);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user