feat: комета в 'поделится
'
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:komet/backend/modules/share_sender.dart';
|
||||
import 'package:komet/frontend/screens/chats/share_composer_bar.dart';
|
||||
import 'package:komet/frontend/widgets/rich_message_controller.dart';
|
||||
import 'package:komet/models/shared_payload.dart';
|
||||
|
||||
PreparedShareFile _file(String name, String mime) => PreparedShareFile(
|
||||
source: SharedFile(path: '/synthetic/$name', name: name, mime: mime, size: 8),
|
||||
);
|
||||
|
||||
Future<RichMessageController> _pump(
|
||||
WidgetTester tester, {
|
||||
required PreparedShare share,
|
||||
required List<String> recipients,
|
||||
bool sending = false,
|
||||
Future<void> Function(String)? onSend,
|
||||
}) async {
|
||||
final controller = RichMessageController();
|
||||
addTearDown(controller.dispose);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: ShareComposerBar(
|
||||
share: share,
|
||||
controller: controller,
|
||||
recipientNames: recipients,
|
||||
sending: sending,
|
||||
onSend: onSend ?? (_) async {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
return controller;
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('a single photo names itself and lists the recipient', (
|
||||
tester,
|
||||
) async {
|
||||
await _pump(
|
||||
tester,
|
||||
share: PreparedShare(files: [_file('a.jpg', 'image/jpeg')]),
|
||||
recipients: const ['ЛУКА'],
|
||||
);
|
||||
|
||||
expect(find.text('Отправить фотографию'), findsOneWidget);
|
||||
expect(find.text('В чат ЛУКА'), findsOneWidget);
|
||||
expect(find.text('Добавить подпись...'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('three chats collapse into a count and a badge', (tester) async {
|
||||
await _pump(
|
||||
tester,
|
||||
share: PreparedShare(
|
||||
files: [
|
||||
_file('a.jpg', 'image/jpeg'),
|
||||
_file('b.jpg', 'image/jpeg'),
|
||||
_file('c.jpg', 'image/jpeg'),
|
||||
],
|
||||
),
|
||||
recipients: const ['a', 'b', 'c'],
|
||||
);
|
||||
|
||||
expect(find.text('Отправить 3 фотографии'), findsOneWidget);
|
||||
expect(find.text('В 3 чата'), findsOneWidget);
|
||||
expect(find.text('3'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a text share drops the preview row', (tester) async {
|
||||
final controller = await _pump(
|
||||
tester,
|
||||
share: const PreparedShare(files: [], text: 'https://komet.pw'),
|
||||
recipients: const ['ЛУКА'],
|
||||
);
|
||||
|
||||
expect(find.text('Отправить сообщение'), findsNothing);
|
||||
expect(find.text('Сообщение'), findsOneWidget);
|
||||
expect(controller.text, '');
|
||||
});
|
||||
|
||||
testWidgets('a file share still shows a title but no photo wording', (
|
||||
tester,
|
||||
) async {
|
||||
await _pump(
|
||||
tester,
|
||||
share: PreparedShare(files: [_file('doc.pdf', 'application/pdf')]),
|
||||
recipients: const ['ЛУКА', 'Zarub'],
|
||||
);
|
||||
|
||||
expect(find.text('Отправить файл'), findsOneWidget);
|
||||
expect(find.text('В чат ЛУКА, Zarub'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('the caption reaches onSend', (tester) async {
|
||||
String? captured;
|
||||
final controller = await _pump(
|
||||
tester,
|
||||
share: PreparedShare(files: [_file('a.jpg', 'image/jpeg')]),
|
||||
recipients: const ['ЛУКА'],
|
||||
onSend: (caption) async => captured = caption,
|
||||
);
|
||||
|
||||
controller.text = ' привет ';
|
||||
await tester.pump();
|
||||
await tester.tap(find.byIcon(Symbols.send));
|
||||
await tester.pump();
|
||||
|
||||
expect(captured, 'привет');
|
||||
});
|
||||
|
||||
testWidgets('with no recipients the send button is inert', (tester) async {
|
||||
var sent = false;
|
||||
await _pump(
|
||||
tester,
|
||||
share: PreparedShare(files: [_file('a.jpg', 'image/jpeg')]),
|
||||
recipients: const [],
|
||||
onSend: (_) async => sent = true,
|
||||
);
|
||||
|
||||
expect(find.text('Выберите чат'), findsOneWidget);
|
||||
await tester.tap(find.byIcon(Symbols.send));
|
||||
await tester.pump();
|
||||
expect(sent, isFalse);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:komet/core/share/share_labels.dart';
|
||||
import 'package:komet/models/shared_payload.dart';
|
||||
|
||||
late Directory _dir;
|
||||
|
||||
String _makeFile(String name, {int bytes = 8}) {
|
||||
final file = File('${_dir.path}${Platform.pathSeparator}$name')
|
||||
..writeAsBytesSync(List<int>.filled(bytes, 0x41));
|
||||
return file.path;
|
||||
}
|
||||
|
||||
Map<String, Object?> _entry(String path, String mime, {int size = 8}) => {
|
||||
'path': path,
|
||||
'name': path.split(Platform.pathSeparator).last,
|
||||
'mime': mime,
|
||||
'size': size,
|
||||
};
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
_dir = Directory.systemTemp.createTempSync('synthetic_share_test');
|
||||
addTearDown(() {
|
||||
if (_dir.existsSync()) _dir.deleteSync(recursive: true);
|
||||
});
|
||||
});
|
||||
|
||||
group('SharedPayload.fromMap', () {
|
||||
test('classifies photos, videos and documents by mime', () {
|
||||
final payload = SharedPayload.fromMap({
|
||||
'files': [
|
||||
_entry(_makeFile('a.jpg'), 'image/jpeg'),
|
||||
_entry(_makeFile('b.mp4'), 'video/mp4'),
|
||||
_entry(_makeFile('c.pdf'), 'application/pdf'),
|
||||
],
|
||||
'text': null,
|
||||
});
|
||||
|
||||
expect(payload, isNotNull);
|
||||
expect(payload!.photos.map((f) => f.name), ['a.jpg']);
|
||||
expect(payload.videos.map((f) => f.name), ['b.mp4']);
|
||||
expect(payload.documents.map((f) => f.name), ['c.pdf']);
|
||||
expect(payload.dominantKind, SharedFileKind.file);
|
||||
});
|
||||
|
||||
test('an svg is a document, not a photo', () {
|
||||
final payload = SharedPayload.fromMap({
|
||||
'files': [_entry(_makeFile('d.svg'), 'image/svg+xml')],
|
||||
});
|
||||
|
||||
expect(payload!.files.single.kind, SharedFileKind.file);
|
||||
});
|
||||
|
||||
test('drops entries whose file is gone', () {
|
||||
final payload = SharedPayload.fromMap({
|
||||
'files': [
|
||||
_entry(_makeFile('present.jpg'), 'image/jpeg'),
|
||||
_entry('${_dir.path}/missing.jpg', 'image/jpeg'),
|
||||
],
|
||||
});
|
||||
|
||||
expect(payload!.files.map((f) => f.name), ['present.jpg']);
|
||||
});
|
||||
|
||||
test('a text-only share survives with no files', () {
|
||||
final payload = SharedPayload.fromMap({
|
||||
'files': const [],
|
||||
'text': ' https://komet.pw ',
|
||||
});
|
||||
|
||||
expect(payload!.isTextOnly, isTrue);
|
||||
expect(payload.text, 'https://komet.pw');
|
||||
});
|
||||
|
||||
test('an empty share is rejected', () {
|
||||
expect(SharedPayload.fromMap({'files': const [], 'text': ' '}), isNull);
|
||||
expect(SharedPayload.fromMap(null), isNull);
|
||||
expect(SharedPayload.fromMap('nonsense'), isNull);
|
||||
});
|
||||
|
||||
test('a missing mime falls back to a document', () {
|
||||
final payload = SharedPayload.fromMap({
|
||||
'files': [
|
||||
{'path': _makeFile('e.bin'), 'name': 'e.bin', 'size': 8},
|
||||
],
|
||||
});
|
||||
|
||||
expect(payload!.files.single.mime, 'application/octet-stream');
|
||||
expect(payload.files.single.kind, SharedFileKind.file);
|
||||
});
|
||||
});
|
||||
|
||||
group('shareTitleFor', () {
|
||||
test('photos use Russian plural forms', () {
|
||||
expect(
|
||||
shareTitleFor(photos: 1, videos: 0, documents: 0),
|
||||
'Отправить фотографию',
|
||||
);
|
||||
expect(
|
||||
shareTitleFor(photos: 3, videos: 0, documents: 0),
|
||||
'Отправить 3 фотографии',
|
||||
);
|
||||
expect(
|
||||
shareTitleFor(photos: 5, videos: 0, documents: 0),
|
||||
'Отправить 5 фотографий',
|
||||
);
|
||||
expect(
|
||||
shareTitleFor(photos: 11, videos: 0, documents: 0),
|
||||
'Отправить 11 фотографий',
|
||||
);
|
||||
});
|
||||
|
||||
test('videos stay uninflected', () {
|
||||
expect(
|
||||
shareTitleFor(photos: 0, videos: 1, documents: 0),
|
||||
'Отправить видео',
|
||||
);
|
||||
expect(
|
||||
shareTitleFor(photos: 0, videos: 2, documents: 0),
|
||||
'Отправить 2 видео',
|
||||
);
|
||||
});
|
||||
|
||||
test('documents and mixed sets fall back to file wording', () {
|
||||
expect(
|
||||
shareTitleFor(photos: 0, videos: 0, documents: 1),
|
||||
'Отправить файл',
|
||||
);
|
||||
expect(
|
||||
shareTitleFor(photos: 0, videos: 0, documents: 4),
|
||||
'Отправить 4 файла',
|
||||
);
|
||||
expect(
|
||||
shareTitleFor(photos: 1, videos: 1, documents: 0),
|
||||
'Отправить 2 файла',
|
||||
);
|
||||
});
|
||||
|
||||
test('a text share has no media wording', () {
|
||||
expect(
|
||||
shareTitleFor(photos: 0, videos: 0, documents: 0, textOnly: true),
|
||||
'Отправить сообщение',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('shareSubtitleFor', () {
|
||||
test('names are listed up to two recipients', () {
|
||||
expect(shareSubtitleFor(const ['ЛУКА']), 'В чат ЛУКА');
|
||||
expect(shareSubtitleFor(const ['ЛУКА', 'Zarub']), 'В чат ЛУКА, Zarub');
|
||||
});
|
||||
|
||||
test('three or more recipients collapse to a count', () {
|
||||
expect(shareSubtitleFor(const ['a', 'b', 'c']), 'В 3 чата');
|
||||
expect(shareSubtitleFor(List.filled(5, 'x')), 'В 5 чатов');
|
||||
});
|
||||
|
||||
test('an empty selection asks for one', () {
|
||||
expect(shareSubtitleFor(const []), 'Выберите чат');
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user