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/models/album.dart'; import 'package:minstrel/models/artist.dart'; import 'package:minstrel/models/page.dart'; import 'package:minstrel/models/track.dart'; import 'package:minstrel/theme/theme_data.dart'; class _ThrowingLikesApi implements LikesApi { bool throwOnNext = false; @override Future like(LikeKind kind, String id) async { if (throwOnNext) throw StateError('boom'); } @override Future unlike(LikeKind kind, String id) async { if (throwOnNext) throw StateError('boom'); } @override Future<({Set artists, Set albums, Set tracks})> ids() async => (artists: {}, albums: {}, tracks: {}); // The list-* methods aren't exercised by this test; return empty pages. @override Future> listTracks({int limit = 50, int offset = 0}) async => const Paged(items: [], total: 0, limit: 50, offset: 0); @override Future> listAlbums({int limit = 50, int offset = 0}) async => const Paged(items: [], total: 0, limit: 50, offset: 0); @override Future> listArtists({int limit = 50, int offset = 0}) async => const Paged(items: [], total: 0, limit: 50, offset: 0); } 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 }); }