44e2623b3c
LikedIdsController loads /api/likes/ids once and mutates the local set on toggle. Failed mutations roll the set back so the icon never lies. Wired into ArtistDetail header, AlbumDetail header, and per-track in the album track list.
64 lines
2.0 KiB
Dart
64 lines
2.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:minstrel/api/endpoints/likes.dart';
|
|
import 'package:minstrel/likes/like_button.dart';
|
|
import 'package:minstrel/likes/likes_provider.dart';
|
|
import 'package:minstrel/theme/theme_data.dart';
|
|
|
|
class _ThrowingLikesApi implements LikesApi {
|
|
bool throwOnNext = false;
|
|
|
|
@override
|
|
Future<void> like(LikeKind kind, String id) async {
|
|
if (throwOnNext) throw StateError('boom');
|
|
}
|
|
|
|
@override
|
|
Future<void> unlike(LikeKind kind, String id) async {
|
|
if (throwOnNext) throw StateError('boom');
|
|
}
|
|
|
|
@override
|
|
Future<({Set<String> artists, Set<String> albums, Set<String> tracks})>
|
|
ids() async =>
|
|
(artists: <String>{}, albums: <String>{}, tracks: <String>{});
|
|
}
|
|
|
|
void main() {
|
|
testWidgets('tap toggles icon optimistically; rollback on error',
|
|
(tester) async {
|
|
final api = _ThrowingLikesApi();
|
|
final container = ProviderContainer(overrides: [
|
|
likesApiProvider.overrideWith((ref) async => api),
|
|
]);
|
|
addTearDown(container.dispose);
|
|
|
|
await tester.pumpWidget(UncontrolledProviderScope(
|
|
container: container,
|
|
child: MaterialApp(
|
|
theme: buildThemeData(),
|
|
home: const Scaffold(
|
|
body: LikeButton(kind: LikeKind.album, id: 'al-1'),
|
|
),
|
|
),
|
|
));
|
|
await tester.pumpAndSettle();
|
|
|
|
// First tap = like; succeeds → icon flips to favorite.
|
|
await tester.tap(find.byType(IconButton));
|
|
await tester.pump();
|
|
expect(find.byIcon(Icons.favorite), findsOneWidget);
|
|
|
|
// Force the next mutation to fail; rollback restores prior state.
|
|
api.throwOnNext = true;
|
|
final notifier = container.read(likedIdsProvider.notifier);
|
|
try {
|
|
await notifier.toggle(LikeKind.album, 'al-1');
|
|
} catch (_) {/* expected */}
|
|
await tester.pump();
|
|
expect(find.byIcon(Icons.favorite), findsOneWidget); // rolled back
|
|
});
|
|
}
|