Files
stream-chat-flutter/packages/stream_chat/test/src/client/channel_test.dart
T
2021-11-01 18:40:07 +05:30

2226 lines
68 KiB
Dart

import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/client/channel.dart';
import 'package:stream_chat/src/client/retry_policy.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
import '../fakes.dart';
import '../matchers.dart';
import '../mocks.dart';
void main() {
ChannelState _generateChannelState(
String channelId,
String channelType, {
bool mockChannelConfig = false,
}) {
ChannelConfig? config;
if (mockChannelConfig) {
config = MockChannelConfig();
when(() => config!.readEvents).thenReturn(true);
when(() => config!.typingEvents).thenReturn(true);
}
final channel = ChannelModel(
id: channelId,
type: channelType,
config: config,
);
final state = ChannelState(channel: channel);
return state;
}
Logger _createLogger(String name) {
final logger = Logger.detached(name)..level = Level.ALL;
logger.onRecord.listen(print);
return logger;
}
group('Non-Initialized Channel', () {
late final client = MockStreamChatClient();
const channelId = 'test-channel-id';
const channelType = 'test-channel-type';
late Channel channel;
setUpAll(() {
// detached loggers
when(() => client.detachedLogger(any())).thenAnswer((invocation) {
final name = invocation.positionalArguments.first;
return _createLogger(name);
});
// client logger
when(() => client.logger).thenReturn(_createLogger('mock-client-logger'));
});
setUp(() {
channel = Channel(client, channelType, channelId);
});
tearDown(() {
channel.dispose();
});
test('should be able to set `extraData`', () {
expect(channel.extraData.isEmpty, isTrue);
expect(
() => channel.extraData = {'name': 'test-channel-name'},
returnsNormally,
);
expect(channel.extraData.isEmpty, isFalse);
expect(channel.extraData.containsKey('name'), isTrue);
expect(channel.extraData['name'], 'test-channel-name');
});
test('should be able to get and set `image`', () {
expect(channel.extraData.isEmpty, isTrue);
const imageUrl = 'https://getstream.io/some-image';
channel.image = imageUrl;
expect(channel.image, imageUrl);
expect(channel.extraData['image'], imageUrl);
const newImage = 'https://getstream.io/new-image';
final newChannelInstance =
Channel(client, channelType, channelId, image: newImage);
expect(newChannelInstance.image, newImage);
expect(newChannelInstance.extraData['image'], newImage);
});
test('should be able to get and set `name`', () {
expect(channel.extraData.isEmpty, isTrue);
const name = 'Channel name';
channel.name = name;
expect(channel.name, name);
expect(channel.extraData['name'], name);
const newName = 'New channel name';
final newChannelInstance =
Channel(client, channelType, channelId, name: newName);
expect(newChannelInstance.name, newName);
expect(newChannelInstance.extraData['name'], newName);
});
});
// TODO : test all persistence related logic in this group
group('Initialized Channel with Persistence', () {
late final client = MockStreamChatClientWithPersistence();
const channelId = 'test-channel-id';
const channelType = 'test-channel-type';
const channelCid = '$channelType:$channelId';
late Channel channel;
setUpAll(() {
// Fallback values
registerFallbackValue(FakeMessage());
registerFallbackValue(<Message>[]);
registerFallbackValue(FakeAttachmentFile());
// detached loggers
when(() => client.detachedLogger(any())).thenAnswer((invocation) {
final name = invocation.positionalArguments.first;
return _createLogger(name);
});
final retryPolicy = RetryPolicy(
shouldRetry: (_, __, ___) => false,
retryTimeout: (_, __, ___) => Duration.zero,
);
when(() => client.retryPolicy).thenReturn(retryPolicy);
final event = Event(type: 'event.local');
when(() => client.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.value(event));
// fake clientState
final clientState = FakeClientState();
when(() => client.state).thenReturn(clientState);
// mock persistence client
final channelThreads = <String, List<Message>>{};
when(() => client.chatPersistenceClient.getChannelThreads(channelCid))
.thenAnswer((_) async => channelThreads);
final channelState = _generateChannelState(channelId, channelType);
when(() => client.chatPersistenceClient.getChannelStateByCid(channelCid))
.thenAnswer((_) async => channelState);
when(() => client.chatPersistenceClient.updateMessages(channelCid, any()))
.thenAnswer((_) => Future.value());
// client logger
when(() => client.logger).thenReturn(_createLogger('mock-client-logger'));
});
// Setting up a initialized channel
setUp(() {
final channelState = _generateChannelState(channelId, channelType);
channel = Channel.fromState(client, channelState);
});
tearDown(() {
channel.dispose();
});
});
group('Initialized Channel', () {
late final client = MockStreamChatClient();
const channelId = 'test-channel-id';
const channelType = 'test-channel-type';
const channelCid = '$channelType:$channelId';
late Channel channel;
setUpAll(() {
// Fallback values
registerFallbackValue(FakeMessage());
registerFallbackValue(FakeAttachmentFile());
registerFallbackValue(FakeEvent());
// detached loggers
when(() => client.detachedLogger(any())).thenAnswer((invocation) {
final name = invocation.positionalArguments.first;
return _createLogger(name);
});
final retryPolicy = RetryPolicy(
shouldRetry: (_, __, ___) => false,
retryTimeout: (_, __, ___) => Duration.zero,
);
when(() => client.retryPolicy).thenReturn(retryPolicy);
final event = Event(type: 'event.local');
when(() => client.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.value(event));
// fake clientState
final clientState = FakeClientState();
when(() => client.state).thenReturn(clientState);
// client logger
when(() => client.logger).thenReturn(_createLogger('mock-client-logger'));
});
// Setting up a initialized channel
setUp(() {
final channelState = _generateChannelState(
channelId,
channelType,
mockChannelConfig: true,
);
channel = Channel.fromState(client, channelState);
});
tearDown(() {
channel.dispose();
});
test('should throw if trying to set `extraData`', () {
try {
channel.extraData = {'name': 'test-channel-name'};
} catch (e) {
expect(e, isA<StateError>());
}
});
test('should throw if trying to set `image`', () {
try {
channel.image = 'https://stream.io/some-image';
} catch (e) {
expect(e, isA<StateError>());
}
});
test('should throw if trying to set `name`', () {
try {
channel.name = 'New name';
} catch (e) {
expect(e, isA<StateError>());
}
});
group('`.sendMessage`', () {
test('should work fine', () async {
final message = Message(id: 'test-message-id');
final sendMessageResponse = SendMessageResponse()..message = message;
when(() => client.sendMessage(
any(that: isSameMessageAs(message)),
channelId,
channelType,
)).thenAnswer((_) async => sendMessageResponse);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.sending),
matchSendingStatus: true,
),
],
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.sent),
matchSendingStatus: true,
),
],
]),
);
final res = await channel.sendMessage(message);
expect(res, isNotNull);
expect(res.message.id, message.id);
verify(() => client.sendMessage(
any(that: isSameMessageAs(message)),
channelId,
channelType,
)).called(1);
});
test('with attachments should work just fine', () async {
final attachments = List.generate(
3,
(index) => Attachment(
id: 'test-attachment-id-$index',
type: index.isEven ? 'image' : 'file',
file: AttachmentFile(size: index * 33, path: 'test-file-path'),
),
);
final message = Message(
id: 'test-message-id',
attachments: attachments,
);
final sendImageResponse = SendImageResponse()..file = 'test-image-url';
final sendFileResponse = SendFileResponse()..file = 'test-file-url';
when(() => client.sendImage(
any(),
channelId,
channelType,
onSendProgress: any(named: 'onSendProgress'),
cancelToken: any(named: 'cancelToken'),
)).thenAnswer((_) async => sendImageResponse);
when(() => client.sendFile(
any(),
channelId,
channelType,
onSendProgress: any(named: 'onSendProgress'),
cancelToken: any(named: 'cancelToken'),
)).thenAnswer((_) async => sendFileResponse);
when(() => client.sendMessage(
any(that: isSameMessageAs(message)),
channelId,
channelType,
)).thenAnswer((_) async => SendMessageResponse()
..message = message.copyWith(
attachments: attachments
.map((it) =>
it.copyWith(uploadState: const UploadState.success()))
.toList(growable: false),
));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder(
[
// preparing attachments to upload
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sending,
attachments: [
...attachments.map((it) => it.copyWith(
uploadState: const UploadState.preparing()))
],
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
// 0th attachment is successfully uploaded
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sending,
attachments: [...attachments]..[0] =
attachments[0].copyWith(
uploadState: const UploadState.success(),
),
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
// 0th and 1st attachment is successfully uploaded
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sending,
attachments: [...attachments]
..[0] = attachments[0].copyWith(
uploadState: const UploadState.success(),
)
..[1] = attachments[1].copyWith(
uploadState: const UploadState.success(),
),
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
// all the attachments are successfully uploaded
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sending,
attachments: [
...attachments.map((it) =>
it.copyWith(uploadState: const UploadState.success()))
],
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
attachments: [
...attachments.map((it) =>
it.copyWith(uploadState: const UploadState.success()))
],
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
],
),
);
final res = await channel.sendMessage(message);
expect(res, isNotNull);
expect(res.message.id, message.id);
expect(res.message.attachments.length, message.attachments.length);
expect(
res.message.attachments.every(
(it) => it.uploadState == const UploadState.success(),
),
isTrue,
);
verify(() => client.sendImage(
any(),
channelId,
channelType,
onSendProgress: any(named: 'onSendProgress'),
cancelToken: any(named: 'cancelToken'),
)).called(2);
verify(() => client.sendFile(
any(),
channelId,
channelType,
onSendProgress: any(named: 'onSendProgress'),
cancelToken: any(named: 'cancelToken'),
)).called(1);
verify(() => client.sendMessage(
any(that: isSameMessageAs(message)),
channelId,
channelType,
)).called(1);
});
});
group('`.updateMessage`', () {
test('should work fine', () async {
final message = Message(id: 'test-message-id');
final updateMessageResponse = UpdateMessageResponse()
..message = message;
when(() => client.updateMessage(any(that: isSameMessageAs(message))))
.thenAnswer((_) async => updateMessageResponse);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.updating),
matchSendingStatus: true,
),
],
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.sent),
matchSendingStatus: true,
),
],
]),
);
final res = await channel.updateMessage(message);
expect(res, isNotNull);
expect(res.message.id, message.id);
verify(() => client.updateMessage(
any(that: isSameMessageAs(message)),
)).called(1);
});
test('with attachments should work just fine', () async {
final attachments = List.generate(
3,
(index) => Attachment(
id: 'test-attachment-id-$index',
type: index.isEven ? 'image' : 'file',
file: AttachmentFile(size: index * 33, path: 'test-file-path'),
),
);
final message = Message(
id: 'test-message-id',
attachments: attachments,
);
final sendImageResponse = SendImageResponse()..file = 'test-image-url';
final sendFileResponse = SendFileResponse()..file = 'test-file-url';
when(() => client.sendImage(
any(),
channelId,
channelType,
onSendProgress: any(named: 'onSendProgress'),
cancelToken: any(named: 'cancelToken'),
)).thenAnswer((_) async => sendImageResponse);
when(() => client.sendFile(
any(),
channelId,
channelType,
onSendProgress: any(named: 'onSendProgress'),
cancelToken: any(named: 'cancelToken'),
)).thenAnswer((_) async => sendFileResponse);
when(() => client.updateMessage(
any(that: isSameMessageAs(message)),
)).thenAnswer((_) async => UpdateMessageResponse()
..message = message.copyWith(
attachments: attachments
.map((it) =>
it.copyWith(uploadState: const UploadState.success()))
.toList(growable: false),
));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder(
[
// preparing attachments to upload
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.updating,
attachments: [
...attachments.map((it) => it.copyWith(
uploadState: const UploadState.preparing()))
],
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
// 0th attachment is successfully uploaded
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.updating,
attachments: [...attachments]..[0] =
attachments[0].copyWith(
uploadState: const UploadState.success(),
),
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
// 0th and 1st attachment is successfully uploaded
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.updating,
attachments: [...attachments]
..[0] = attachments[0].copyWith(
uploadState: const UploadState.success(),
)
..[1] = attachments[1].copyWith(
uploadState: const UploadState.success(),
),
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
// all the attachments are successfully uploaded
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.updating,
attachments: [
...attachments.map((it) =>
it.copyWith(uploadState: const UploadState.success()))
],
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
attachments: [
...attachments.map((it) =>
it.copyWith(uploadState: const UploadState.success()))
],
),
matchSendingStatus: true,
matchAttachments: true,
matchAttachmentsUploadState: true,
),
],
],
),
);
final res = await channel.updateMessage(message);
expect(res, isNotNull);
expect(res.message.id, message.id);
expect(res.message.attachments.length, message.attachments.length);
expect(
res.message.attachments.every(
(it) => it.uploadState == const UploadState.success(),
),
isTrue,
);
verify(() => client.sendImage(
any(),
channelId,
channelType,
onSendProgress: any(named: 'onSendProgress'),
cancelToken: any(named: 'cancelToken'),
)).called(2);
verify(() => client.sendFile(
any(),
channelId,
channelType,
onSendProgress: any(named: 'onSendProgress'),
cancelToken: any(named: 'cancelToken'),
)).called(1);
verify(() => client.updateMessage(
any(that: isSameMessageAs(message)),
)).called(1);
});
});
test('`.partialUpdateMessage`', () async {
final message = Message(id: 'test-message-id');
const set = {'text': 'Update Message text'};
const unset = ['pinExpires'];
final updateMessageResponse = UpdateMessageResponse()
..message = message.copyWith(text: set['text'], pinExpires: null);
when(
() => client.partialUpdateMessage(message.id, set: set, unset: unset),
).thenAnswer((_) async => updateMessageResponse);
channel.state?.messagesStream.skip(1).listen(print);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
updateMessageResponse.message.copyWith(
status: MessageSendingStatus.sent,
),
matchText: true,
matchSendingStatus: true,
),
],
]),
);
final res = await channel.partialUpdateMessage(
message,
set: set,
unset: unset,
);
expect(res, isNotNull);
expect(res.message.id, message.id);
expect(res.message.id, message.id);
expect(res.message.text, set['text']);
expect(res.message.pinExpires, isNull);
verify(
() => client.partialUpdateMessage(message.id, set: set, unset: unset),
).called(1);
});
group('`.deleteMessage`', () {
test('should work fine', () async {
const messageId = 'test-message-id';
final message = Message(id: messageId);
when(() => client.deleteMessage(messageId))
.thenAnswer((_) async => EmptyResponse());
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.deleting),
matchSendingStatus: true,
),
],
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.sent),
matchSendingStatus: true,
),
],
]),
);
final res = await channel.deleteMessage(message);
expect(res, isNotNull);
verify(() => client.deleteMessage(messageId)).called(1);
});
test(
'''should directly update the state with message as deleted if the state is sending or failed''',
() async {
const messageId = 'test-message-id';
final message = Message(
id: messageId,
status: MessageSendingStatus.sending,
);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.sent),
matchSendingStatus: true,
),
],
]),
);
final res = await channel.deleteMessage(message);
expect(res, isNotNull);
},
);
});
group('`.pinMessage`', () {
test('should work fine without passing timeoutOrExpirationDate',
() async {
final message = Message(id: 'test-message-id');
when(() => client.partialUpdateMessage(
message.id,
set: any(named: 'set'),
unset: any(named: 'unset'),
)).thenAnswer((_) async => UpdateMessageResponse()
..message = message.copyWith(
pinned: true,
pinExpires: null,
status: MessageSendingStatus.sent,
));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.sent),
matchSendingStatus: true,
),
],
]),
);
final res = await channel.pinMessage(message);
expect(res, isNotNull);
expect(res.message.pinned, isTrue);
expect(res.message.pinExpires, isNull);
verify(() => client.partialUpdateMessage(
message.id,
set: any(named: 'set'),
unset: any(named: 'unset'),
)).called(1);
});
test(
'should work fine if passed timeoutOrExpirationDate as num(seconds)',
() async {
final message = Message(id: 'test-message-id');
const timeoutOrExpirationDate = 300; // 300 seconds
when(() => client.partialUpdateMessage(
message.id,
set: any(named: 'set'),
unset: any(named: 'unset'),
)).thenAnswer((_) async => UpdateMessageResponse()
..message = message.copyWith(
pinned: true,
pinExpires: DateTime.now().add(
const Duration(seconds: timeoutOrExpirationDate),
),
status: MessageSendingStatus.sent,
));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.sent),
matchSendingStatus: true,
),
],
]),
);
final res = await channel.pinMessage(
message,
timeoutOrExpirationDate: timeoutOrExpirationDate,
);
expect(res, isNotNull);
expect(res.message.pinned, isTrue);
expect(res.message.pinExpires, isNotNull);
verify(() => client.partialUpdateMessage(
message.id,
set: any(named: 'set'),
unset: any(named: 'unset'),
)).called(1);
},
);
test(
'should work fine if passed timeoutOrExpirationDate as DateTime',
() async {
final message = Message(id: 'test-message-id');
final timeoutOrExpirationDate =
DateTime.now().add(const Duration(days: 3)); // 3 days
when(() => client.partialUpdateMessage(
message.id,
set: any(named: 'set'),
unset: any(named: 'unset'),
)).thenAnswer((_) async => UpdateMessageResponse()
..message = message.copyWith(
pinned: true,
pinExpires: timeoutOrExpirationDate,
status: MessageSendingStatus.sent,
));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.sent),
matchSendingStatus: true,
),
],
]),
);
final res = await channel.pinMessage(
message,
timeoutOrExpirationDate: timeoutOrExpirationDate,
);
expect(res, isNotNull);
expect(res.message.pinned, isTrue);
expect(res.message.pinExpires, isNotNull);
expect(res.message.pinExpires, timeoutOrExpirationDate.toUtc());
verify(() => client.partialUpdateMessage(
message.id,
set: any(named: 'set'),
unset: any(named: 'unset'),
)).called(1);
},
);
test(
'should throw if invalid timeoutOrExpirationDate is passed',
() async {
final message = Message(id: 'test-message-id');
const timeoutOrExpirationDate = 'invalid-value';
try {
await channel.pinMessage(
message,
timeoutOrExpirationDate: timeoutOrExpirationDate,
);
} catch (e) {
expect(e, isA<ArgumentError>());
}
},
);
});
test('`.unpinMessage`', () async {
final message = Message(id: 'test-message-id', pinned: true);
when(() => client.partialUpdateMessage(
message.id,
set: {'pinned': false},
)).thenAnswer((_) async => UpdateMessageResponse()
..message = message.copyWith(
pinned: false,
status: MessageSendingStatus.sent,
));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(status: MessageSendingStatus.sent),
matchSendingStatus: true,
),
],
]),
);
final res = await channel.unpinMessage(message);
expect(res, isNotNull);
expect(res.message.pinned, isFalse);
verify(() => client.partialUpdateMessage(
message.id,
set: {'pinned': false},
)).called(1);
});
group('`.search`', () {
final filter = Filter.in_('cid', const [channelCid]);
test('should work fine with `query`', () async {
const query = 'test-search-query';
const sort = [SortOption('test-sort-field')];
const pagination = PaginationParams();
final results = List.generate(3, (index) => GetMessageResponse());
when(() => client.search(
filter,
query: query,
sort: any(named: 'sort'),
paginationParams: any(named: 'paginationParams'),
)).thenAnswer(
(_) async => SearchMessagesResponse()..results = results,
);
final res = await channel.search(
query: query,
sort: sort,
paginationParams: pagination,
);
expect(res, isNotNull);
expect(res.results.length, results.length);
verify(() => client.search(
filter,
query: query,
sort: any(named: 'sort'),
paginationParams: any(named: 'paginationParams'),
)).called(1);
});
test('should work fine with `messageFilters`', () async {
final messageFilters = Filter.query('key', 'text');
const sort = [SortOption('test-sort-field')];
const pagination = PaginationParams();
final results = List.generate(3, (index) => GetMessageResponse());
when(() => client.search(
filter,
messageFilters: messageFilters,
sort: any(named: 'sort'),
paginationParams: any(named: 'paginationParams'),
)).thenAnswer(
(_) async => SearchMessagesResponse()..results = results,
);
final res = await channel.search(
sort: sort,
paginationParams: pagination,
messageFilters: messageFilters,
);
expect(res, isNotNull);
expect(res.results.length, results.length);
verify(() => client.search(
filter,
messageFilters: messageFilters,
sort: any(named: 'sort'),
paginationParams: any(named: 'paginationParams'),
)).called(1);
});
});
test('`.deleteFile`', () async {
const url = 'test-file-url';
when(() => client.deleteFile(url, channelId, channelType,
cancelToken: any(named: 'cancelToken')))
.thenAnswer((_) async => EmptyResponse());
final res = await channel.deleteFile(url);
expect(res, isNotNull);
verify(() => client.deleteFile(url, channelId, channelType,
cancelToken: any(named: 'cancelToken'))).called(1);
});
test('`.deleteImage`', () async {
const url = 'test-image-url';
when(() => client.deleteImage(url, channelId, channelType,
cancelToken: any(named: 'cancelToken')))
.thenAnswer((_) async => EmptyResponse());
final res = await channel.deleteImage(url);
expect(res, isNotNull);
verify(() => client.deleteImage(url, channelId, channelType,
cancelToken: any(named: 'cancelToken'))).called(1);
});
test('`.sendEvent`', () async {
final event = Event(type: 'event.local');
when(() => client.sendEvent(channelId, channelType, event))
.thenAnswer((_) async => EmptyResponse());
final res = await channel.sendEvent(event);
expect(res, isNotNull);
verify(() => client.sendEvent(channelId, channelType, event)).called(1);
});
group('`.sendReaction`', () {
test('should work fine', () async {
const type = 'test-reaction-type';
final message = Message(id: 'test-message-id');
final reaction = Reaction(type: type, messageId: message.id);
when(() => client.sendReaction(message.id, type)).thenAnswer(
(_) async => SendReactionResponse()
..message = message
..reaction = reaction,
);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
reactionCounts: {type: 1},
reactionScores: {type: 1},
latestReactions: [reaction],
ownReactions: [reaction],
),
matchReactions: true,
matchSendingStatus: true,
),
],
]),
);
final res = await channel.sendReaction(message, type);
expect(res, isNotNull);
expect(res.reaction.type, type);
expect(res.reaction.messageId, message.id);
verify(() => client.sendReaction(message.id, type)).called(1);
});
test(
'should restore previous message if `client.sendReaction` throws',
() async {
const type = 'test-reaction-type';
final message = Message(id: 'test-message-id');
final reaction = Reaction(type: type, messageId: message.id);
when(() => client.sendReaction(message.id, type))
.thenThrow(StreamChatNetworkError(ChatErrorCode.inputError));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
reactionCounts: {type: 1},
reactionScores: {type: 1},
latestReactions: [reaction],
ownReactions: [reaction],
),
matchReactions: true,
matchSendingStatus: true,
),
],
[
isSameMessageAs(
message,
matchReactions: true,
matchSendingStatus: true,
),
],
]),
);
try {
await channel.sendReaction(message, type);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
}
verify(() => client.sendReaction(message.id, type)).called(1);
},
);
test(
'''should override previous reaction if present and `enforceUnique` is true''',
() async {
const userId = 'test-user-id';
const messageId = 'test-message-id';
const prevType = 'test-reaction-type';
final prevReaction = Reaction(
type: prevType,
messageId: messageId,
userId: userId,
);
final message = Message(
id: messageId,
ownReactions: [prevReaction],
latestReactions: [prevReaction],
reactionScores: const {prevType: 1},
reactionCounts: const {prevType: 1},
);
const type = 'test-reaction-type-2';
final newReaction = Reaction(
type: type,
messageId: messageId,
userId: userId,
);
final newMessage = message.copyWith(
ownReactions: [newReaction],
latestReactions: [newReaction],
);
const enforceUnique = true;
when(() => client.sendReaction(
messageId,
type,
enforceUnique: enforceUnique,
)).thenAnswer(
(_) async => SendReactionResponse()
..message = newMessage
..reaction = newReaction,
);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
newMessage.copyWith(status: MessageSendingStatus.sent),
matchReactions: true,
matchSendingStatus: true,
),
],
]),
);
final res = await channel.sendReaction(
message,
type,
enforceUnique: enforceUnique,
);
expect(res, isNotNull);
expect(res.reaction.type, type);
expect(res.reaction.messageId, messageId);
verify(() => client.sendReaction(
messageId,
type,
enforceUnique: enforceUnique,
)).called(1);
},
);
});
group('`.deleteReaction`', () {
test('should work fine', () async {
const userId = 'test-user-id';
const messageId = 'test-message-id';
const type = 'test-reaction-type';
final reaction = Reaction(
type: type,
messageId: messageId,
userId: userId,
);
final message = Message(
id: messageId,
ownReactions: [reaction],
latestReactions: [reaction],
reactionScores: const {type: 1},
reactionCounts: const {type: 1},
);
when(() => client.deleteReaction(messageId, type))
.thenAnswer((_) async => EmptyResponse());
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
latestReactions: [],
ownReactions: [],
),
matchReactions: true,
matchSendingStatus: true,
),
],
]),
);
final res = await channel.deleteReaction(message, reaction);
expect(res, isNotNull);
verify(() => client.deleteReaction(messageId, type)).called(1);
});
test(
'should restore prev message state if `client.deleteReaction` throws',
() async {
const userId = 'test-user-id';
const messageId = 'test-message-id';
const type = 'test-reaction-type';
final reaction = Reaction(
type: type,
messageId: messageId,
userId: userId,
);
final message = Message(
id: messageId,
ownReactions: [reaction],
latestReactions: [reaction],
reactionScores: const {type: 1},
reactionCounts: const {type: 1},
);
when(() => client.deleteReaction(messageId, type))
.thenThrow(StreamChatNetworkError(ChatErrorCode.inputError));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
latestReactions: [],
ownReactions: [],
),
matchReactions: true,
matchSendingStatus: true,
),
],
[
isSameMessageAs(
message,
matchReactions: true,
matchSendingStatus: true,
),
],
]),
);
try {
await channel.deleteReaction(message, reaction);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
}
verify(() => client.deleteReaction(messageId, type)).called(1);
},
);
});
test('`.update`', () async {
const channelData = {
'name': 'Stream Team',
'profile_image': 'test-profile-image',
};
final updateMessage = Message(
id: 'test-message-id',
text: 'updated channel',
);
final channelModel = ChannelModel(
cid: channelCid,
extraData: channelData,
);
when(() => client.updateChannel(channelId, channelType, channelData,
message: any(named: 'message'))).thenAnswer(
(_) async => UpdateChannelResponse()
..channel = channelModel
..message = updateMessage,
);
final res = await channel.update(channelData, updateMessage);
expect(res, isNotNull);
expect(res.channel.cid, channelModel.cid);
expect(res.channel.extraData, channelData);
expect(res.message?.id, updateMessage.id);
verify(() => client.updateChannel(channelId, channelType, channelData,
message: any(named: 'message'))).called(1);
});
test('`.updateImage`', () async {
const image = 'https://getstream.io/new-image';
final channelModel = ChannelModel(
cid: channelCid,
extraData: {'image': image},
);
when(() => client.updateChannelPartial(
channelId,
channelType,
set: {'image': image},
)).thenAnswer(
(_) async => PartialUpdateChannelResponse()..channel = channelModel,
);
final res = await channel.updateImage(image);
expect(res, isNotNull);
expect(res.channel.extraData['image'], image);
verify(() => client.updateChannelPartial(
channelId,
channelType,
set: {'image': image},
)).called(1);
});
test('`.updateName`', () async {
const name = 'Name';
final channelModel = ChannelModel(
cid: channelCid,
extraData: {'name': name},
);
when(() => client.updateChannelPartial(
channelId,
channelType,
set: {'name': name},
)).thenAnswer(
(_) async => PartialUpdateChannelResponse()..channel = channelModel,
);
final res = await channel.updateName(name);
expect(res, isNotNull);
expect(res.channel.extraData['name'], name);
verify(() => client.updateChannelPartial(
channelId,
channelType,
set: {'name': name},
)).called(1);
});
test('`.updatePartial`', () async {
const set = {
'name': 'Stream Team',
'profile_image': 'test-profile-image',
};
const unset = ['tag', 'last_name'];
final channelModel = ChannelModel(
cid: channelCid,
extraData: {
'coolness': 999,
...set,
},
);
when(() => client.updateChannelPartial(
channelId,
channelType,
set: set,
unset: unset,
)).thenAnswer(
(_) async => PartialUpdateChannelResponse()..channel = channelModel,
);
final res = await channel.updatePartial(set: set, unset: unset);
expect(res, isNotNull);
expect(res.channel.cid, channelModel.cid);
expect(
res.channel.extraData,
{'coolness': 999, ...set},
);
verify(() => client.updateChannelPartial(
channelId,
channelType,
set: set,
unset: unset,
)).called(1);
});
test('`.delete`', () async {
when(() => client.deleteChannel(channelId, channelType))
.thenAnswer((_) async => EmptyResponse());
final res = await channel.delete();
expect(res, isNotNull);
verify(() => client.deleteChannel(channelId, channelType)).called(1);
});
test('`.truncate`', () async {
when(() => client.truncateChannel(channelId, channelType))
.thenAnswer((_) async => EmptyResponse());
final res = await channel.truncate();
expect(res, isNotNull);
verify(() => client.truncateChannel(channelId, channelType)).called(1);
});
test('`.acceptInvite`', () async {
final message = Message(id: 'test-message-id', text: 'Invite Accepted');
final channelModel = ChannelModel(cid: channelCid);
when(() => client.acceptChannelInvite(channelId, channelType,
message: any(named: 'message'))).thenAnswer(
(_) async => AcceptInviteResponse()
..channel = channelModel
..message = message,
);
final res = await channel.acceptInvite(message);
expect(res, isNotNull);
expect(res.channel.cid, channelModel.cid);
expect(res.message?.id, message.id);
verify(() => client.acceptChannelInvite(channelId, channelType,
message: any(named: 'message'))).called(1);
});
test('`.rejectInvite`', () async {
final message = Message(id: 'test-message-id', text: 'Invite Rejected');
final channelModel = ChannelModel(cid: channelCid);
when(() => client.rejectChannelInvite(channelId, channelType,
message: any(named: 'message'))).thenAnswer(
(_) async => RejectInviteResponse()
..channel = channelModel
..message = message,
);
final res = await channel.rejectInvite(message);
expect(res, isNotNull);
expect(res.channel.cid, channelModel.cid);
expect(res.message?.id, message.id);
verify(() => client.rejectChannelInvite(channelId, channelType,
message: any(named: 'message'))).called(1);
});
test('`.addMembers`', () async {
final members = List.generate(
3,
(index) => Member(userId: 'test-member-id-$index'),
);
final memberIds = members
.map((it) => it.userId)
.whereType<String>()
.toList(growable: false);
final message = Message(id: 'test-message-id', text: 'Members Added');
final channelModel = ChannelModel(cid: channelCid);
when(() => client.addChannelMembers(channelId, channelType, memberIds,
message: any(named: 'message'))).thenAnswer(
(_) async => AddMembersResponse()
..channel = channelModel
..members = members
..message = message,
);
final res = await channel.addMembers(memberIds, message);
expect(res, isNotNull);
expect(res.channel.cid, channelModel.cid);
expect(res.members.length, members.length);
expect(res.message?.id, message.id);
verify(() => client.addChannelMembers(channelId, channelType, memberIds,
message: any(named: 'message'))).called(1);
});
test('`.inviteMembers`', () async {
final members = List.generate(
3,
(index) => Member(userId: 'test-member-id-$index'),
);
final memberIds = members
.map((it) => it.userId)
.whereType<String>()
.toList(growable: false);
final message = Message(id: 'test-message-id', text: 'Members Invited');
final channelModel = ChannelModel(cid: channelCid);
when(() => client.inviteChannelMembers(channelId, channelType, memberIds,
message: any(named: 'message'))).thenAnswer(
(_) async => InviteMembersResponse()
..channel = channelModel
..members = members
..message = message,
);
final res = await channel.inviteMembers(memberIds, message);
expect(res, isNotNull);
expect(res.channel.cid, channelModel.cid);
expect(res.members.length, members.length);
expect(res.message?.id, message.id);
verify(() => client.inviteChannelMembers(
channelId, channelType, memberIds,
message: any(named: 'message'))).called(1);
});
test('`.removeMembers`', () async {
final members = List.generate(
3,
(index) => Member(userId: 'test-member-id-$index'),
);
final memberIds = members
.map((it) => it.userId)
.whereType<String>()
.toList(growable: false);
final message = Message(id: 'test-message-id', text: 'Members Removed');
final channelModel = ChannelModel(cid: channelCid);
when(() => client.removeChannelMembers(channelId, channelType, memberIds,
message: any(named: 'message'))).thenAnswer(
(_) async => RemoveMembersResponse()
..channel = channelModel
..members = members
..message = message,
);
final res = await channel.removeMembers(memberIds, message);
expect(res, isNotNull);
expect(res.channel.cid, channelModel.cid);
expect(res.members.length, members.length);
expect(res.message?.id, message.id);
verify(() => client.removeChannelMembers(
channelId, channelType, memberIds,
message: any(named: 'message'))).called(1);
});
group('`.sendAction`', () {
test('should work fine', () async {
final message = Message(id: 'test-message-id', text: 'Action Sent');
const formData = {'key': 'value'};
when(
() => client.sendAction(channelId, channelType, message.id, formData),
).thenAnswer((_) async => SendActionResponse());
final res = await channel.sendAction(message, formData);
expect(res, isNotNull);
verify(
() => client.sendAction(channelId, channelType, message.id, formData),
).called(1);
});
test('should emit received message if not null', () async {
final message = Message(id: 'test-message-id', text: 'Action Sent');
const formData = {'key': 'value'};
when(
() => client.sendAction(channelId, channelType, message.id, formData),
).thenAnswer((_) async => SendActionResponse()..message = message);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message,
matchSendingStatus: true,
),
],
]),
);
final res = await channel.sendAction(message, formData);
expect(res, isNotNull);
expect(res.message?.id, message.id);
verify(
() => client.sendAction(channelId, channelType, message.id, formData),
).called(1);
});
});
test('`.markRead`', () async {
const messageId = 'test-message-id';
when(() => client.markChannelRead(channelId, channelType,
messageId: messageId)).thenAnswer((_) async => EmptyResponse());
final res = await channel.markRead(messageId: messageId);
expect(res, isNotNull);
expect(client.state.totalUnreadCount, 0);
verify(() => client.markChannelRead(channelId, channelType,
messageId: messageId)).called(1);
});
group('`.watch`', () {
test('should work fine', () async {
when(() => client.queryChannel(
channelType,
channelId: channelId,
watch: true,
channelData: any(named: 'channelData'),
messagesPagination: any(named: 'messagesPagination'),
membersPagination: any(named: 'membersPagination'),
watchersPagination: any(named: 'watchersPagination'),
)).thenAnswer(
(_) async => _generateChannelState(channelId, channelType),
);
final res = await channel.watch();
expect(res, isNotNull);
expect(res.channel, isNotNull);
expect(res.channel?.cid, channelCid);
verify(() => client.queryChannel(
channelType,
channelId: channelId,
watch: true,
channelData: any(named: 'channelData'),
messagesPagination: any(named: 'messagesPagination'),
membersPagination: any(named: 'membersPagination'),
watchersPagination: any(named: 'watchersPagination'),
)).called(1);
});
test('should rethrow if `.query` throws', () async {
when(() => client.queryChannel(
channelType,
channelId: channelId,
watch: true,
channelData: any(named: 'channelData'),
messagesPagination: any(named: 'messagesPagination'),
membersPagination: any(named: 'membersPagination'),
watchersPagination: any(named: 'watchersPagination'),
)).thenThrow(StreamChatNetworkError(ChatErrorCode.inputError));
try {
await channel.watch();
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
}
verify(() => client.queryChannel(
channelType,
channelId: channelId,
watch: true,
channelData: any(named: 'channelData'),
messagesPagination: any(named: 'messagesPagination'),
membersPagination: any(named: 'membersPagination'),
watchersPagination: any(named: 'watchersPagination'),
)).called(1);
});
});
test('`.stopWatching`', () async {
when(() => client.stopChannelWatching(channelId, channelType))
.thenAnswer((_) async => EmptyResponse());
final res = await channel.stopWatching();
expect(res, isNotNull);
verify(() => client.stopChannelWatching(channelId, channelType))
.called(1);
});
test('`.getReplies`', () async {
const parentId = 'test-parent-id';
final messages = List.generate(
3,
(index) => Message(
id: 'test-message-id-$index',
parentId: parentId,
),
);
when(() => client.getReplies(parentId)).thenAnswer(
(_) async => QueryRepliesResponse()..messages = messages,
);
final res = await channel.getReplies(parentId);
expect(res, isNotNull);
expect(res.messages.length, messages.length);
expect(res.messages.every((it) => it.parentId == parentId), isTrue);
verify(() => client.getReplies(parentId)).called(1);
});
test('`.getReactions`', () async {
const messageId = 'test-message-id';
final reactions = List.generate(
3,
(index) => Reaction(
type: 'test-reaction-type-$index',
messageId: messageId,
),
);
when(() => client.getReactions(messageId)).thenAnswer(
(_) async => QueryReactionsResponse()..reactions = reactions,
);
final res = await channel.getReactions(messageId);
expect(res, isNotNull);
expect(res.reactions.length, reactions.length);
expect(res.reactions.every((it) => it.messageId == messageId), isTrue);
verify(() => client.getReactions(messageId)).called(1);
});
test('`.getMessagesById`', () async {
final messages = List.generate(
3,
(index) => Message(id: 'test-message-id-$index'),
);
final messageIds = messages.map((it) => it.id).toList(growable: false);
when(() => client.getMessagesById(channelId, channelType, messageIds))
.thenAnswer(
(_) async => GetMessagesByIdResponse()..messages = messages,
);
final res = await channel.getMessagesById(messageIds);
expect(res, isNotNull);
expect(res.messages.length, messageIds.length);
verify(
() => client.getMessagesById(channelId, channelType, messageIds),
).called(1);
});
test('`.translateMessage`', () async {
const messageId = 'test-message-id';
const language = 'hi'; // Hindi
const translatedMessageText = 'नमस्ते';
final translatedMessage = Message(
i18n: const {
language: translatedMessageText,
},
);
when(() => client.translateMessage(messageId, language)).thenAnswer(
(_) async => TranslateMessageResponse()..message = translatedMessage,
);
final res = await channel.translateMessage(messageId, language);
expect(res, isNotNull);
expect(res.message.i18n, translatedMessage.i18n);
verify(() => client.translateMessage(messageId, language)).called(1);
});
group('`.query`', () {
test('should work fine', () async {
final channelState = _generateChannelState(channelId, channelType);
when(
() => client.queryChannel(
channelType,
channelId: channelId,
channelData: any(named: 'channelData'),
messagesPagination: any(named: 'messagesPagination'),
membersPagination: any(named: 'membersPagination'),
watchersPagination: any(named: 'watchersPagination'),
),
).thenAnswer((_) async => channelState);
final res = await channel.query();
expect(res, isNotNull);
verify(
() => client.queryChannel(
channelType,
channelId: channelId,
channelData: any(named: 'channelData'),
messagesPagination: any(named: 'messagesPagination'),
membersPagination: any(named: 'membersPagination'),
watchersPagination: any(named: 'watchersPagination'),
),
).called(1);
});
test('should rethrow if `client.queryChannel` throws', () async {
when(
() => client.queryChannel(
channelType,
channelId: channelId,
channelData: any(named: 'channelData'),
messagesPagination: any(named: 'messagesPagination'),
membersPagination: any(named: 'membersPagination'),
watchersPagination: any(named: 'watchersPagination'),
),
).thenThrow(StreamChatNetworkError(ChatErrorCode.inputError));
try {
await channel.query();
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
}
verify(
() => client.queryChannel(
channelType,
channelId: channelId,
channelData: any(named: 'channelData'),
messagesPagination: any(named: 'messagesPagination'),
membersPagination: any(named: 'membersPagination'),
watchersPagination: any(named: 'watchersPagination'),
),
).called(1);
});
});
test('`.queryMembers`', () async {
final filter = Filter.in_('cid', const [channelCid]);
final members = List.generate(
3,
(index) => Member(userId: 'test-user-id-$index'),
);
when(() => client.queryMembers(
channelType,
channelId: channelId,
filter: filter,
members: any(named: 'members'),
sort: any(named: 'sort'),
pagination: any(named: 'pagination'),
)).thenAnswer((_) async => QueryMembersResponse()..members = members);
final res = await channel.queryMembers(filter: filter);
expect(res, isNotNull);
expect(res.members.length, members.length);
verify(() => client.queryMembers(
channelType,
channelId: channelId,
filter: filter,
members: any(named: 'members'),
sort: any(named: 'sort'),
pagination: any(named: 'pagination'),
)).called(1);
});
test('`.mute`', () async {
when(() => client.muteChannel(
channelCid,
expiration: any(named: 'expiration'),
)).thenAnswer((_) async => EmptyResponse());
final res = await channel.mute();
expect(res, isNotNull);
verify(() => client.muteChannel(
channelCid,
expiration: any(named: 'expiration'),
)).called(1);
});
test('`.unmute`', () async {
when(
() => client.unmuteChannel(channelCid),
).thenAnswer((_) async => EmptyResponse());
final res = await channel.unmute();
expect(res, isNotNull);
verify(
() => client.unmuteChannel(channelCid),
).called(1);
});
test('`.enableSlowMode`', () async {
const cooldown = 10;
final channelModel = ChannelModel(
cid: channelCid,
cooldown: cooldown,
);
when(() => client.enableSlowdown(
channelId,
channelType,
cooldown,
)).thenAnswer((_) async => PartialUpdateChannelResponse()
..channel = channelModel);
final res = await channel.enableSlowMode(cooldownInterval: 10);
expect(res, isNotNull);
verify(() => client.enableSlowdown(
channelId,
channelType,
cooldown,
)).called(1);
});
test('`.disableSlowMode`', () async {
final channelModel = ChannelModel(
cid: channelCid,
);
when(() => client.disableSlowdown(
channelId,
channelType,
)).thenAnswer((_) async => PartialUpdateChannelResponse()
..channel = channelModel);
final res = await channel.disableSlowMode();
expect(res, isNotNull);
verify(() => client.disableSlowdown(channelId, channelType)).called(1);
});
test('`.banUser`', () async {
const userId = 'test-user-id';
const options = {'key': 'value'};
when(() => client.banUser(
userId,
{'type': channelType, 'id': channelId, ...options},
)).thenAnswer((_) async => EmptyResponse());
final res = await channel.banUser(userId, options);
expect(res, isNotNull);
verify(() => client.banUser(
userId,
{'type': channelType, 'id': channelId, ...options},
)).called(1);
});
test('`.unbanUser`', () async {
const userId = 'test-user-id';
when(() => client.unbanUser(userId, any()))
.thenAnswer((_) async => EmptyResponse());
final res = await channel.unbanUser(userId);
expect(res, isNotNull);
verify(() => client.unbanUser(userId, any())).called(1);
});
test('`.shadowBan`', () async {
const userId = 'test-user-id';
const options = {'key': 'value'};
when(() => client.shadowBan(
userId,
{'type': channelType, 'id': channelId, ...options},
)).thenAnswer((_) async => EmptyResponse());
final res = await channel.shadowBan(userId, options);
expect(res, isNotNull);
verify(() => client.shadowBan(
userId,
{'type': channelType, 'id': channelId, ...options},
)).called(1);
});
test('`.removeShadowBan`', () async {
const userId = 'test-user-id';
when(() => client.removeShadowBan(userId, any()))
.thenAnswer((_) async => EmptyResponse());
final res = await channel.removeShadowBan(userId);
expect(res, isNotNull);
verify(() => client.removeShadowBan(userId, any())).called(1);
});
test('`.hide`', () async {
const clearHistory = true;
when(() => client.hideChannel(
channelId,
channelType,
clearHistory: clearHistory,
)).thenAnswer((_) async => EmptyResponse());
final res = await channel.hide(clearHistory: clearHistory);
expect(res, isNotNull);
verify(() => client.hideChannel(
channelId,
channelType,
clearHistory: clearHistory,
)).called(1);
});
test('`.show`', () async {
when(() => client.showChannel(channelId, channelType))
.thenAnswer((_) async => EmptyResponse());
final res = await channel.show();
expect(res, isNotNull);
verify(() => client.showChannel(channelId, channelType)).called(1);
});
test('`.on`', () async {
const eventType = 'test.event';
final event = Event(type: eventType, cid: channelCid);
when(() => client.on(eventType, any(), any(), any()))
.thenAnswer((_) => Stream.value(event));
expectLater(channel.on(eventType), emitsInOrder([event]));
verify(() => client.on(eventType, any(), any(), any())).called(1);
});
group(
'`.keyStroke`',
() {
test('should return if `config.typingEvents` is false', () async {
when(() => channel.config?.typingEvents).thenReturn(false);
final typingEvent = Event(type: EventType.typingStart);
await channel.keyStroke();
verifyNever(() => client.sendEvent(
channelId,
channelType,
any(that: isSameEventAs(typingEvent)),
));
});
test(
'''should send `typingStart` event if there is not already a typingEvent or the difference between the two is >= 2 seconds''',
() async {
final typingEvent = Event(type: EventType.typingStart);
when(() => channel.config?.typingEvents).thenReturn(true);
when(() => client.sendEvent(
channelId,
channelType,
any(that: isSameEventAs(typingEvent)),
)).thenAnswer((_) async => EmptyResponse());
await channel.keyStroke();
verify(() => client.sendEvent(
channelId,
channelType,
any(that: isSameEventAs(typingEvent)),
)).called(1);
},
);
},
);
group('`.stopTyping`', () {
test('should return if `config.typingEvents` is false', () async {
when(() => channel.config?.typingEvents).thenReturn(false);
final typingStopEvent = Event(type: EventType.typingStop);
await channel.keyStroke();
verifyNever(() => client.sendEvent(
channelId,
channelType,
any(that: isSameEventAs(typingStopEvent)),
));
});
test('should send `typingStop` successfully', () async {
final typingStopEvent = Event(type: EventType.typingStop);
when(() => channel.config?.typingEvents).thenReturn(true);
when(() => client.sendEvent(
channelId,
channelType,
any(that: isSameEventAs(typingStopEvent)),
)).thenAnswer((_) async => EmptyResponse());
await channel.stopTyping();
verify(() => client.sendEvent(
channelId,
channelType,
any(that: isSameEventAs(typingStopEvent)),
)).called(1);
});
});
});
}