import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:minstrel/auth/auth_provider.dart'; import 'package:minstrel/models/admin_request.dart'; import 'package:minstrel/models/user.dart'; import 'package:minstrel/requests/requests_provider.dart'; import 'package:minstrel/requests/requests_screen.dart'; import 'package:minstrel/theme/theme_data.dart'; class _StubAuth extends AuthController { @override Future build() async => const User(id: 'u1', username: 'alice', isAdmin: false); } class _StubRequests extends MyRequestsController { _StubRequests(this._initial); final List _initial; @override Future> build() async => _initial; } const _pendingAlbum = AdminRequest( id: 'r1', userId: 'u1', status: 'pending', kind: 'album', artistName: 'Aphex Twin', albumTitle: 'Drukqs', trackTitle: null, requestedAt: '2026-05-08T00:00:00Z', decidedAt: null, notes: null, importedAlbumCount: 0, importedTrackCount: 0, ); const _completedTrackWithMatch = AdminRequest( id: 'r2', userId: 'u1', status: 'completed', kind: 'track', artistName: 'Boards of Canada', albumTitle: 'Geogaddi', trackTitle: 'Roygbiv', requestedAt: '2026-05-01T00:00:00Z', decidedAt: '2026-05-02T00:00:00Z', notes: null, importedAlbumCount: 1, importedTrackCount: 1, matchedTrackId: 't1', matchedAlbumId: 'a1', ); const _rejectedWithNotes = AdminRequest( id: 'r3', userId: 'u1', status: 'rejected', kind: 'artist', artistName: 'Some Artist', albumTitle: null, trackTitle: null, requestedAt: '2026-05-01T00:00:00Z', decidedAt: '2026-05-02T00:00:00Z', notes: 'Not in MusicBrainz', importedAlbumCount: 0, importedTrackCount: 0, ); Widget _harness(List requests) => ProviderScope( overrides: [ authControllerProvider.overrideWith(() => _StubAuth()), myRequestsProvider.overrideWith(() => _StubRequests(requests)), ], child: MaterialApp( theme: buildThemeData(), home: const RequestsScreen(), ), ); void main() { testWidgets('renders empty state when no requests', (t) async { await t.pumpWidget(_harness(const [])); await t.pumpAndSettle(); expect(find.text('Nothing requested yet.'), findsOneWidget); }); testWidgets('renders pending row with Cancel CTA', (t) async { await t.pumpWidget(_harness(const [_pendingAlbum])); await t.pumpAndSettle(); expect(find.byKey(const Key('request_row_r1')), findsOneWidget); expect(find.text('Drukqs'), findsOneWidget); expect(find.text('Cancel'), findsOneWidget); }); testWidgets('renders completed row with Listen CTA', (t) async { await t.pumpWidget(_harness(const [_completedTrackWithMatch])); await t.pumpAndSettle(); expect(find.text('Roygbiv'), findsOneWidget); expect(find.text('Listen'), findsOneWidget); // Ingest progress copy expect(find.text('Track ingested'), findsOneWidget); }); testWidgets('renders rejected row with notes; no CTA', (t) async { await t.pumpWidget(_harness(const [_rejectedWithNotes])); await t.pumpAndSettle(); expect(find.text('Some Artist'), findsOneWidget); expect(find.text('Not in MusicBrainz'), findsOneWidget); expect(find.text('Cancel'), findsNothing); expect(find.text('Listen'), findsNothing); }); testWidgets('Cancel button opens confirm dialog', (t) async { await t.pumpWidget(_harness(const [_pendingAlbum])); await t.pumpAndSettle(); await t.tap(find.text('Cancel')); await t.pumpAndSettle(); expect(find.text('Cancel request?'), findsOneWidget); expect(find.text('Cancel "Drukqs"?'), findsOneWidget); // Dismiss with Keep await t.tap(find.text('Keep')); await t.pumpAndSettle(); expect(find.text('Cancel request?'), findsNothing); }); }