Files
IKAR/test/avatar_viewer_test.dart
T

234 lines
6.8 KiB
Dart

import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:ikar/backend/api.dart';
import 'package:ikar/frontend/widgets/avatar_gallery.dart';
import 'package:ikar/frontend/widgets/photo_viewer.dart';
import 'package:ikar/l10n/app_localizations.dart';
import 'package:material_symbols_icons/symbols.dart';
String _url(int i) => 'https://example.test/avatar/$i';
class _HistoryApi extends Api {
int count = 0;
final List<int> requested = [];
@override
Future<Map<dynamic, dynamic>?> sendRequestMap(
int opcode,
Map<dynamic, dynamic> payload, {
bool silent = false,
}) async {
final from = payload['from'] as int;
final end = math.min(from + (payload['count'] as int), count);
requested.add(from);
return {
'total': count,
'urls': [for (var i = from; i < end; i++) _url(i)],
'ids': [for (var i = from; i < end; i++) 1000 + i],
};
}
}
final _ru = lookupAppLocalizations(const Locale('ru'));
void main() {
late _HistoryApi history;
setUpAll(() => history = _HistoryApi());
setUp(() => history.requested.clear());
_HistoryApi withHistory(int count) => history..count = count;
group('avatar feed', () {
test('the main photo already in history is not repeated', () async {
final feed = AvatarFeed(
const AvatarGallery(
contactId: 601,
name: 'Synthetic',
currentUrl: 'https://example.test/main-link',
mainPhotoId: 1000,
initialPhotoId: 1002,
),
client: withHistory(3),
);
await feed.load();
expect(feed.photos.map((p) => p.id), [1000, 1001, 1002]);
expect(feed.total, 3);
expect(feed.initialIndex, 2);
expect(feed.reachedEnd, isTrue);
});
test('a main photo missing from history leads the list', () async {
final feed = AvatarFeed(
const AvatarGallery(
contactId: 602,
name: 'Synthetic',
currentUrl: 'https://example.test/fresh',
mainPhotoId: 77,
),
client: withHistory(2),
);
await feed.load();
expect(feed.photos.first.url, 'https://example.test/fresh');
expect(feed.photos.length, 3);
expect(feed.total, 3);
expect(feed.initialIndex, 0);
});
test('history is paged until the server runs out', () async {
final api = withHistory(AvatarFeed.pageSize + 10);
final feed = AvatarFeed(
AvatarGallery(
contactId: 603,
name: 'Synthetic',
currentUrl: _url(0),
mainPhotoId: 1000,
),
client: api,
);
await feed.load();
expect(feed.photos.length, AvatarFeed.pageSize);
expect(feed.reachedEnd, isFalse);
await feed.loadMore();
expect(feed.photos.length, AvatarFeed.pageSize + 10);
expect(feed.reachedEnd, isTrue);
expect(api.requested, [0, AvatarFeed.pageSize]);
});
test('a chat without a contact has only its current photo', () async {
final api = withHistory(5);
final feed = AvatarFeed(
const AvatarGallery(
contactId: 0,
name: 'Synthetic group',
currentUrl: 'https://example.test/group',
),
client: api,
);
await feed.load();
expect(feed.photos.length, 1);
expect(feed.reachedEnd, isTrue);
expect(api.requested, isEmpty);
});
});
Future<void> openViewer(WidgetTester tester, AvatarFeed feed) async {
await tester.pumpWidget(
MaterialApp(
locale: const Locale('ru'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Builder(
builder: (context) => Scaffold(
body: TextButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => PhotoViewerScreen.avatars(avatars: feed),
),
),
child: const Text('open'),
),
),
),
),
);
await tester.tap(find.text('open'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
}
testWidgets('avatars open in the media viewer, older ones to the right', (
tester,
) async {
await openViewer(
tester,
AvatarFeed(
AvatarGallery(
contactId: 701,
name: 'Synthetic Name',
currentUrl: _url(0),
mainPhotoId: 1000,
),
client: withHistory(3),
),
);
expect(find.text(_ru.mediaViewerCounter(1, 3)), findsOneWidget);
expect(find.text('Synthetic Name'), findsOneWidget);
expect(find.byType(InteractiveViewer), findsWidgets);
expect(find.byIcon(Symbols.chevron_left), findsNothing);
expect(find.byIcon(Symbols.chevron_right), findsOneWidget);
await tester.tap(find.byIcon(Symbols.chevron_right));
await tester.pump();
await tester.pump(const Duration(milliseconds: 400));
expect(find.text(_ru.mediaViewerCounter(2, 3)), findsOneWidget);
expect(find.byIcon(Symbols.chevron_left), findsOneWidget);
});
testWidgets(
'deleting an avatar confirms, closes the viewer, reports the id',
(tester) async {
final deleted = <int>[];
await openViewer(
tester,
AvatarFeed(
AvatarGallery(
contactId: 702,
name: 'Synthetic Name',
currentUrl: _url(0),
mainPhotoId: 1000,
initialPhotoId: 1001,
onDelete: deleted.add,
),
client: withHistory(3),
),
);
expect(find.text(_ru.mediaViewerCounter(2, 3)), findsOneWidget);
await tester.tap(find.byIcon(Symbols.more_vert));
await tester.pump();
await tester.pump(const Duration(milliseconds: 400));
await tester.tap(find.text(_ru.msgActionsDelete).last);
for (var i = 0; i < 4; i++) {
await tester.pump(const Duration(milliseconds: 300));
}
await tester.tap(find.widgetWithText(FilledButton, _ru.msgActionsDelete));
await tester.pump();
await tester.pump(const Duration(milliseconds: 600));
expect(deleted, [1001]);
expect(find.byType(PhotoViewerScreen), findsNothing);
},
);
testWidgets('someone else\'s avatars offer no delete', (tester) async {
await openViewer(
tester,
AvatarFeed(
AvatarGallery(
contactId: 703,
name: 'Synthetic Name',
currentUrl: _url(0),
mainPhotoId: 1000,
),
client: withHistory(2),
),
);
await tester.tap(find.byIcon(Symbols.more_vert));
await tester.pump();
await tester.pump(const Duration(milliseconds: 400));
expect(find.text(_ru.photoViewerSaveAs), findsOneWidget);
expect(find.text(_ru.msgActionsDelete), findsNothing);
});
}