174 lines
6.1 KiB
Dart
174 lines
6.1 KiB
Dart
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);
|
|
});
|
|
}
|