add initialized channel tests
Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
@@ -51,9 +51,9 @@ class Channel {
|
||||
|
||||
String? _id;
|
||||
String? _cid;
|
||||
final Map<String, dynamic> _extraData;
|
||||
final Map<String, Object?> _extraData;
|
||||
|
||||
set extraData(Map<String, dynamic> extraData) {
|
||||
set extraData(Map<String, Object?> extraData) {
|
||||
if (_initializedCompleter.isCompleted) {
|
||||
throw StateError(
|
||||
'Once the channel is initialized you should use channel.update '
|
||||
@@ -734,7 +734,7 @@ class Channel {
|
||||
|
||||
/// Edit the channel custom data
|
||||
Future<UpdateChannelResponse> update(
|
||||
Map<String, dynamic> channelData, [
|
||||
Map<String, Object?> channelData, [
|
||||
Message? updateMessage,
|
||||
]) async {
|
||||
_checkInitialized();
|
||||
@@ -814,19 +814,24 @@ class Channel {
|
||||
final messageId = message.id;
|
||||
final res = await _client.sendAction(id!, type, messageId, formData);
|
||||
|
||||
// update the passed message with response message
|
||||
if (res.message != null) {
|
||||
state!.addMessage(res.message!);
|
||||
} else {
|
||||
// remove the passed message if response does
|
||||
// not contain message
|
||||
final oldIndex = state!.messages.indexWhere((m) => m.id == messageId);
|
||||
|
||||
Message? oldMessage;
|
||||
// remove regular message if present
|
||||
if (oldIndex != -1) {
|
||||
oldMessage = state!.messages[oldIndex];
|
||||
final oldMessage = state!.messages[oldIndex];
|
||||
state!.updateChannelState(state!._channelState.copyWith(
|
||||
messages: state?.messages?..remove(oldMessage),
|
||||
));
|
||||
} else {
|
||||
oldMessage = state!.threads.values
|
||||
// remove thread message if present
|
||||
// also reduces total reply count
|
||||
final oldMessage = state!.threads.values
|
||||
.expand((messages) => messages)
|
||||
.firstWhereOrNull((m) => m.id == messageId);
|
||||
if (oldMessage?.parentId != null) {
|
||||
@@ -858,18 +863,11 @@ class Channel {
|
||||
}
|
||||
|
||||
/// Loads the initial channel state and watches for changes
|
||||
Future<ChannelState> watch([Map<String, dynamic> options = const {}]) async {
|
||||
final watchOptions = Map<String, dynamic>.from({
|
||||
'state': true,
|
||||
'watch': true,
|
||||
'presence': false,
|
||||
})
|
||||
..addAll(options);
|
||||
|
||||
Future<ChannelState> watch() async {
|
||||
ChannelState response;
|
||||
|
||||
try {
|
||||
response = await query(options: watchOptions);
|
||||
response = await query(watch: true);
|
||||
} catch (error, stackTrace) {
|
||||
if (!_initializedCompleter.isCompleted) {
|
||||
_initializedCompleter.completeError(error, stackTrace);
|
||||
@@ -956,17 +954,15 @@ class Channel {
|
||||
);
|
||||
|
||||
/// Creates a new channel
|
||||
Future<ChannelState> create() async => query(options: {
|
||||
'watch': false,
|
||||
'state': false,
|
||||
'presence': false,
|
||||
});
|
||||
Future<ChannelState> create() async => query(state: false);
|
||||
|
||||
/// Query the API, get messages, members or other channel fields
|
||||
/// Set [preferOffline] to true to avoid the api call if the data is already
|
||||
/// in the offline storage
|
||||
Future<ChannelState> query({
|
||||
Map<String, dynamic> options = const {},
|
||||
bool state = true,
|
||||
bool watch = false,
|
||||
bool presence = false,
|
||||
PaginationParams? messagesPagination,
|
||||
PaginationParams? membersPagination,
|
||||
PaginationParams? watchersPagination,
|
||||
@@ -976,10 +972,10 @@ class Channel {
|
||||
final updatedState = await _client.chatPersistenceClient
|
||||
?.getChannelStateByCid(cid!, messagePagination: messagesPagination);
|
||||
if (updatedState != null && updatedState.messages.isNotEmpty) {
|
||||
if (state == null) {
|
||||
if (this.state == null) {
|
||||
_initState(updatedState);
|
||||
} else {
|
||||
state?.updateChannelState(updatedState);
|
||||
this.state?.updateChannelState(updatedState);
|
||||
}
|
||||
return updatedState;
|
||||
}
|
||||
@@ -990,6 +986,9 @@ class Channel {
|
||||
type,
|
||||
channelId: id,
|
||||
channelData: _extraData,
|
||||
state: state,
|
||||
watch: watch,
|
||||
presence: presence,
|
||||
messagesPagination: messagesPagination,
|
||||
membersPagination: membersPagination,
|
||||
watchersPagination: watchersPagination,
|
||||
@@ -1000,7 +999,7 @@ class Channel {
|
||||
_cid = updatedState.channel!.cid;
|
||||
}
|
||||
|
||||
state?.updateChannelState(updatedState);
|
||||
this.state?.updateChannelState(updatedState);
|
||||
return updatedState;
|
||||
} catch (e) {
|
||||
if (!_client.persistenceEnabled) {
|
||||
@@ -1775,7 +1774,7 @@ class ChannelClientState {
|
||||
);
|
||||
}
|
||||
|
||||
late Timer _cleaningTimer;
|
||||
Timer? _cleaningTimer;
|
||||
|
||||
void _startCleaning() {
|
||||
if (_channelState.channel?.config.typingEvents == false) {
|
||||
@@ -1842,7 +1841,7 @@ class ChannelClientState {
|
||||
_channelStateController.close();
|
||||
_isUpToDateController.close();
|
||||
_threadsController.close();
|
||||
_cleaningTimer.cancel();
|
||||
_cleaningTimer?.cancel();
|
||||
_pinnedMessagesTimer.cancel();
|
||||
_typingEventsController.close();
|
||||
}
|
||||
|
||||
@@ -757,7 +757,7 @@ class StreamChatClient {
|
||||
Future<UpdateChannelResponse> updateChannel(
|
||||
String channelId,
|
||||
String channelType,
|
||||
Map<String, dynamic> data, {
|
||||
Map<String, Object?> data, {
|
||||
Message? message,
|
||||
}) =>
|
||||
_chatApi.channel.updateChannel(
|
||||
|
||||
@@ -92,7 +92,7 @@ class ChannelApi {
|
||||
Future<UpdateChannelResponse> updateChannel(
|
||||
String channelId,
|
||||
String channelType,
|
||||
Map<String, dynamic> data, {
|
||||
Map<String, Object?> data, {
|
||||
Message? message,
|
||||
}) async {
|
||||
final response = await _client.post(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -82,3 +82,24 @@ class FakeChatApi extends Fake implements StreamChatApi {
|
||||
AttachmentFileUploader get fileUploader =>
|
||||
_fileUploader ??= MockAttachmentFileUploader();
|
||||
}
|
||||
|
||||
class FakeClientState extends Fake implements ClientState {
|
||||
@override
|
||||
OwnUser? get user => OwnUser(id: 'test-user-id');
|
||||
|
||||
var _totalUnreadCount = 0;
|
||||
|
||||
@override
|
||||
int? get totalUnreadCount => _totalUnreadCount;
|
||||
|
||||
@override
|
||||
set totalUnreadCount(int? unreadCount) {
|
||||
_totalUnreadCount += unreadCount ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeMessage extends Fake implements Message {}
|
||||
|
||||
class FakeAttachmentFile extends Fake implements AttachmentFile {}
|
||||
|
||||
class FakeEvent extends Fake implements Event {}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dio/dio.dart' show MultipartFile;
|
||||
import 'package:stream_chat/src/core/models/event.dart';
|
||||
import 'package:stream_chat/src/core/models/message.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
Matcher isSameMultipartFileAs(MultipartFile targetFile) =>
|
||||
@@ -17,3 +20,72 @@ class _IsSameMultipartFileAs extends Matcher {
|
||||
bool matches(covariant MultipartFile file, Map matchState) =>
|
||||
file.length == targetFile.length;
|
||||
}
|
||||
|
||||
Matcher isSameEventAs(Event targetEvent) =>
|
||||
_IsSameEventAs(targetEvent: targetEvent);
|
||||
|
||||
class _IsSameEventAs extends Matcher {
|
||||
const _IsSameEventAs({required this.targetEvent});
|
||||
|
||||
final Event targetEvent;
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same event as $targetEvent');
|
||||
|
||||
@override
|
||||
bool matches(covariant Event event, Map matchState) =>
|
||||
event.type == targetEvent.type;
|
||||
}
|
||||
|
||||
Matcher isSameMessageAs(
|
||||
Message targetMessage, {
|
||||
bool matchReactions = false,
|
||||
bool matchSendingStatus = false,
|
||||
}) =>
|
||||
_IsSameMessageAs(
|
||||
targetMessage: targetMessage,
|
||||
matchReactions: matchReactions,
|
||||
matchSendingStatus: matchSendingStatus,
|
||||
);
|
||||
|
||||
class _IsSameMessageAs extends Matcher {
|
||||
const _IsSameMessageAs({
|
||||
required this.targetMessage,
|
||||
this.matchReactions = false,
|
||||
this.matchSendingStatus = false,
|
||||
});
|
||||
|
||||
final Message targetMessage;
|
||||
final bool matchReactions;
|
||||
final bool matchSendingStatus;
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same message as $targetMessage');
|
||||
|
||||
@override
|
||||
bool matches(covariant Message message, Map matchState) {
|
||||
var matches = message.id == targetMessage.id;
|
||||
if (matchSendingStatus) {
|
||||
matches &= message.status == targetMessage.status;
|
||||
}
|
||||
if (matchReactions) {
|
||||
matches &= const ListEquality().equals(
|
||||
message.ownReactions
|
||||
?.map((it) => '${it.type}-${it.messageId}')
|
||||
.toList(),
|
||||
targetMessage.ownReactions
|
||||
?.map((it) => '${it.type}-${it.messageId}')
|
||||
.toList());
|
||||
matches &= const ListEquality().equals(
|
||||
message.latestReactions
|
||||
?.map((it) => '${it.type}-${it.messageId}')
|
||||
.toList(),
|
||||
targetMessage.latestReactions
|
||||
?.map((it) => '${it.type}-${it.messageId}')
|
||||
.toList());
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:stream_chat/src/client/client.dart';
|
||||
import 'package:stream_chat/src/core/api/attachment_file_uploader.dart';
|
||||
import 'package:stream_chat/src/core/api/channel_api.dart';
|
||||
import 'package:stream_chat/src/core/api/device_api.dart';
|
||||
@@ -12,8 +13,13 @@ import 'package:stream_chat/src/core/api/user_api.dart';
|
||||
import 'package:stream_chat/src/core/http/connection_id_manager.dart';
|
||||
import 'package:stream_chat/src/core/http/stream_http_client.dart';
|
||||
import 'package:stream_chat/src/core/http/token_manager.dart';
|
||||
import 'package:stream_chat/src/core/models/channel_config.dart';
|
||||
import 'package:stream_chat/src/core/models/channel_model.dart';
|
||||
import 'package:stream_chat/src/db/chat_persistence_client.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
|
||||
import 'db/chat_persistence_client_test.dart';
|
||||
|
||||
class MockWebSocketChannel extends Mock implements WebSocketChannel {}
|
||||
|
||||
class MockWebSocketSink extends Mock implements WebSocketSink {}
|
||||
@@ -57,3 +63,24 @@ class MockGeneralApi extends Mock implements GeneralApi {}
|
||||
|
||||
class MockAttachmentFileUploader extends Mock
|
||||
implements AttachmentFileUploader {}
|
||||
|
||||
class MockPersistenceClient extends Mock implements ChatPersistenceClient {}
|
||||
|
||||
class MockStreamChatClient extends Mock implements StreamChatClient {
|
||||
@override
|
||||
bool get persistenceEnabled => false;
|
||||
}
|
||||
|
||||
class MockStreamChatClientWithPersistence extends Mock
|
||||
implements StreamChatClient {
|
||||
ChatPersistenceClient? _persistenceClient;
|
||||
|
||||
@override
|
||||
ChatPersistenceClient get chatPersistenceClient =>
|
||||
_persistenceClient ??= MockPersistenceClient();
|
||||
|
||||
@override
|
||||
bool get persistenceEnabled => true;
|
||||
}
|
||||
|
||||
class MockChannelConfig extends Mock implements ChannelConfig {}
|
||||
|
||||
Reference in New Issue
Block a user