mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-02 06:09:39 +02:00
feat(album): replace selection sharing with metadata re-enrichment
This commit is contained in:
1 parent
ce13e29b71
commit
eed56d4cbb
3 files changed
+292
-47
No files matched your search
@@ -0,0 +1,209 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/models/settings.dart';
|
||||
import 'package:spotiflac_android/providers/download_history_provider.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/screens/downloaded_album_screen.dart';
|
||||
import 'package:spotiflac_android/services/downloaded_embedded_cover_resolver.dart';
|
||||
import 'package:spotiflac_android/theme/app_theme.dart';
|
||||
import 'package:spotiflac_android/theme/mornye_theme.dart';
|
||||
import 'package:spotiflac_android/widgets/selection_bottom_bar.dart';
|
||||
|
||||
class _Settings extends SettingsNotifier {
|
||||
@override
|
||||
AppSettings build() => const AppSettings();
|
||||
|
||||
@override
|
||||
Future<void> syncLyricsSettingsToBackend({AppSettings? settings}) async {}
|
||||
}
|
||||
|
||||
class _History extends DownloadHistoryNotifier {
|
||||
final updates = <String, String?>{};
|
||||
|
||||
@override
|
||||
DownloadHistoryState build() => DownloadHistoryState();
|
||||
|
||||
@override
|
||||
Future<void> updateMetadataForItem({
|
||||
required String id,
|
||||
required String trackName,
|
||||
required String artistName,
|
||||
required String albumName,
|
||||
String? albumArtist,
|
||||
String? isrc,
|
||||
int? trackNumber,
|
||||
int? totalTracks,
|
||||
int? discNumber,
|
||||
int? totalDiscs,
|
||||
String? releaseDate,
|
||||
String? genre,
|
||||
String? composer,
|
||||
String? label,
|
||||
String? copyright,
|
||||
bool? explicit,
|
||||
bool? hasLyrics,
|
||||
int? lyricsMetadataScanVersion,
|
||||
}) async {
|
||||
updates[id] = isrc;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
const channel = MethodChannel('com.zarz.spotiflac/backend');
|
||||
final messenger =
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger;
|
||||
|
||||
for (final mornye in [false, true]) {
|
||||
testWidgets(
|
||||
'downloaded album re-enrich restores selection on cancel and updates only selected files ($mornye)',
|
||||
(tester) async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
final cache = Directory.systemTemp.createTempSync('album_reenrich_');
|
||||
DownloadedEmbeddedCoverResolver.setPersistentCacheDirectoryForTesting(
|
||||
cache,
|
||||
);
|
||||
addTearDown(() async {
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
await tester.runAsync(() async {
|
||||
await DownloadedEmbeddedCoverResolver.resetMemoryStateForTesting();
|
||||
DownloadedEmbeddedCoverResolver.setPersistentCacheDirectoryForTesting(
|
||||
null,
|
||||
);
|
||||
await cache.delete(recursive: true);
|
||||
});
|
||||
});
|
||||
const path = 'content://library/document/track.flac';
|
||||
final requests = <Map<String, dynamic>>[];
|
||||
messenger.setMockMethodCallHandler(channel, (call) async {
|
||||
if (call.method == 'reEnrichFile') {
|
||||
final request = Map<String, dynamic>.from(
|
||||
jsonDecode((call.arguments as Map)['request_json'] as String)
|
||||
as Map,
|
||||
);
|
||||
requests.add(request);
|
||||
return request['preview_only'] == true
|
||||
? {
|
||||
'method': 'preview',
|
||||
'enriched_metadata': {'isrc': 'USABC2600001'},
|
||||
}
|
||||
: {'method': 'native'};
|
||||
}
|
||||
if (call.method == 'readAudioMetadata') {
|
||||
return {
|
||||
'trackName': 'Track',
|
||||
'artistName': 'Artist',
|
||||
'albumName': 'Album',
|
||||
'isrc': 'USABC2600001',
|
||||
'hasLyrics': true,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
addTearDown(() => messenger.setMockMethodCallHandler(channel, null));
|
||||
final history = _History();
|
||||
final navigator = GlobalKey<NavigatorState>();
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
settingsProvider.overrideWith(_Settings.new),
|
||||
downloadHistoryProvider.overrideWith(() => history),
|
||||
downloadedAlbumTracksProvider(
|
||||
const DownloadedAlbumTracksRequest(
|
||||
albumName: 'Album',
|
||||
artistName: 'Artist',
|
||||
),
|
||||
).overrideWith(
|
||||
(ref) async => [
|
||||
for (final id in ['track', 'other'])
|
||||
DownloadHistoryItem(
|
||||
id: id,
|
||||
trackName: id == 'track' ? 'Track' : 'Other',
|
||||
artistName: 'Artist',
|
||||
albumName: 'Album',
|
||||
filePath: id == 'track' ? path : '/music/other.flac',
|
||||
service: 'example-provider',
|
||||
downloadedAt: DateTime(2026),
|
||||
duration: 180,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
child: MaterialApp(
|
||||
navigatorKey: navigator,
|
||||
theme: mornye
|
||||
? MornyeTheme.build(Brightness.light)
|
||||
: AppTheme.light(),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: const SelectionOverlayHost(
|
||||
child: DownloadedAlbumScreen(
|
||||
albumName: 'Album',
|
||||
artistName: 'Artist',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.scrollUntilVisible(find.text('Track'), 200);
|
||||
await tester.longPress(find.text('Track'));
|
||||
await tester.pumpAndSettle();
|
||||
final l10n = AppLocalizations.of(
|
||||
tester.element(find.byType(SelectionBottomBar)),
|
||||
);
|
||||
expect(find.text(l10n.selectionShareCount(1)), findsNothing);
|
||||
|
||||
Future<void> openReEnrich() async {
|
||||
await tester.tap(find.text('${l10n.trackReEnrich} (1)'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(SelectionBottomBar), findsNothing);
|
||||
expect(find.text('Review changes'), findsOneWidget);
|
||||
}
|
||||
|
||||
await openReEnrich();
|
||||
navigator.currentState!.pop();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(SelectionBottomBar), findsOneWidget);
|
||||
expect(requests, isEmpty);
|
||||
|
||||
await openReEnrich();
|
||||
await tester.ensureVisible(find.text('Review changes'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Review changes'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(requests, hasLength(1));
|
||||
expect(requests.single['file_path'], path);
|
||||
expect(requests.single['duration_ms'], 180000);
|
||||
await tester.ensureVisible(find.text('Apply changes'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Apply changes'));
|
||||
// The history refresh also invalidates the disk-backed cover cache.
|
||||
for (
|
||||
var attempt = 0;
|
||||
attempt < 50 && history.updates.isEmpty;
|
||||
attempt++
|
||||
) {
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
await tester.runAsync(() async {
|
||||
await Future<void>.delayed(const Duration(milliseconds: 10));
|
||||
});
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(requests, hasLength(2));
|
||||
expect(requests.last['file_path'], path);
|
||||
expect(history.updates, {'track': 'USABC2600001'});
|
||||
expect(find.byType(SelectionBottomBar), findsNothing);
|
||||
expect(tester.takeException(), isNull);
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user