chore: 1.5.1 (#387)
* lint(persistence): Apply team linter Signed-off-by: Sahil Kumar <[email protected]> * style: flutter format Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): Add tests for channel dao Signed-off-by: Sahil Kumar <[email protected]> * refactor(persistence): remove dummy comments Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): Add test for channel query dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for connection event dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add test for member dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for message dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add test for pinned message dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add test for reaction dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add test for read dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): minor improvement in update function tests Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for user dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add test for list converter Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add test for map converter Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for message sending status converter Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for channel mapper Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add test for event mapper Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for member mapper Signed-off-by: Sahil Kumar <[email protected]> * remove useless dependencies * test(persistence): add tests for message mapper Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for pinned message mapper Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for reaction mapper Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for read mapper Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for user mapper Signed-off-by: Sahil Kumar <[email protected]> * refactor(persistence): remove redundant UsersTable getter from ConnectionEventDao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests for stream chat persistence client Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add tests moor chat database Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add moor schemas for future migration tests Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add clear queryCache test in channel query dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add getThreadMessagesByParentId along with pagination test in message, pinned message dao. Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add getMessagesByCid along with pagination test in message, pinned message dao. Signed-off-by: Sahil Kumar <[email protected]> * update ci * apply locale to jiffy * fix test * test(persistence): add getMessagesByCid along with quotedMessage test in message, pinned message dao. Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add extra checks in getMessagesByCid along with pagination test in message, pinned message dao. Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add return channel with pagination applies test in channel query dao Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): improve message mapper, pinned message mapper test Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add client constructor test in stream chat persistence client Signed-off-by: Sahil Kumar <[email protected]> * test(persistence): add coverage:ignore-file for entities and db/shared directory Signed-off-by: Sahil Kumar <[email protected]> * skip pub publish --dry-run for develop branch Signed-off-by: Sahil Kumar <[email protected]> * add remove from coverage script Signed-off-by: Sahil Kumar <[email protected]> * fix Signed-off-by: Sahil Kumar <[email protected]> * fix Signed-off-by: Sahil Kumar <[email protected]> * fix Signed-off-by: Sahil Kumar <[email protected]> * fix Signed-off-by: Sahil Kumar <[email protected]> * fix locale * fix message input _parseExistingMessage * fix(UI-Kit): Show selected attachment file as file in message input widget Signed-off-by: Sahil Kumar <[email protected]> * fix: ws disconnection (#345) * update ws removing custom implementation; fixed disconnection while connecting * add test * feat: Apply team lints to core package (#334) * Add analyzer file to core package * Apply team lint rules to core package * clean up analyzer warnings in stream chat flutter * remove unused line * add ignore * sync analysis options * [Core -> MessageWidget] Fix variable dynamic types Signed-off-by: Sahil Kumar <[email protected]> Co-authored-by: Salvatore Giordano <[email protected]> Co-authored-by: Sahil Kumar <[email protected]> * test: add tests for stream chat flutter core (#354) * test(core): add tests for channels bloc Signed-off-by: Sahil Kumar <[email protected]> * test(core): refactor some tests due to changes in channels bloc Signed-off-by: Sahil Kumar <[email protected]> * test(core): add tests for channel list core Signed-off-by: Sahil Kumar <[email protected]> * test(core): add tests for users bloc, minor improvements Signed-off-by: Sahil Kumar <[email protected]> * test(core): add tests for user list core, minor improvements Signed-off-by: Sahil Kumar <[email protected]> * test(core): add tests for message search bloc, refactor Signed-off-by: Sahil Kumar <[email protected]> * test(core): add tests for message search list core, refactor Signed-off-by: Sahil Kumar <[email protected]> * test(core): add tests for lazy load scroll view Signed-off-by: Sahil Kumar <[email protected]> * test(core): add tests for stream chat core Signed-off-by: Sahil Kumar <[email protected]> * test(core): add tests for message list core Signed-off-by: Sahil Kumar <[email protected]> * test(core): add tests for stream channel Signed-off-by: Sahil Kumar <[email protected]> * Fix merge conflicts Signed-off-by: Sahil Kumar <[email protected]> * flutter format Signed-off-by: Sahil Kumar <[email protected]> * fixes Signed-off-by: Sahil Kumar <[email protected]> * update workflow Signed-off-by: Sahil Kumar <[email protected]> * chore(core): fix analyzer issues Signed-off-by: Sahil Kumar <[email protected]> * chore(ui-kit): fix analyzer issues Signed-off-by: Sahil Kumar <[email protected]> * refactor(ui-kit): update giphy interaction (#361) * refactor(ui-kit): update giphy interaction Signed-off-by: Sahil Kumar <[email protected]> * fix overflow Co-authored-by: Salvatore Giordano <[email protected]> * fix: Fixed sendingbutton.outside case (#355) * fix: Fixed sendingbutton.outside case * fix: Fixed all actionbutton cases * fix review Co-authored-by: Salvatore Giordano <[email protected]> * fix(ui-kit): fix accessibility text size scaling in message widget Signed-off-by: Sahil Kumar <[email protected]> * fix(ui-kit): fix message widget long username overflow Signed-off-by: Sahil Kumar <[email protected]> * fix(ui-kit): fix message input dm checkbox accessibility scaling issue Signed-off-by: Sahil Kumar <[email protected]> * feat: exposed textbuilder callback (#367) * exposed textbuilder callback * exposed textbuilder callback * fixes Signed-off-by: Sahil Kumar <[email protected]> * check if user.mutes is null * check if user.mutes is null * fixes #370: remove all controls on full screen media if message is of type ephemeral (#373) * fix: fix channel list pagination spinner (#375) Signed-off-by: Sahil Kumar <[email protected]> * fix: attachments upload when size exceeds (#377) * fix: Fixed file upload error * fix: Fixed file upload error * fix coverage script Signed-off-by: Sahil Kumar <[email protected]> * fix: message input (#382) * feat: Added mentions suffix builder * feat: Added builders for mentions and theme element for message input expanded button * refactor: refactored PR to suit other style * fix: fixes trailing commas and changes names * doc: added documentation to new elements * doc: added documentation to new elements * bump version * add meta dep to core * sort deps Co-authored-by: Sahil Kumar <[email protected]> Co-authored-by: Neevash Ramdial (Nash) <[email protected]> Co-authored-by: Deven Joshi <[email protected]>
This commit is contained in:
co-authored by
Sahil Kumar
Neevash Ramdial
Deven Joshi
parent
aeb6da8d38
commit
87ca818a9a
@@ -0,0 +1,504 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter_core/src/channel_list_core.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
const pagination = PaginationParams(offset: 0, limit: 3);
|
||||
|
||||
List<Channel> _generateChannels(
|
||||
StreamChatClient client, {
|
||||
int count = 3,
|
||||
int offset = 0,
|
||||
}) {
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return Channel(
|
||||
client,
|
||||
'testType$index',
|
||||
'testId$index',
|
||||
{'extra_data_key': 'extra_data_value_$index'},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test(
|
||||
'should throw assertion error in case listBuilder is null',
|
||||
() {
|
||||
final channelListCore = () => ChannelListCore(
|
||||
listBuilder: null,
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
expect(channelListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case loadingBuilder is null',
|
||||
() {
|
||||
final channelListCore = () => ChannelListCore(
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: null,
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
expect(channelListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case emptyBuilder is null',
|
||||
() {
|
||||
final channelListCore = () => ChannelListCore(
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: null,
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
expect(channelListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case errorBuilder is null',
|
||||
() {
|
||||
final channelListCore = () => ChannelListCore(
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: null,
|
||||
);
|
||||
expect(channelListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should throw if ChannelListCore is used where ChannelsBloc is not present '
|
||||
'in the widget tree',
|
||||
(tester) async {
|
||||
const channelListCoreKey = Key('channelListCore');
|
||||
final channelListCore = ChannelListCore(
|
||||
key: channelListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(channelListCore);
|
||||
|
||||
expect(find.byKey(channelListCoreKey), findsNothing);
|
||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should render ChannelListCore if used with ChannelsBloc as an ancestor',
|
||||
(tester) async {
|
||||
const channelListCoreKey = Key('channelListCore');
|
||||
final channelListCore = ChannelListCore(
|
||||
key: channelListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: ChannelsBloc(
|
||||
child: channelListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(channelListCoreKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should assign loadData and paginateData callback to '
|
||||
'ChannelListController if passed',
|
||||
(tester) async {
|
||||
const channelListCoreKey = Key('channelListCore');
|
||||
final controller = ChannelListController();
|
||||
final channelListCore = ChannelListCore(
|
||||
key: channelListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
channelListController: controller,
|
||||
);
|
||||
|
||||
expect(controller.loadData, isNull);
|
||||
expect(controller.paginateData, isNull);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: ChannelsBloc(
|
||||
child: channelListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(channelListCoreKey), findsOneWidget);
|
||||
expect(controller.loadData, isNotNull);
|
||||
expect(controller.paginateData, isNotNull);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build error widget if channelsBlocState.channelsStream emits error',
|
||||
(tester) async {
|
||||
const channelListCoreKey = Key('channelListCore');
|
||||
const errorWidgetKey = Key('errorWidget');
|
||||
final channelListCore = ChannelListCore(
|
||||
key: channelListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) =>
|
||||
Container(key: errorWidgetKey),
|
||||
pagination: pagination,
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
const error = 'Error! Error! Error!';
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).thenThrow(error);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: ChannelsBloc(
|
||||
child: channelListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(errorWidgetKey), findsOneWidget);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build empty widget if channelsBlocState.channelsStream emits empty data',
|
||||
(tester) async {
|
||||
const channelListCoreKey = Key('channelListCore');
|
||||
const emptyWidgetKey = Key('emptyWidget');
|
||||
final channelListCore = ChannelListCore(
|
||||
key: channelListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Container(key: emptyWidgetKey),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
pagination: pagination,
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
const channels = <Channel>[];
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).thenAnswer((_) => Stream.value(channels));
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: ChannelsBloc(
|
||||
child: channelListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(emptyWidgetKey), findsOneWidget);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build list widget if channelsBlocState.channelsStream emits some data',
|
||||
(tester) async {
|
||||
const channelListCoreKey = Key('channelListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
final channelListCore = ChannelListCore(
|
||||
key: channelListCoreKey,
|
||||
listBuilder: (_, __) => Container(key: listWidgetKey),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
pagination: pagination,
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
final channels = _generateChannels(mockClient);
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).thenAnswer((_) => Stream.value(channels));
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: ChannelsBloc(
|
||||
child: channelListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build list widget with paginated data '
|
||||
'on calling channelListCoreState.paginateData',
|
||||
(tester) async {
|
||||
const channelListCoreKey = Key('channelListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
final channelListCore = ChannelListCore(
|
||||
key: channelListCoreKey,
|
||||
listBuilder: (_, channels) {
|
||||
return Container(
|
||||
key: listWidgetKey,
|
||||
child: Text(
|
||||
channels.map((e) => e.cid).join(','),
|
||||
),
|
||||
);
|
||||
},
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
pagination: pagination,
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
final channels = _generateChannels(mockClient);
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).thenAnswer((_) => Stream.value(channels));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChatCore(
|
||||
client: mockClient,
|
||||
child: ChannelsBloc(
|
||||
child: channelListCore,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
expect(find.text(channels.map((e) => e.cid).join(',')), findsOneWidget);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).called(1);
|
||||
|
||||
final channelListCoreState = tester.state<ChannelListCoreState>(
|
||||
find.byKey(channelListCoreKey),
|
||||
);
|
||||
|
||||
final offset = channels.length;
|
||||
final paginatedChannels = _generateChannels(mockClient, offset: offset);
|
||||
final updatedPagination = pagination.copyWith(offset: offset);
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: updatedPagination,
|
||||
)).thenAnswer((_) => Stream.value(paginatedChannels));
|
||||
|
||||
await channelListCoreState.paginateData();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
expect(
|
||||
find.text([
|
||||
...channels,
|
||||
...paginatedChannels,
|
||||
].map((e) => e.cid).join(',')),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: updatedPagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should rebuild ChannelListCore with updated widget data '
|
||||
'on calling setState()',
|
||||
(tester) async {
|
||||
StateSetter _stateSetter;
|
||||
int limit = pagination.limit;
|
||||
|
||||
const channelListCoreKey = Key('channelListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
|
||||
ChannelListCore channelListCoreBuilder(int limit) => ChannelListCore(
|
||||
key: channelListCoreKey,
|
||||
listBuilder: (_, channels) {
|
||||
return Container(
|
||||
key: listWidgetKey,
|
||||
child: Text(
|
||||
channels.map((e) => e.cid).join(','),
|
||||
),
|
||||
);
|
||||
},
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
pagination: pagination.copyWith(limit: limit),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
final channels = _generateChannels(mockClient);
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).thenAnswer((_) => Stream.value(channels));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChatCore(
|
||||
client: mockClient,
|
||||
child: ChannelsBloc(
|
||||
child: StatefulBuilder(builder: (context, stateSetter) {
|
||||
// Assigning stateSetter for rebuilding ChannelListCore
|
||||
_stateSetter = stateSetter;
|
||||
return channelListCoreBuilder(limit);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
expect(find.text(channels.map((e) => e.cid).join(',')), findsOneWidget);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: pagination,
|
||||
)).called(1);
|
||||
|
||||
// Rebuilding ChannelListCore with new pagination limit
|
||||
_stateSetter(() => limit = 6);
|
||||
|
||||
final updatedChannels = _generateChannels(mockClient, count: limit);
|
||||
final updatedPagination = pagination.copyWith(limit: limit);
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: updatedPagination,
|
||||
)).thenAnswer((_) => Stream.value(updatedChannels));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
expect(
|
||||
find.text(updatedChannels.map((e) => e.cid).join(',')),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: updatedPagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,847 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
import 'matchers/channel_matcher.dart';
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
List<Channel> _generateChannels(
|
||||
StreamChatClient client, {
|
||||
int count = 3,
|
||||
int offset = 0,
|
||||
}) {
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return Channel(
|
||||
client,
|
||||
'testType$index',
|
||||
'testId$index',
|
||||
{'extra_data_key': 'extra_data_value_$index'},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test(
|
||||
'should throw assertion error if child is null',
|
||||
() async {
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
final channelsBloc = () => ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: null,
|
||||
);
|
||||
expect(channelsBloc, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should throw if ChannelsBloc is used where StreamChat is not present in the widget tree',
|
||||
(tester) async {
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
const childKey = Key('child');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(channelsBloc);
|
||||
|
||||
expect(find.byKey(channelsBlocKey), findsNothing);
|
||||
expect(find.byKey(childKey), findsNothing);
|
||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should render ChannelsBloc if used with StreamChatCore as an ancestor',
|
||||
(tester) async {
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
const childKey = Key('child');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(channelsBlocKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'channelsBlocState.queryChannels() should emit data through channelsStream',
|
||||
(tester) async {
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
const childKey = Key('child');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Builder(
|
||||
key: childKey,
|
||||
builder: (context) {
|
||||
return Offstage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
final offlineChannels = _generateChannels(mockClient);
|
||||
final onlineChannels = _generateChannels(mockClient, offset: 3);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) => Stream.fromIterable([offlineChannels, onlineChannels]),
|
||||
);
|
||||
|
||||
channelsBlocState.queryChannels();
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emitsInOrder([
|
||||
isSameChannelListAs(offlineChannels),
|
||||
isSameChannelListAs(onlineChannels),
|
||||
]),
|
||||
);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'channelsBlocState.channelsStream should emit error '
|
||||
'if client.queryChannels() throws',
|
||||
(tester) async {
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
const childKey = Key('child');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Builder(
|
||||
key: childKey,
|
||||
builder: (context) {
|
||||
return Offstage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
final error = 'Error! Error! Error!';
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenThrow(error);
|
||||
|
||||
channelsBlocState.queryChannels();
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emitsError(error),
|
||||
);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'calling channelsBlocState.queryChannels() again with an offset '
|
||||
'should emit new data through channelsStream and also emit loading state '
|
||||
'through queryChannelsLoading',
|
||||
(tester) async {
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
final channels = _generateChannels(mockClient);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer((_) => Stream.value(channels));
|
||||
|
||||
channelsBlocState.queryChannels();
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emits(isSameChannelListAs(channels)),
|
||||
);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final offset = channels.length;
|
||||
final paginationParams = PaginationParams(offset: offset);
|
||||
|
||||
final newChannels = _generateChannels(mockClient, offset: offset);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: paginationParams,
|
||||
)).thenAnswer(
|
||||
(_) => Stream.value(newChannels),
|
||||
);
|
||||
|
||||
channelsBlocState.queryChannels(paginationParams: paginationParams);
|
||||
|
||||
await Future.wait([
|
||||
expectLater(
|
||||
channelsBlocState.queryChannelsLoading,
|
||||
emitsInOrder([true, false]),
|
||||
),
|
||||
expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emits(isSameChannelListAs(channels + newChannels)),
|
||||
),
|
||||
]);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: paginationParams,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'calling channelsBlocState.queryChannels() again with an offset '
|
||||
'should emit error through queryChannelsLoading if '
|
||||
'client.queryChannels() throws',
|
||||
(tester) async {
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty());
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
final channels = _generateChannels(mockClient);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer((_) => Stream.value(channels));
|
||||
|
||||
channelsBlocState.queryChannels();
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emits(isSameChannelListAs(channels)),
|
||||
);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final offset = channels.length;
|
||||
final paginationParams = PaginationParams(offset: offset);
|
||||
|
||||
final error = 'Error! Error! Error!';
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: paginationParams,
|
||||
)).thenThrow(error);
|
||||
|
||||
channelsBlocState.queryChannels(paginationParams: paginationParams);
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.queryChannelsLoading,
|
||||
emitsError(error),
|
||||
);
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: paginationParams,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
group('event controller test', () {
|
||||
StreamController<Event> eventController;
|
||||
setUp(() {
|
||||
eventController = StreamController<Event>.broadcast();
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'channel should get hide when EventType.channelHidden event is received',
|
||||
(tester) async {
|
||||
final mockClient = MockClient();
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(),
|
||||
);
|
||||
|
||||
when(mockClient.on(any, any, any, any))
|
||||
.thenAnswer((_) => Stream.empty());
|
||||
|
||||
when(mockClient.on(
|
||||
EventType.channelHidden,
|
||||
)).thenAnswer((_) => eventController.stream);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
final channels = _generateChannels(mockClient);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) => Stream.value(channels),
|
||||
);
|
||||
|
||||
await channelsBlocState.queryChannels();
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final channelHiddenEvent = Event(
|
||||
type: EventType.channelHidden,
|
||||
cid: channels.first.cid,
|
||||
);
|
||||
|
||||
eventController.add(channelHiddenEvent);
|
||||
|
||||
final newChannels = [...channels]
|
||||
..removeWhere((it) => it.cid == channelHiddenEvent.cid);
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emitsInOrder([
|
||||
isSameChannelListAs(channels),
|
||||
isSameChannelListAs(newChannels),
|
||||
]),
|
||||
);
|
||||
|
||||
verify(mockClient.on(EventType.channelHidden)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'channel should get removed when EventType.channelDeleted or '
|
||||
'EventType.notificationRemovedFromChannel, event is received',
|
||||
(tester) async {
|
||||
final mockClient = MockClient();
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(),
|
||||
);
|
||||
|
||||
when(mockClient.on(any, any, any, any))
|
||||
.thenAnswer((_) => Stream.empty());
|
||||
|
||||
when(mockClient.on(
|
||||
EventType.channelDeleted,
|
||||
EventType.notificationRemovedFromChannel,
|
||||
)).thenAnswer((_) => eventController.stream);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
final channels = _generateChannels(mockClient);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) => Stream.value(channels),
|
||||
);
|
||||
|
||||
await channelsBlocState.queryChannels();
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final channelDeletedOrNotificationRemovedEvent = Event(
|
||||
channel: EventChannel(cid: channels.first.cid),
|
||||
);
|
||||
|
||||
eventController.add(channelDeletedOrNotificationRemovedEvent);
|
||||
|
||||
final channelCid = channelDeletedOrNotificationRemovedEvent.channel.cid;
|
||||
final newChannels = [...channels]
|
||||
..removeWhere((it) => it.cid == channelCid);
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emitsInOrder([
|
||||
isSameChannelListAs(channels),
|
||||
isSameChannelListAs(newChannels),
|
||||
]),
|
||||
);
|
||||
|
||||
verify(mockClient.on(
|
||||
EventType.channelDeleted,
|
||||
EventType.notificationRemovedFromChannel,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'event channel should be moved to top of the list if present when'
|
||||
'EventType.messageNew event is received',
|
||||
(tester) async {
|
||||
final mockClient = MockClient();
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(),
|
||||
);
|
||||
|
||||
when(mockClient.on(any, any, any, any))
|
||||
.thenAnswer((_) => Stream.empty());
|
||||
|
||||
when(mockClient.on(
|
||||
EventType.messageNew,
|
||||
)).thenAnswer((_) => eventController.stream);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
final channels = _generateChannels(mockClient);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) => Stream.value(channels),
|
||||
);
|
||||
|
||||
await channelsBlocState.queryChannels();
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final messageNewEvent = Event(
|
||||
type: EventType.messageNew,
|
||||
cid: channels.last.cid,
|
||||
);
|
||||
|
||||
eventController.add(messageNewEvent);
|
||||
|
||||
final channelCid = messageNewEvent.cid;
|
||||
final index = channels.indexWhere((it) => it.cid == channelCid);
|
||||
final updatedChannel = channels[index];
|
||||
final newChannels = [...channels]
|
||||
..removeAt(index)
|
||||
..insert(0, updatedChannel);
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emitsInOrder([
|
||||
isSameChannelListAs(channels),
|
||||
isSameChannelListAs(newChannels),
|
||||
]),
|
||||
);
|
||||
|
||||
verify(mockClient.on(EventType.messageNew)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'event channel should be moved to top of the list if present inside '
|
||||
'hiddenChannels list and shouldAddChannel is true when '
|
||||
'EventType.messageNew event is received',
|
||||
(tester) async {
|
||||
final hiddenChannelEventController = StreamController<Event>();
|
||||
|
||||
addTearDown(() {
|
||||
hiddenChannelEventController.close();
|
||||
});
|
||||
|
||||
final mockClient = MockClient();
|
||||
final channels = _generateChannels(mockClient);
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(),
|
||||
shouldAddChannel: (e) => channels.map((it) => it.cid).contains(e.cid),
|
||||
);
|
||||
|
||||
when(mockClient.on(any, any, any, any))
|
||||
.thenAnswer((_) => Stream.empty());
|
||||
|
||||
when(mockClient.on(
|
||||
EventType.channelHidden,
|
||||
)).thenAnswer((_) => hiddenChannelEventController.stream);
|
||||
|
||||
when(mockClient.on(
|
||||
EventType.messageNew,
|
||||
)).thenAnswer((_) => eventController.stream);
|
||||
|
||||
final messageNewEvent = Event(
|
||||
type: EventType.messageNew,
|
||||
cid: channels.last.cid,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) => Stream.value(channels),
|
||||
);
|
||||
|
||||
await channelsBlocState.queryChannels();
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final channelHiddenEvent = Event(
|
||||
type: EventType.channelHidden,
|
||||
cid: channels.last.cid,
|
||||
);
|
||||
|
||||
// Hiding the channel before passing messageNew event
|
||||
hiddenChannelEventController.add(channelHiddenEvent);
|
||||
|
||||
final channelsAfterHiddenEvent = [...channels]..removeLast();
|
||||
|
||||
eventController.add(messageNewEvent);
|
||||
|
||||
final channelCid = messageNewEvent.cid;
|
||||
final index = channels.indexWhere((it) => it.cid == channelCid);
|
||||
final newChannels = [...channels]
|
||||
..removeAt(index)
|
||||
..insert(0, channels[index]);
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emitsInOrder([
|
||||
isSameChannelListAs(channels),
|
||||
isSameChannelListAs(channelsAfterHiddenEvent),
|
||||
isSameChannelListAs(newChannels),
|
||||
]),
|
||||
);
|
||||
|
||||
verify(mockClient.on(EventType.channelHidden)).called(1);
|
||||
verify(mockClient.on(EventType.messageNew)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'event channel should be moved to top of the list if present inside '
|
||||
'channel state and shouldAddChannel is true when '
|
||||
'EventType.messageNew event is received',
|
||||
(tester) async {
|
||||
final mockClient = MockClient();
|
||||
final channels = _generateChannels(mockClient);
|
||||
final stateChannels = {
|
||||
for (var c in _generateChannels(mockClient, offset: 5)) c.cid: c
|
||||
};
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(),
|
||||
shouldAddChannel: (_) => true,
|
||||
);
|
||||
|
||||
when(mockClient.state.channels).thenReturn(stateChannels);
|
||||
|
||||
when(mockClient.on(any, any, any, any))
|
||||
.thenAnswer((_) => Stream.empty());
|
||||
|
||||
when(mockClient.on(
|
||||
EventType.messageNew,
|
||||
)).thenAnswer((_) => eventController.stream);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) => Stream.value(channels),
|
||||
);
|
||||
|
||||
await channelsBlocState.queryChannels();
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final messageNewEvent = Event(
|
||||
type: EventType.messageNew,
|
||||
cid: stateChannels.keys.first,
|
||||
);
|
||||
|
||||
eventController.add(messageNewEvent);
|
||||
|
||||
final newChannels = [...channels]
|
||||
..insert(0, stateChannels[stateChannels.keys.first]);
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emitsInOrder([
|
||||
isSameChannelListAs(channels),
|
||||
isSameChannelListAs(newChannels),
|
||||
]),
|
||||
);
|
||||
|
||||
verify(mockClient.on(EventType.messageNew)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'channels should get sorted according to channelsComparator when '
|
||||
'EventType.messageNew event is received',
|
||||
(tester) async {
|
||||
final mockClient = MockClient();
|
||||
final channels = _generateChannels(mockClient);
|
||||
int channelComparator(Channel a, Channel b) {
|
||||
final aData = a.extraData['extra_data_key'] as String;
|
||||
final bData = b.extraData['extra_data_key'] as String;
|
||||
return bData.compareTo(aData);
|
||||
}
|
||||
|
||||
const channelsBlocKey = Key('channelsBloc');
|
||||
final channelsBloc = ChannelsBloc(
|
||||
key: channelsBlocKey,
|
||||
child: Offstage(),
|
||||
shouldAddChannel: (_) => true,
|
||||
channelsComparator: channelComparator,
|
||||
);
|
||||
|
||||
when(mockClient.on(any, any, any, any))
|
||||
.thenAnswer((_) => Stream.empty());
|
||||
|
||||
when(mockClient.on(
|
||||
EventType.messageNew,
|
||||
)).thenAnswer((_) => eventController.stream);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: channelsBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final channelsBlocState = tester.state<ChannelsBlocState>(
|
||||
find.byKey(channelsBlocKey),
|
||||
);
|
||||
|
||||
when(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) => Stream.value(channels),
|
||||
);
|
||||
|
||||
await channelsBlocState.queryChannels();
|
||||
|
||||
verify(mockClient.queryChannels(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final messageNewEvent = Event(
|
||||
type: EventType.messageNew,
|
||||
cid: channels.first.cid,
|
||||
);
|
||||
|
||||
eventController.add(messageNewEvent);
|
||||
|
||||
final newChannels = [...channels]..sort(channelComparator);
|
||||
|
||||
await expectLater(
|
||||
channelsBlocState.channelsStream,
|
||||
emitsInOrder([
|
||||
isSameChannelListAs(channels),
|
||||
isSameChannelListAs(newChannels),
|
||||
]),
|
||||
);
|
||||
|
||||
verify(mockClient.on(EventType.messageNew)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
tearDown(() {
|
||||
eventController.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter_core/src/lazy_load_scroll_view.dart';
|
||||
|
||||
void main() {
|
||||
test(
|
||||
'should throw assertion error if child is null',
|
||||
() async {
|
||||
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
|
||||
final lazyLoadScrollView = () => LazyLoadScrollView(
|
||||
key: lazyLoadScrollViewKey,
|
||||
child: null,
|
||||
);
|
||||
expect(lazyLoadScrollView, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should render LazyLoadScrollView if child is provided',
|
||||
(tester) async {
|
||||
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
|
||||
final lazyLoadScrollView = LazyLoadScrollView(
|
||||
key: lazyLoadScrollViewKey,
|
||||
child: Offstage(),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(lazyLoadScrollView);
|
||||
|
||||
expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should render LazyLoadScrollView if child is provided',
|
||||
(tester) async {
|
||||
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
|
||||
const childKey = Key('childKey');
|
||||
final lazyLoadScrollView = LazyLoadScrollView(
|
||||
key: lazyLoadScrollViewKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(lazyLoadScrollView);
|
||||
|
||||
expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should call onPageScrollStart if child scrollView scrolls',
|
||||
(tester) async {
|
||||
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
|
||||
const childListViewKey = Key('childListView');
|
||||
|
||||
bool onPageScrollStartCalled = false;
|
||||
|
||||
final lazyLoadScrollView = LazyLoadScrollView(
|
||||
key: lazyLoadScrollViewKey,
|
||||
onPageScrollStart: () {
|
||||
onPageScrollStartCalled = true;
|
||||
},
|
||||
child: ListView(
|
||||
key: childListViewKey,
|
||||
children: List.generate(
|
||||
12,
|
||||
(index) => Container(
|
||||
height: 100,
|
||||
child: Text('Item #$index'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: lazyLoadScrollView,
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget);
|
||||
expect(find.byKey(childListViewKey), findsOneWidget);
|
||||
expect(onPageScrollStartCalled, isFalse);
|
||||
|
||||
await tester.startGesture(const Offset(100.0, 100.0));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(onPageScrollStartCalled, isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should call onPageScrollStart, onPageScrollEnd '
|
||||
'if child scrollView starts scrolling and ends',
|
||||
(tester) async {
|
||||
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
|
||||
const childListViewKey = Key('childListView');
|
||||
|
||||
bool onPageScrollStartCalled = false;
|
||||
bool onPageScrollEndCalled = false;
|
||||
|
||||
final lazyLoadScrollView = LazyLoadScrollView(
|
||||
key: lazyLoadScrollViewKey,
|
||||
onPageScrollStart: () {
|
||||
onPageScrollStartCalled = true;
|
||||
},
|
||||
onPageScrollEnd: () {
|
||||
onPageScrollEndCalled = true;
|
||||
},
|
||||
child: ListView(
|
||||
key: childListViewKey,
|
||||
children: List.generate(
|
||||
12,
|
||||
(index) => Container(
|
||||
height: 100,
|
||||
child: Text('Item #$index'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: lazyLoadScrollView,
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget);
|
||||
expect(find.byKey(childListViewKey), findsOneWidget);
|
||||
expect(onPageScrollStartCalled, isFalse);
|
||||
expect(onPageScrollEndCalled, isFalse);
|
||||
|
||||
final gesture = await tester.createGesture();
|
||||
|
||||
await gesture.down(const Offset(100.0, 100.0));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(onPageScrollStartCalled, isTrue);
|
||||
|
||||
await gesture.up();
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(onPageScrollEndCalled, isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should call onInBetweenOfPage if child scrollView '
|
||||
'scroll position is in-between of view',
|
||||
(tester) async {
|
||||
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
|
||||
const childListViewKey = Key('childListView');
|
||||
|
||||
bool onInBetweenOfPageCalled = false;
|
||||
|
||||
final lazyLoadScrollView = LazyLoadScrollView(
|
||||
key: lazyLoadScrollViewKey,
|
||||
onInBetweenOfPage: () async {
|
||||
onInBetweenOfPageCalled = true;
|
||||
},
|
||||
child: ListView(
|
||||
key: childListViewKey,
|
||||
children: List.generate(
|
||||
12,
|
||||
(index) => Container(
|
||||
height: 100,
|
||||
child: Text('Item #$index'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: lazyLoadScrollView,
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget);
|
||||
expect(find.byKey(childListViewKey), findsOneWidget);
|
||||
expect(onInBetweenOfPageCalled, isFalse);
|
||||
|
||||
final gesture = await tester.createGesture();
|
||||
|
||||
await gesture.down(const Offset(100.0, 100.0));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
await gesture.moveBy(const Offset(-200.0, -200.0));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(onInBetweenOfPageCalled, isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should call onStartOfPage if child scrollView '
|
||||
'tends to reach the start',
|
||||
(tester) async {
|
||||
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
|
||||
const childListViewKey = Key('childListView');
|
||||
|
||||
bool onStartOfPageCalled = false;
|
||||
|
||||
final lazyLoadScrollView = LazyLoadScrollView(
|
||||
key: lazyLoadScrollViewKey,
|
||||
onStartOfPage: () async {
|
||||
onStartOfPageCalled = true;
|
||||
},
|
||||
child: ListView(
|
||||
key: childListViewKey,
|
||||
children: List.generate(
|
||||
12,
|
||||
(index) => Container(
|
||||
height: 100,
|
||||
child: Text('Item #$index'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: lazyLoadScrollView,
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget);
|
||||
expect(find.byKey(childListViewKey), findsOneWidget);
|
||||
expect(onStartOfPageCalled, isFalse);
|
||||
|
||||
final gesture = await tester.createGesture();
|
||||
|
||||
await gesture.down(const Offset(100.0, 100.0));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
await gesture.moveBy(const Offset(-200.0, -200.0));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
await gesture.moveBy(const Offset(201.0, 201.0));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(onStartOfPageCalled, isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should call onEndOfPage if child scrollView '
|
||||
'tends to reach the end',
|
||||
(tester) async {
|
||||
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
|
||||
const childListViewKey = Key('childListView');
|
||||
|
||||
bool onEndOfPageCalled = false;
|
||||
|
||||
final lazyLoadScrollView = LazyLoadScrollView(
|
||||
key: lazyLoadScrollViewKey,
|
||||
onEndOfPage: () async {
|
||||
onEndOfPageCalled = true;
|
||||
},
|
||||
child: ListView(
|
||||
key: childListViewKey,
|
||||
children: List.generate(
|
||||
12,
|
||||
(index) => Container(
|
||||
height: 100,
|
||||
child: Text('Item #$index'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: lazyLoadScrollView,
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget);
|
||||
expect(find.byKey(childListViewKey), findsOneWidget);
|
||||
expect(onEndOfPageCalled, isFalse);
|
||||
|
||||
final gesture = await tester.createGesture();
|
||||
|
||||
await gesture.down(const Offset(100.0, 100.0));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
await gesture.moveBy(const Offset(-601.0, -601.0));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(onEndOfPageCalled, isTrue);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
Matcher isSameChannelAs(Channel targetChannel) =>
|
||||
_IsSameChannelAs(targetChannel: targetChannel);
|
||||
|
||||
class _IsSameChannelAs extends Matcher {
|
||||
const _IsSameChannelAs({
|
||||
@required this.targetChannel,
|
||||
}) : assert(targetChannel != null, '');
|
||||
|
||||
final Channel targetChannel;
|
||||
|
||||
@override
|
||||
bool matches(covariant Channel channel, Map matchState) =>
|
||||
channel.cid == targetChannel.cid;
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same channel as $targetChannel');
|
||||
}
|
||||
|
||||
Matcher isSameChannelListAs(List<Channel> targetChannelList) =>
|
||||
_IsSameChannelListAs(targetChannelList: targetChannelList);
|
||||
|
||||
class _IsSameChannelListAs extends Matcher {
|
||||
const _IsSameChannelListAs({
|
||||
@required this.targetChannelList,
|
||||
}) : assert(targetChannelList != null, '');
|
||||
|
||||
final List<Channel> targetChannelList;
|
||||
|
||||
@override
|
||||
bool matches(covariant List<Channel> channelList, Map matchState) {
|
||||
bool matches = true;
|
||||
for (var i = 0; i < channelList.length; i++) {
|
||||
final channel = channelList[i];
|
||||
final targetChannel = targetChannelList[i];
|
||||
matches = isSameChannelAs(targetChannel).matches(channel, matchState);
|
||||
if (!matches) break;
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same channelList as $targetChannelList');
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
Matcher isSameMessageResponseAs(GetMessageResponse targetResponse) =>
|
||||
_IsSameMessageResponseAs(targetResponse: targetResponse);
|
||||
|
||||
class _IsSameMessageResponseAs extends Matcher {
|
||||
const _IsSameMessageResponseAs({
|
||||
@required this.targetResponse,
|
||||
}) : assert(targetResponse != null, '');
|
||||
|
||||
final GetMessageResponse targetResponse;
|
||||
|
||||
@override
|
||||
bool matches(covariant GetMessageResponse response, Map matchState) =>
|
||||
response.message.id == targetResponse.message.id &&
|
||||
response.channel.cid == targetResponse.channel.cid;
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same message response as $targetResponse');
|
||||
}
|
||||
|
||||
Matcher isSameMessageResponseListAs(
|
||||
List<GetMessageResponse> targetResponseList) =>
|
||||
_IsSameMessageResponseListAs(targetResponseList: targetResponseList);
|
||||
|
||||
class _IsSameMessageResponseListAs extends Matcher {
|
||||
const _IsSameMessageResponseListAs({
|
||||
@required this.targetResponseList,
|
||||
}) : assert(targetResponseList != null, '');
|
||||
|
||||
final List<GetMessageResponse> targetResponseList;
|
||||
|
||||
@override
|
||||
bool matches(
|
||||
covariant List<GetMessageResponse> responseList, Map matchState) {
|
||||
bool matches = true;
|
||||
for (var i = 0; i < responseList.length; i++) {
|
||||
final response = responseList[i];
|
||||
final targetResponse = targetResponseList[i];
|
||||
matches = isSameMessageResponseAs(targetResponse).matches(
|
||||
response,
|
||||
matchState,
|
||||
);
|
||||
if (!matches) break;
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same userResponseList as $targetResponseList');
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
Matcher isSameMessageAs(Message targetMessage) =>
|
||||
_IsSameMessageAs(targetMessage: targetMessage);
|
||||
|
||||
class _IsSameMessageAs extends Matcher {
|
||||
const _IsSameMessageAs({
|
||||
@required this.targetMessage,
|
||||
}) : assert(targetMessage != null, '');
|
||||
|
||||
final Message targetMessage;
|
||||
|
||||
@override
|
||||
bool matches(covariant Message message, Map matchState) =>
|
||||
message.id == targetMessage.id;
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same message as $targetMessage');
|
||||
}
|
||||
|
||||
Matcher isSameMessageListAs(List<Message> targetMessageList) =>
|
||||
_IsSameMessageListAs(targetMessageList: targetMessageList);
|
||||
|
||||
class _IsSameMessageListAs extends Matcher {
|
||||
const _IsSameMessageListAs({
|
||||
@required this.targetMessageList,
|
||||
}) : assert(targetMessageList != null, '');
|
||||
|
||||
final List<Message> targetMessageList;
|
||||
|
||||
@override
|
||||
bool matches(covariant List<Message> messageList, Map matchState) {
|
||||
bool matches = true;
|
||||
for (var i = 0; i < messageList.length; i++) {
|
||||
final message = messageList[i];
|
||||
final targetMessage = targetMessageList[i];
|
||||
matches = isSameMessageAs(targetMessage).matches(message, matchState);
|
||||
if (!matches) break;
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same messageList as $targetMessageList');
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
Matcher isSameUserAs(User targetUser) => _IsSameUserAs(targetUser: targetUser);
|
||||
|
||||
class _IsSameUserAs extends Matcher {
|
||||
const _IsSameUserAs({
|
||||
@required this.targetUser,
|
||||
}) : assert(targetUser != null, '');
|
||||
|
||||
final User targetUser;
|
||||
|
||||
@override
|
||||
bool matches(covariant User user, Map matchState) => user.id == targetUser.id;
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same user as $targetUser');
|
||||
}
|
||||
|
||||
Matcher isSameUserListAs(List<User> targetUserList) =>
|
||||
_IsSameUserListAs(targetUserList: targetUserList);
|
||||
|
||||
class _IsSameUserListAs extends Matcher {
|
||||
const _IsSameUserListAs({
|
||||
@required this.targetUserList,
|
||||
}) : assert(targetUserList != null, '');
|
||||
|
||||
final List<User> targetUserList;
|
||||
|
||||
@override
|
||||
bool matches(covariant List<User> userList, Map matchState) {
|
||||
bool matches = true;
|
||||
for (var i = 0; i < userList.length; i++) {
|
||||
final user = userList[i];
|
||||
final targetUser = targetUserList[i];
|
||||
matches = isSameUserAs(targetUser).matches(user, matchState);
|
||||
if (!matches) break;
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('is same userList as $targetUserList');
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter_core/src/message_list_core.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
List<Message> _generateMessages({
|
||||
int count = 3,
|
||||
int offset = 0,
|
||||
bool threads = false,
|
||||
}) {
|
||||
final users = List.generate(count, (index) {
|
||||
index = count + offset;
|
||||
return User(id: 'testUserId$index');
|
||||
});
|
||||
final messages = List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return Message(
|
||||
id: 'testMessageId$index',
|
||||
type: 'testType',
|
||||
user: users[index],
|
||||
createdAt: DateTime.now(),
|
||||
shadowed: false,
|
||||
replyCount: index,
|
||||
updatedAt: DateTime.now(),
|
||||
extraData: {'extra_test_field': 'extraTestData'},
|
||||
text: 'Dummy text #$index',
|
||||
pinned: true,
|
||||
pinnedAt: DateTime.now(),
|
||||
pinnedBy: User(id: 'testUserId$index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
final threadMessages = List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return Message(
|
||||
id: 'testThreadMessageId$index',
|
||||
type: 'testType',
|
||||
user: users[index],
|
||||
parentId: messages[0].id,
|
||||
createdAt: DateTime.now(),
|
||||
shadowed: false,
|
||||
replyCount: index,
|
||||
updatedAt: DateTime.now(),
|
||||
extraData: {'extra_test_field': 'extraTestData'},
|
||||
text: 'Dummy text #$index',
|
||||
pinned: true,
|
||||
pinnedAt: DateTime.now(),
|
||||
pinnedBy: User(id: 'testUserId$index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
return threads ? threadMessages : messages;
|
||||
}
|
||||
|
||||
test(
|
||||
'should throw assertion error in case messageListBuilder is null',
|
||||
() {
|
||||
final messageListCore = () => MessageListCore(
|
||||
messageListBuilder: null,
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) =>
|
||||
Offstage(),
|
||||
);
|
||||
expect(messageListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case loadingBuilder is null',
|
||||
() {
|
||||
final messageListCore = () => MessageListCore(
|
||||
messageListBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: null,
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) =>
|
||||
Offstage(),
|
||||
);
|
||||
expect(messageListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case emptyBuilder is null',
|
||||
() {
|
||||
final messageListCore = () => MessageListCore(
|
||||
messageListBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: null,
|
||||
errorWidgetBuilder: (BuildContext context, Object error) =>
|
||||
Offstage(),
|
||||
);
|
||||
expect(messageListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case errorWidgetBuilder is null',
|
||||
() {
|
||||
final messageListCore = () => MessageListCore(
|
||||
messageListBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: null,
|
||||
);
|
||||
expect(messageListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should throw if MessageListCore is used where StreamChannel is not present '
|
||||
'in the widget tree',
|
||||
(tester) async {
|
||||
const messageListCoreKey = Key('messageListCore');
|
||||
final messageListCore = MessageListCore(
|
||||
key: messageListCoreKey,
|
||||
messageListBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(messageListCore);
|
||||
|
||||
expect(find.byKey(messageListCoreKey), findsNothing);
|
||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should render MessageListCore if used with StreamChannel as an ancestor',
|
||||
(tester) async {
|
||||
const messageListCoreKey = Key('messageListCore');
|
||||
final messageListCore = MessageListCore(
|
||||
key: messageListCoreKey,
|
||||
messageListBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.state.isUpToDate).thenReturn(true);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChannel(
|
||||
channel: mockChannel,
|
||||
child: messageListCore,
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(messageListCoreKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should assign paginateData callback to MessageListController if passed',
|
||||
(tester) async {
|
||||
const messageListCoreKey = Key('messageListCore');
|
||||
final controller = MessageListController();
|
||||
final messageListCore = MessageListCore(
|
||||
key: messageListCoreKey,
|
||||
messageListBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
messageListController: controller,
|
||||
);
|
||||
|
||||
expect(controller.paginateData, isNull);
|
||||
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.state.isUpToDate).thenReturn(true);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChannel(
|
||||
channel: mockChannel,
|
||||
child: messageListCore,
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(messageListCoreKey), findsOneWidget);
|
||||
expect(controller.paginateData, isNotNull);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build error widget if messagesStream emits error',
|
||||
(tester) async {
|
||||
const messageListCoreKey = Key('messageListCore');
|
||||
const errorWidgetKey = Key('errorWidget');
|
||||
final messageListCore = MessageListCore(
|
||||
key: messageListCoreKey,
|
||||
messageListBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) => Offstage(
|
||||
key: errorWidgetKey,
|
||||
),
|
||||
);
|
||||
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.state.isUpToDate).thenReturn(true);
|
||||
when(mockChannel.initialized).thenAnswer((_) async => true);
|
||||
|
||||
const error = 'Error! Error! Error!';
|
||||
when(mockChannel.state.messagesStream)
|
||||
.thenAnswer((_) => Stream.error(error));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChannel(
|
||||
channel: mockChannel,
|
||||
child: messageListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(errorWidgetKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build empty widget if the channel is upToDate and '
|
||||
'messagesStream emits empty data',
|
||||
(tester) async {
|
||||
const messageListCoreKey = Key('messageListCore');
|
||||
const emptyWidgetKey = Key('emptyWidget');
|
||||
final messageListCore = MessageListCore(
|
||||
key: messageListCoreKey,
|
||||
messageListBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(key: emptyWidgetKey),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.state.isUpToDate).thenReturn(true);
|
||||
when(mockChannel.initialized).thenAnswer((_) async => true);
|
||||
|
||||
const messages = <Message>[];
|
||||
when(mockChannel.state.messagesStream)
|
||||
.thenAnswer((_) => Stream.value(messages));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChannel(
|
||||
channel: mockChannel,
|
||||
child: messageListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(emptyWidgetKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build list widget if the channel is not upToDate and '
|
||||
'messagesStream emits empty data',
|
||||
(tester) async {
|
||||
const messageListCoreKey = Key('messageListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
final messageListCore = MessageListCore(
|
||||
key: messageListCoreKey,
|
||||
messageListBuilder: (_, __) => Offstage(key: listWidgetKey),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.state.isUpToDate).thenReturn(false);
|
||||
when(mockChannel.initialized).thenAnswer((_) async => true);
|
||||
|
||||
const messages = <Message>[];
|
||||
when(mockChannel.state.messagesStream)
|
||||
.thenAnswer((_) => Stream.value(messages));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChannel(
|
||||
channel: mockChannel,
|
||||
child: messageListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build list widget and save it for future rebuilds '
|
||||
'if messagesStream emits some data',
|
||||
(tester) async {
|
||||
const messageListCoreKey = Key('messageListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
final messageListCore = MessageListCore(
|
||||
key: messageListCoreKey,
|
||||
messageListBuilder: (_, messages) => Container(
|
||||
key: listWidgetKey,
|
||||
child: Text(
|
||||
messages.reversed.map((it) => it.id).join(','),
|
||||
),
|
||||
),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.state.isUpToDate).thenReturn(true);
|
||||
when(mockChannel.initialized).thenAnswer((_) async => true);
|
||||
|
||||
final messages = _generateMessages();
|
||||
when(mockChannel.state.messagesStream)
|
||||
.thenAnswer((_) => Stream.value(messages));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChannel(
|
||||
channel: mockChannel,
|
||||
child: messageListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
expect(find.text(messages.map((it) => it.id).join(',')), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build list widget with thread messages if parentMessage is passed',
|
||||
(tester) async {
|
||||
final messages = _generateMessages(threads: true);
|
||||
final parentMessage = Message(id: messages.first.parentId);
|
||||
const messageListCoreKey = Key('messageListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
final messageListCore = MessageListCore(
|
||||
key: messageListCoreKey,
|
||||
messageListBuilder: (_, messages) => Container(
|
||||
key: listWidgetKey,
|
||||
child: Text(
|
||||
messages.reversed.map((it) => '${it.parentId}-${it.id}').join(','),
|
||||
),
|
||||
),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorWidgetBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
parentMessage: parentMessage,
|
||||
);
|
||||
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.state.isUpToDate).thenReturn(true);
|
||||
when(mockChannel.initialized).thenAnswer((_) async => true);
|
||||
|
||||
final threads = {parentMessage.id: messages};
|
||||
|
||||
when(mockChannel.state.threads).thenReturn(threads);
|
||||
when(mockChannel.state.threadsStream)
|
||||
.thenAnswer((_) => Stream.value(threads));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChannel(
|
||||
channel: mockChannel,
|
||||
child: messageListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
expect(
|
||||
find.text(messages.map((it) => '${it.parentId}-${it.id}').join(',')),
|
||||
findsOneWidget,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter_core/src/message_search_bloc.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
import 'matchers/get_message_response_matcher.dart';
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
List<GetMessageResponse> _generateMessages({
|
||||
int count = 3,
|
||||
int offset = 0,
|
||||
}) {
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return GetMessageResponse()
|
||||
..message = Message(
|
||||
id: 'testId$index',
|
||||
text: 'testTextData$index',
|
||||
)
|
||||
..channel = ChannelModel(
|
||||
cid: 'testCid',
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test(
|
||||
'should throw assertion error if child is null',
|
||||
() async {
|
||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||
final messageSearchBloc = () => MessageSearchBloc(
|
||||
key: messageSearchBlocKey,
|
||||
child: null,
|
||||
);
|
||||
expect(messageSearchBloc, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'messageSearchBlocState.search() should throw if used where '
|
||||
'StreamChat is not present in the widget tree',
|
||||
(tester) async {
|
||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||
const childKey = Key('child');
|
||||
final messageSearchBloc = MessageSearchBloc(
|
||||
key: messageSearchBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(messageSearchBloc);
|
||||
|
||||
expect(find.byKey(messageSearchBlocKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
|
||||
final usersBlocState = tester.state<MessageSearchBlocState>(
|
||||
find.byKey(messageSearchBlocKey),
|
||||
);
|
||||
|
||||
try {
|
||||
await usersBlocState.search();
|
||||
} catch (e) {
|
||||
expect(e, isInstanceOf<Exception>());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'messageSearchBlocState.search() should emit data through usersStream',
|
||||
(tester) async {
|
||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||
const childKey = Key('child');
|
||||
final messageSearchBloc = MessageSearchBloc(
|
||||
key: messageSearchBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: messageSearchBloc,
|
||||
),
|
||||
);
|
||||
final messageSearchBlocState = tester.state<MessageSearchBlocState>(
|
||||
find.byKey(messageSearchBlocKey),
|
||||
);
|
||||
|
||||
final messageResponseList = _generateMessages();
|
||||
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
||||
);
|
||||
|
||||
messageSearchBlocState.search();
|
||||
|
||||
await expectLater(
|
||||
messageSearchBlocState.messagesStream,
|
||||
emits(isSameMessageResponseListAs(messageResponseList)),
|
||||
);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'messageSearchBlocState.messagesStream should emit error '
|
||||
'if client.search() throws',
|
||||
(tester) async {
|
||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||
const childKey = Key('child');
|
||||
final messageSearchBloc = MessageSearchBloc(
|
||||
key: messageSearchBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: messageSearchBloc,
|
||||
),
|
||||
);
|
||||
final messageSearchBlocState = tester.state<MessageSearchBlocState>(
|
||||
find.byKey(messageSearchBlocKey),
|
||||
);
|
||||
|
||||
const error = 'Error! Error! Error!';
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenThrow(error);
|
||||
|
||||
messageSearchBlocState.search();
|
||||
|
||||
await expectLater(
|
||||
messageSearchBlocState.messagesStream,
|
||||
emitsError(error),
|
||||
);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'calling messageSearchBlocState.search() again with an offset '
|
||||
'should emit new data through messagesStream and also emit loading state '
|
||||
'through queryMessagesLoading',
|
||||
(tester) async {
|
||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||
const childKey = Key('child');
|
||||
final messageSearchBloc = MessageSearchBloc(
|
||||
key: messageSearchBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: messageSearchBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final messageSearchBlocState = tester.state<MessageSearchBlocState>(
|
||||
find.byKey(messageSearchBlocKey),
|
||||
);
|
||||
|
||||
final messageResponseList = _generateMessages();
|
||||
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
||||
);
|
||||
|
||||
messageSearchBlocState.search();
|
||||
|
||||
await expectLater(
|
||||
messageSearchBlocState.messagesStream,
|
||||
emits(isSameMessageResponseListAs(messageResponseList)),
|
||||
);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final offset = messageResponseList.length;
|
||||
final paginatedMessageResponseList = _generateMessages(offset: offset);
|
||||
final pagination = PaginationParams(offset: offset);
|
||||
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: pagination,
|
||||
)).thenAnswer(
|
||||
(_) async =>
|
||||
SearchMessagesResponse()..results = paginatedMessageResponseList,
|
||||
);
|
||||
|
||||
messageSearchBlocState.search(pagination: pagination);
|
||||
|
||||
await Future.wait([
|
||||
expectLater(
|
||||
messageSearchBlocState.queryMessagesLoading,
|
||||
emitsInOrder([true, false]),
|
||||
),
|
||||
expectLater(
|
||||
messageSearchBlocState.messagesStream,
|
||||
emits(isSameMessageResponseListAs(
|
||||
messageResponseList + paginatedMessageResponseList,
|
||||
)),
|
||||
),
|
||||
]);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: pagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'calling messageSearchBlocState.search() again with an offset '
|
||||
'should emit error through queryUsersLoading if '
|
||||
'client.search() throws',
|
||||
(tester) async {
|
||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||
const childKey = Key('child');
|
||||
final messageSearchBloc = MessageSearchBloc(
|
||||
key: messageSearchBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: messageSearchBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final messageSearchBlocState = tester.state<MessageSearchBlocState>(
|
||||
find.byKey(messageSearchBlocKey),
|
||||
);
|
||||
|
||||
final messageResponseList = _generateMessages();
|
||||
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
||||
);
|
||||
|
||||
messageSearchBlocState.search();
|
||||
|
||||
await expectLater(
|
||||
messageSearchBlocState.messagesStream,
|
||||
emits(isSameMessageResponseListAs(messageResponseList)),
|
||||
);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
|
||||
final offset = messageResponseList.length;
|
||||
final pagination = PaginationParams(offset: offset);
|
||||
|
||||
const error = 'Error! Error! Error!';
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: pagination,
|
||||
)).thenThrow(error);
|
||||
|
||||
messageSearchBlocState.search(pagination: pagination);
|
||||
|
||||
await expectLater(
|
||||
messageSearchBlocState.queryMessagesLoading,
|
||||
emitsError(error),
|
||||
);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: pagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
import 'package:stream_chat_flutter_core/src/message_search_list_core.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
List<GetMessageResponse> _generateMessages({
|
||||
int count = 3,
|
||||
int offset = 0,
|
||||
}) {
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return GetMessageResponse()
|
||||
..message = Message(
|
||||
id: 'testId$index',
|
||||
text: 'testTextData$index',
|
||||
)
|
||||
..channel = ChannelModel(
|
||||
cid: 'testCid',
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test(
|
||||
'should throw assertion error in case childBuilder is null',
|
||||
() {
|
||||
final messageSearchListCore = () => MessageSearchListCore(
|
||||
childBuilder: null,
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
expect(messageSearchListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case loadingBuilder is null',
|
||||
() {
|
||||
final messageSearchListCore = () => MessageSearchListCore(
|
||||
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
|
||||
loadingBuilder: null,
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
expect(messageSearchListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case emptyBuilder is null',
|
||||
() {
|
||||
final messageSearchListCore = () => MessageSearchListCore(
|
||||
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: null,
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
expect(messageSearchListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case errorBuilder is null',
|
||||
() {
|
||||
final messageSearchListCore = () => MessageSearchListCore(
|
||||
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: null,
|
||||
);
|
||||
expect(messageSearchListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should throw if MessageSearchListCore is used where MessageSearchBloc '
|
||||
'is not present in the widget tree',
|
||||
(tester) async {
|
||||
const messageSearchListCoreKey = Key('messageSearchListCore');
|
||||
final messageSearchListCore = MessageSearchListCore(
|
||||
key: messageSearchListCoreKey,
|
||||
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(messageSearchListCore);
|
||||
|
||||
expect(find.byKey(messageSearchListCoreKey), findsNothing);
|
||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should render MessageSearchListCore if used with '
|
||||
'MessageSearchListCore as an ancestor',
|
||||
(tester) async {
|
||||
const messageSearchListCoreKey = Key('messageSearchListCore');
|
||||
final messageSearchListCore = MessageSearchListCore(
|
||||
key: messageSearchListCoreKey,
|
||||
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: MessageSearchBloc(
|
||||
child: messageSearchListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(messageSearchListCoreKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should assign loadData and paginateData callback to '
|
||||
'UserListController if passed',
|
||||
(tester) async {
|
||||
const messageSearchListCoreKey = Key('messageSearchListCore');
|
||||
final controller = MessageSearchListController();
|
||||
final messageSearchListCore = MessageSearchListCore(
|
||||
key: messageSearchListCoreKey,
|
||||
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
messageSearchListController: controller,
|
||||
);
|
||||
|
||||
expect(controller.loadData, isNull);
|
||||
expect(controller.paginateData, isNull);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: MessageSearchBloc(
|
||||
child: messageSearchListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(messageSearchListCoreKey), findsOneWidget);
|
||||
expect(controller.loadData, isNotNull);
|
||||
expect(controller.paginateData, isNotNull);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build error widget if messageSearchBloc.messagesStream emits error',
|
||||
(tester) async {
|
||||
const messageSearchListCoreKey = Key('messageSearchListCore');
|
||||
const errorWidgetKey = Key('errorWidget');
|
||||
final messageSearchListCore = MessageSearchListCore(
|
||||
key: messageSearchListCoreKey,
|
||||
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(
|
||||
key: errorWidgetKey,
|
||||
),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
const error = 'Error! Error! Error!';
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenThrow(error);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: MessageSearchBloc(
|
||||
child: messageSearchListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(errorWidgetKey), findsOneWidget);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build empty widget if messageSearchBloc.messagesStream '
|
||||
'emits empty data',
|
||||
(tester) async {
|
||||
const messageSearchListCoreKey = Key('messageSearchListCore');
|
||||
const emptyWidgetKey = Key('emptyWidget');
|
||||
final messageSearchListCore = MessageSearchListCore(
|
||||
key: messageSearchListCoreKey,
|
||||
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(key: emptyWidgetKey),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
final messageResponseList = <GetMessageResponse>[];
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: MessageSearchBloc(
|
||||
child: messageSearchListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(emptyWidgetKey), findsOneWidget);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build child widget if usersBlocState.usersStream emits some data',
|
||||
(tester) async {
|
||||
const messageSearchListCoreKey = Key('messageSearchListCore');
|
||||
const childWidgetKey = Key('childWidget');
|
||||
final messageSearchListCore = MessageSearchListCore(
|
||||
key: messageSearchListCoreKey,
|
||||
childBuilder: (List<GetMessageResponse> messages) => Offstage(
|
||||
key: childWidgetKey,
|
||||
),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
final messageResponseList = _generateMessages();
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).thenAnswer(
|
||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: MessageSearchBloc(
|
||||
child: messageSearchListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(childWidgetKey), findsOneWidget);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: anyNamed('paginationParams'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build child widget with paginated data '
|
||||
'on calling channelListCoreState.paginateData',
|
||||
(tester) async {
|
||||
const messageSearchListCoreKey = Key('messageSearchListCore');
|
||||
const childWidgetKey = Key('childWidget');
|
||||
const pagination = PaginationParams();
|
||||
final messageSearchListCore = MessageSearchListCore(
|
||||
key: messageSearchListCoreKey,
|
||||
childBuilder: (List<GetMessageResponse> messages) => Container(
|
||||
key: childWidgetKey,
|
||||
child: Text(
|
||||
messages.map((e) => '${e.channel.cid}-${e.message.id}').join(','),
|
||||
),
|
||||
),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
paginationParams: pagination,
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
final messageResponseList = _generateMessages();
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: pagination,
|
||||
)).thenAnswer(
|
||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChatCore(
|
||||
client: mockClient,
|
||||
child: MessageSearchBloc(
|
||||
child: messageSearchListCore,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(childWidgetKey), findsOneWidget);
|
||||
expect(
|
||||
find.text(
|
||||
messageResponseList
|
||||
.map((e) => '${e.channel.cid}-${e.message.id}')
|
||||
.join(','),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: pagination,
|
||||
)).called(1);
|
||||
|
||||
final messageSearchListCoreState =
|
||||
tester.state<MessageSearchListCoreState>(
|
||||
find.byKey(messageSearchListCoreKey),
|
||||
);
|
||||
|
||||
final offset = messageResponseList.length;
|
||||
final paginatedMessageResponseList = _generateMessages(offset: offset);
|
||||
final updatedPagination = pagination.copyWith(offset: offset);
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: updatedPagination,
|
||||
)).thenAnswer(
|
||||
(_) async =>
|
||||
SearchMessagesResponse()..results = paginatedMessageResponseList,
|
||||
);
|
||||
|
||||
await messageSearchListCoreState.paginateData();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(childWidgetKey), findsOneWidget);
|
||||
expect(
|
||||
find.text([
|
||||
...messageResponseList,
|
||||
...paginatedMessageResponseList,
|
||||
].map((e) => '${e.channel.cid}-${e.message.id}').join(',')),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: updatedPagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should rebuild MessageSearchListCore with updated widget data '
|
||||
'on calling setState()',
|
||||
(tester) async {
|
||||
const pagination = PaginationParams();
|
||||
|
||||
StateSetter _stateSetter;
|
||||
int limit = pagination.limit;
|
||||
|
||||
const messageSearchListCoreKey = Key('messageSearchListCore');
|
||||
const childWidgetKey = Key('childWidget');
|
||||
MessageSearchListCore messageSearchListCoreBuilder(int limit) =>
|
||||
MessageSearchListCore(
|
||||
key: messageSearchListCoreKey,
|
||||
childBuilder: (List<GetMessageResponse> messages) => Container(
|
||||
key: childWidgetKey,
|
||||
child: Text(
|
||||
messages
|
||||
.map((e) => '${e.channel.cid}-${e.message.id}')
|
||||
.join(','),
|
||||
),
|
||||
),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (BuildContext context, Object error) => Offstage(),
|
||||
paginationParams: pagination.copyWith(limit: limit),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
final messageResponseList = _generateMessages();
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: pagination,
|
||||
)).thenAnswer(
|
||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChatCore(
|
||||
client: mockClient,
|
||||
child: MessageSearchBloc(
|
||||
child: StatefulBuilder(builder: (context, stateSetter) {
|
||||
// Assigning stateSetter for rebuilding UserListCore
|
||||
_stateSetter = stateSetter;
|
||||
return messageSearchListCoreBuilder(limit);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(childWidgetKey), findsOneWidget);
|
||||
expect(
|
||||
find.text(
|
||||
messageResponseList
|
||||
.map((e) => '${e.channel.cid}-${e.message.id}')
|
||||
.join(','),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: pagination,
|
||||
)).called(1);
|
||||
|
||||
// Rebuilding MessageSearchListCore with new pagination limit
|
||||
_stateSetter(() => limit = 6);
|
||||
|
||||
final updatedMessageResponseList = _generateMessages(count: limit);
|
||||
final updatedPagination = pagination.copyWith(limit: limit);
|
||||
when(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: updatedPagination,
|
||||
)).thenAnswer(
|
||||
(_) async =>
|
||||
SearchMessagesResponse()..results = updatedMessageResponseList,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(childWidgetKey), findsOneWidget);
|
||||
expect(
|
||||
find.text(updatedMessageResponseList
|
||||
.map((e) => '${e.channel.cid}-${e.message.id}')
|
||||
.join(',')),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
verify(mockClient.search(
|
||||
any,
|
||||
query: anyNamed('query'),
|
||||
sort: anyNamed('sort'),
|
||||
messageFilters: anyNamed('messageFilters'),
|
||||
paginationParams: updatedPagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,39 @@
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
|
||||
class MockClient extends Mock implements StreamChatClient {}
|
||||
class MockLogger extends Mock implements Logger {}
|
||||
|
||||
class MockClientState extends Mock implements ClientState {}
|
||||
class MockClient extends Mock implements StreamChatClient {
|
||||
final Logger logger = MockLogger();
|
||||
|
||||
class MockChannel extends Mock implements Channel {}
|
||||
ClientState _state;
|
||||
|
||||
@override
|
||||
ClientState get state => _state ??= MockClientState();
|
||||
}
|
||||
|
||||
class MockClientState extends Mock implements ClientState {
|
||||
OwnUser _user;
|
||||
|
||||
@override
|
||||
OwnUser get user => _user ??= OwnUser(
|
||||
id: 'testUserId',
|
||||
role: 'admin',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
class MockChannel extends Mock implements Channel {
|
||||
ChannelClientState _state;
|
||||
|
||||
@override
|
||||
ChannelClientState get state => _state ??= MockChannelState();
|
||||
|
||||
StreamChatClient _client;
|
||||
|
||||
@override
|
||||
StreamChatClient get client => _client ??= MockClient();
|
||||
}
|
||||
|
||||
class MockChannelState extends Mock implements ChannelClientState {}
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
List<Message> _generateMessages({
|
||||
int count = 3,
|
||||
int offset = 0,
|
||||
bool threads = false,
|
||||
}) {
|
||||
final users = List.generate(count, (index) {
|
||||
index = count + offset;
|
||||
return User(id: 'testUserId$index');
|
||||
});
|
||||
final messages = List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return Message(
|
||||
id: 'testMessageId$index',
|
||||
type: 'testType',
|
||||
user: users[index],
|
||||
createdAt: DateTime.now(),
|
||||
shadowed: false,
|
||||
replyCount: index,
|
||||
updatedAt: DateTime.now(),
|
||||
extraData: {'extra_test_field': 'extraTestData'},
|
||||
text: 'Dummy text #$index',
|
||||
pinned: true,
|
||||
pinnedAt: DateTime.now(),
|
||||
pinnedBy: User(id: 'testUserId$index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
final threadMessages = List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return Message(
|
||||
id: 'testThreadMessageId$index',
|
||||
type: 'testType',
|
||||
user: users[index],
|
||||
parentId: messages[0].id,
|
||||
createdAt: DateTime.now(),
|
||||
shadowed: false,
|
||||
replyCount: index,
|
||||
updatedAt: DateTime.now(),
|
||||
extraData: {'extra_test_field': 'extraTestData'},
|
||||
text: 'Dummy text #$index',
|
||||
pinned: true,
|
||||
pinnedAt: DateTime.now(),
|
||||
pinnedBy: User(id: 'testUserId$index'),
|
||||
);
|
||||
},
|
||||
);
|
||||
return threads ? threadMessages : messages;
|
||||
}
|
||||
|
||||
test(
|
||||
'should throw assertion error if child is null',
|
||||
() async {
|
||||
final mockChannel = MockChannel();
|
||||
const streamChannelKey = Key('streamChannel');
|
||||
final streamChannel = () => StreamChannel(
|
||||
key: streamChannelKey,
|
||||
channel: mockChannel,
|
||||
child: null,
|
||||
);
|
||||
expect(streamChannel, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error if channel is null',
|
||||
() async {
|
||||
const streamChannelKey = Key('streamChannel');
|
||||
final streamChannel = () => StreamChannel(
|
||||
key: streamChannelKey,
|
||||
child: Offstage(),
|
||||
channel: null,
|
||||
);
|
||||
expect(streamChannel, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should render StreamChannel if both channel and child is provided',
|
||||
(tester) async {
|
||||
final mockChannel = MockChannel();
|
||||
const streamChannelKey = Key('streamChannel');
|
||||
const childKey = Key('childKey');
|
||||
final streamChannel = StreamChannel(
|
||||
key: streamChannelKey,
|
||||
channel: mockChannel,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(streamChannel);
|
||||
|
||||
expect(find.byKey(streamChannelKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build error widget if channel.initialized throws',
|
||||
(tester) async {
|
||||
final mockChannel = MockChannel();
|
||||
const streamChannelKey = Key('streamChannel');
|
||||
const childKey = Key('childKey');
|
||||
final streamChannel = StreamChannel(
|
||||
key: streamChannelKey,
|
||||
channel: mockChannel,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final errorMessage = 'Error! Error! Error!';
|
||||
final error = DioError(type: DioErrorType.RESPONSE, error: errorMessage);
|
||||
when(mockChannel.initialized).thenAnswer((_) => Future.error(error));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: streamChannel,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text(errorMessage), findsOneWidget);
|
||||
|
||||
verify(mockChannel.initialized).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should show circular progress indicator showLoading is true '
|
||||
'and channel is still not initialized',
|
||||
(tester) async {
|
||||
final mockChannel = MockChannel();
|
||||
const streamChannelKey = Key('streamChannel');
|
||||
const childKey = Key('childKey');
|
||||
final streamChannel = StreamChannel(
|
||||
key: streamChannelKey,
|
||||
channel: mockChannel,
|
||||
child: Offstage(key: childKey),
|
||||
showLoading: true,
|
||||
);
|
||||
|
||||
when(mockChannel.initialized).thenAnswer((_) async => false);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: streamChannel,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(find.byType(CircularProgressIndicator), findsOneWidget);
|
||||
|
||||
verify(mockChannel.initialized).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should preload extra messages if initialMessageId is provided',
|
||||
(tester) async {
|
||||
final mockChannel = MockChannel();
|
||||
const streamChannelKey = Key('streamChannel');
|
||||
const childKey = Key('childKey');
|
||||
final streamChannel = StreamChannel(
|
||||
key: streamChannelKey,
|
||||
channel: mockChannel,
|
||||
child: Offstage(key: childKey),
|
||||
initialMessageId: 'testInitialMessageId',
|
||||
);
|
||||
|
||||
when(mockChannel.initialized).thenAnswer((_) async => true);
|
||||
final messages = _generateMessages();
|
||||
when(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: anyNamed('messagesPagination'),
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).thenAnswer((_) async => ChannelState(messages: messages));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: streamChannel,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
verify(mockChannel.initialized).called(1);
|
||||
verify(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: anyNamed('messagesPagination'),
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).called(
|
||||
2, // Fetching After messages + Fetching Before messages,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should rebuild StreamChannel with updated widget data '
|
||||
'on calling setState()',
|
||||
(tester) async {
|
||||
StateSetter _stateSetter;
|
||||
|
||||
var initialMessageId = 'testInitialMessageId';
|
||||
|
||||
final mockChannel = MockChannel();
|
||||
const streamChannelKey = Key('streamChannel');
|
||||
const childKey = Key('childKey');
|
||||
StreamChannel streamChannelBuilder(String initialMessageId) =>
|
||||
StreamChannel(
|
||||
key: streamChannelKey,
|
||||
channel: mockChannel,
|
||||
child: Offstage(key: childKey),
|
||||
initialMessageId: initialMessageId,
|
||||
);
|
||||
|
||||
final beforePagination = PaginationParams(
|
||||
lessThan: initialMessageId,
|
||||
limit: 20,
|
||||
);
|
||||
|
||||
final afterPagination = PaginationParams(
|
||||
greaterThanOrEqual: initialMessageId,
|
||||
limit: 20,
|
||||
);
|
||||
|
||||
when(mockChannel.initialized).thenAnswer((_) async => true);
|
||||
|
||||
final messages = _generateMessages();
|
||||
|
||||
when(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: beforePagination,
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).thenAnswer((_) async => ChannelState(messages: messages));
|
||||
|
||||
when(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: afterPagination,
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).thenAnswer((_) async => ChannelState(messages: messages));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StatefulBuilder(
|
||||
builder: (context, stateSetter) {
|
||||
// Assigning stateSetter for rebuilding StreamChannel
|
||||
_stateSetter = stateSetter;
|
||||
return streamChannelBuilder(initialMessageId);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
verify(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: beforePagination,
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).called(1);
|
||||
|
||||
verify(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: afterPagination,
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).called(1);
|
||||
|
||||
_stateSetter(() => initialMessageId = 'testInitialMessageId2');
|
||||
|
||||
final updatedBeforePagination = beforePagination.copyWith(
|
||||
lessThan: initialMessageId,
|
||||
);
|
||||
|
||||
final updatedAfterPagination = afterPagination.copyWith(
|
||||
greaterThanOrEqual: initialMessageId,
|
||||
);
|
||||
|
||||
when(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: updatedBeforePagination,
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).thenAnswer((_) async => ChannelState(messages: messages));
|
||||
|
||||
when(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: updatedAfterPagination,
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).thenAnswer((_) async => ChannelState(messages: messages));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
verify(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: updatedBeforePagination,
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).called(1);
|
||||
|
||||
verify(mockChannel.query(
|
||||
options: anyNamed('options'),
|
||||
messagesPagination: updatedAfterPagination,
|
||||
membersPagination: anyNamed('membersPagination'),
|
||||
watchersPagination: anyNamed('watchersPagination'),
|
||||
preferOffline: anyNamed('preferOffline'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,181 +1,289 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fake_async/fake_async.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
class MockShowLocalNotifications extends Mock {
|
||||
class MockOnBackgroundEventReceived extends Mock {
|
||||
void call(Event event);
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'StreamChatCore.of(context) should throw an exception',
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
Builder(
|
||||
builder: (context) {
|
||||
expect(() => StreamChatCore.of(context), throwsException);
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
);
|
||||
test(
|
||||
'should throw assertion error in case client is null',
|
||||
() {
|
||||
final streamChatCore = () => StreamChatCore(
|
||||
client: null,
|
||||
child: Offstage(),
|
||||
);
|
||||
expect(streamChatCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case child is null',
|
||||
() {
|
||||
final mockClient = MockClient();
|
||||
final streamChatCore = () => StreamChatCore(
|
||||
client: mockClient,
|
||||
child: null,
|
||||
);
|
||||
expect(streamChatCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'StreamChatCore.of(context) should return the StreamChatCore ancestor',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: client,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final sc = StreamChatCore.of(context);
|
||||
expect(sc, isNotNull);
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
),
|
||||
'should render StreamChatCore if both client and child is provided',
|
||||
(tester) async {
|
||||
final mockClient = MockClient();
|
||||
const streamChatCoreKey = Key('streamChatCore');
|
||||
const childKey = Key('child');
|
||||
final streamChatCore = StreamChatCore(
|
||||
key: streamChatCoreKey,
|
||||
client: mockClient,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(streamChatCore);
|
||||
|
||||
expect(find.byKey(streamChatCoreKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'StreamChatCore.of(context).client should return the client',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(
|
||||
OwnUser(
|
||||
id: 'test',
|
||||
),
|
||||
);
|
||||
final userStream = Stream<OwnUser>.value(
|
||||
OwnUser(
|
||||
id: 'test',
|
||||
),
|
||||
);
|
||||
when(clientState.userStream).thenAnswer(
|
||||
(_) => userStream,
|
||||
'should render StreamChatCore if both client and child is provided',
|
||||
(tester) async {
|
||||
final mockClient = MockClient();
|
||||
const streamChatCoreKey = Key('streamChatCore');
|
||||
const childKey = Key('child');
|
||||
final streamChatCore = StreamChatCore(
|
||||
key: streamChatCoreKey,
|
||||
client: mockClient,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: client,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final sc = StreamChatCore.of(context);
|
||||
expect(sc.client, client);
|
||||
expect(sc.user, client.state.user);
|
||||
expect(sc.userStream, userStream);
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpWidget(streamChatCore);
|
||||
|
||||
expect(find.byKey(streamChatCoreKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'StreamChatCore should disconnect on background',
|
||||
(WidgetTester tester) async {
|
||||
fakeAsync((_async) {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(
|
||||
OwnUser(
|
||||
id: 'test',
|
||||
),
|
||||
);
|
||||
final showLocalNotificationMock = MockShowLocalNotifications().call;
|
||||
final eventStreamController = StreamController<Event>();
|
||||
when(client.on()).thenAnswer((_) => eventStreamController.stream);
|
||||
'didChangeAppLifecycleState should call client.disconnect() and return '
|
||||
'if onBackgroundEventReceived is null and the widget lifestyle changes to '
|
||||
'AppLifecycleState.paused',
|
||||
(tester) async {
|
||||
final mockClient = MockClient();
|
||||
const streamChatCoreKey = Key('streamChatCore');
|
||||
const childKey = Key('child');
|
||||
final streamChatCore = StreamChatCore(
|
||||
key: streamChatCoreKey,
|
||||
client: mockClient,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
when(client.channel('test', id: 'testid')).thenReturn(channel);
|
||||
await tester.pumpWidget(streamChatCore);
|
||||
|
||||
final scKey = GlobalKey<StreamChatCoreState>();
|
||||
tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
key: scKey,
|
||||
client: client,
|
||||
onBackgroundEventReceived: showLocalNotificationMock,
|
||||
backgroundKeepAlive: const Duration(seconds: 4),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
),
|
||||
expect(find.byKey(streamChatCoreKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
|
||||
when(mockClient.disconnect()).thenAnswer((_) async {
|
||||
return;
|
||||
});
|
||||
|
||||
final streamChatCoreState = tester.state<StreamChatCoreState>(
|
||||
find.byKey(streamChatCoreKey),
|
||||
);
|
||||
|
||||
streamChatCoreState.didChangeAppLifecycleState(AppLifecycleState.paused);
|
||||
|
||||
verify(mockClient.disconnect()).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'didChangeAppLifecycleState should subscribe to client events for '
|
||||
'backgroundKeepAlive duration if onBackgroundEventReceived is provided '
|
||||
'and the widget lifestyle changes to AppLifecycleState.paused',
|
||||
(tester) async {
|
||||
await tester.runAsync(() async {
|
||||
final mockClient = MockClient();
|
||||
final mockOnBackgroundEventReceived = MockOnBackgroundEventReceived();
|
||||
const backgroundKeepAlive = const Duration(seconds: 3);
|
||||
const streamChatCoreKey = Key('streamChatCore');
|
||||
const childKey = Key('child');
|
||||
final streamChatCore = StreamChatCore(
|
||||
key: streamChatCoreKey,
|
||||
client: mockClient,
|
||||
child: Offstage(key: childKey),
|
||||
onBackgroundEventReceived: mockOnBackgroundEventReceived,
|
||||
backgroundKeepAlive: backgroundKeepAlive,
|
||||
);
|
||||
|
||||
final sc = scKey.currentState;
|
||||
sc.didChangeAppLifecycleState(AppLifecycleState.paused);
|
||||
await tester.pumpWidget(streamChatCore);
|
||||
|
||||
_async.elapse(Duration(seconds: 5));
|
||||
expect(find.byKey(streamChatCoreKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
|
||||
verify(client.disconnect()).called(1);
|
||||
eventStreamController.close();
|
||||
final event = Event();
|
||||
when(mockClient.on()).thenAnswer((_) => Stream.value(event));
|
||||
when(mockClient.disconnect()).thenAnswer((_) async {
|
||||
return;
|
||||
});
|
||||
|
||||
final streamChatCoreState = tester.state<StreamChatCoreState>(
|
||||
find.byKey(streamChatCoreKey),
|
||||
);
|
||||
|
||||
streamChatCoreState
|
||||
.didChangeAppLifecycleState(AppLifecycleState.paused);
|
||||
|
||||
await untilCalled(mockOnBackgroundEventReceived.call(event));
|
||||
|
||||
verify(mockOnBackgroundEventReceived.call(event)).called(1);
|
||||
|
||||
await Future.delayed(backgroundKeepAlive);
|
||||
|
||||
verify(mockClient.disconnect()).called(1);
|
||||
verifyNever(mockOnBackgroundEventReceived.call(event));
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'StreamChatCore should handle notifications when on background and connected',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(
|
||||
OwnUser(
|
||||
id: 'test',
|
||||
),
|
||||
);
|
||||
final showLocalNotificationMock = MockShowLocalNotifications().call;
|
||||
final eventStreamController = StreamController<Event>();
|
||||
when(client.on()).thenAnswer((_) => eventStreamController.stream);
|
||||
'didChangeAppLifecycleState should cancel the backgroundKeepAlive timer '
|
||||
'if it is currently running in case the widget lifestyle changes to '
|
||||
'AppLifecycleState.resume',
|
||||
(tester) async {
|
||||
await tester.runAsync(() async {
|
||||
final mockClient = MockClient();
|
||||
final mockOnBackgroundEventReceived = MockOnBackgroundEventReceived();
|
||||
const backgroundKeepAlive = const Duration(seconds: 3);
|
||||
const streamChatCoreKey = Key('streamChatCore');
|
||||
const childKey = Key('child');
|
||||
final streamChatCore = StreamChatCore(
|
||||
key: streamChatCoreKey,
|
||||
client: mockClient,
|
||||
child: Offstage(key: childKey),
|
||||
onBackgroundEventReceived: mockOnBackgroundEventReceived,
|
||||
backgroundKeepAlive: backgroundKeepAlive,
|
||||
);
|
||||
|
||||
when(client.channel('test', id: 'testid')).thenReturn(channel);
|
||||
await tester.pumpWidget(streamChatCore);
|
||||
|
||||
final scKey = GlobalKey<StreamChatCoreState>();
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
key: scKey,
|
||||
client: client,
|
||||
onBackgroundEventReceived: showLocalNotificationMock,
|
||||
backgroundKeepAlive: const Duration(seconds: 4),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(find.byKey(streamChatCoreKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
|
||||
final sc = scKey.currentState;
|
||||
sc.didChangeAppLifecycleState(AppLifecycleState.paused);
|
||||
final event = Event(
|
||||
type: EventType.messageNew,
|
||||
message: Message(text: 'hey'),
|
||||
channelType: 'test',
|
||||
channelId: 'testid',
|
||||
user: User(id: 'other user'),
|
||||
);
|
||||
eventStreamController.add(event);
|
||||
final event = Event();
|
||||
when(mockClient.on()).thenAnswer((_) => Stream.value(event));
|
||||
|
||||
await untilCalled(showLocalNotificationMock(event));
|
||||
final streamChatCoreState = tester.state<StreamChatCoreState>(
|
||||
find.byKey(streamChatCoreKey),
|
||||
);
|
||||
|
||||
verify(showLocalNotificationMock(event)).called(1);
|
||||
eventStreamController.close();
|
||||
streamChatCoreState
|
||||
.didChangeAppLifecycleState(AppLifecycleState.paused);
|
||||
|
||||
await untilCalled(mockOnBackgroundEventReceived.call(event));
|
||||
|
||||
verify(mockOnBackgroundEventReceived.call(event)).called(1);
|
||||
|
||||
streamChatCoreState
|
||||
.didChangeAppLifecycleState(AppLifecycleState.resumed);
|
||||
|
||||
verifyNever(mockOnBackgroundEventReceived.call(event));
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'didChangeAppLifecycleState should call client.connect() '
|
||||
'if the connectionStatus is ConnectionStatus.disconnected in case the '
|
||||
'widget lifestyle changes to AppLifecycleState.resume',
|
||||
(tester) async {
|
||||
await tester.runAsync(() async {
|
||||
final mockClient = MockClient();
|
||||
const streamChatCoreKey = Key('streamChatCore');
|
||||
const childKey = Key('child');
|
||||
final streamChatCore = StreamChatCore(
|
||||
key: streamChatCoreKey,
|
||||
client: mockClient,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(streamChatCore);
|
||||
|
||||
expect(find.byKey(streamChatCoreKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
|
||||
final event = Event();
|
||||
when(mockClient.on()).thenAnswer((_) => Stream.value(event));
|
||||
when(mockClient.connect()).thenAnswer((_) async => event);
|
||||
when(mockClient.wsConnectionStatus)
|
||||
.thenReturn(ConnectionStatus.disconnected);
|
||||
|
||||
final streamChatCoreState = tester.state<StreamChatCoreState>(
|
||||
find.byKey(streamChatCoreKey),
|
||||
);
|
||||
|
||||
streamChatCoreState
|
||||
.didChangeAppLifecycleState(AppLifecycleState.paused);
|
||||
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
|
||||
streamChatCoreState
|
||||
.didChangeAppLifecycleState(AppLifecycleState.resumed);
|
||||
|
||||
verify(mockClient.connect()).called(1);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'streamChatCoreState.userStream should emit all the user events '
|
||||
'provided by client',
|
||||
(tester) async {
|
||||
await tester.runAsync(() async {
|
||||
final userController = StreamController<OwnUser>();
|
||||
|
||||
final mockClient = MockClient();
|
||||
const streamChatCoreKey = Key('streamChatCore');
|
||||
const childKey = Key('child');
|
||||
final streamChatCore = StreamChatCore(
|
||||
key: streamChatCoreKey,
|
||||
client: mockClient,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(streamChatCore);
|
||||
|
||||
expect(find.byKey(streamChatCoreKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
|
||||
when(mockClient.state.userStream)
|
||||
.thenAnswer((_) => userController.stream);
|
||||
|
||||
final streamChatCoreState = tester.state<StreamChatCoreState>(
|
||||
find.byKey(streamChatCoreKey),
|
||||
);
|
||||
|
||||
final ownUser = OwnUser(id: 'testUserId');
|
||||
userController.add(ownUser);
|
||||
|
||||
await expectLater(
|
||||
streamChatCoreState.userStream,
|
||||
emits(ownUser),
|
||||
);
|
||||
|
||||
addTearDown(() {
|
||||
userController.close();
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,572 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter_core/src/user_list_core.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
const alphabets = 'abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
List<User> _generateUsers({
|
||||
int count = 3,
|
||||
int offset = 0,
|
||||
}) {
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return User(
|
||||
id: 'testId$index',
|
||||
role: 'testRole$index',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
lastActive: DateTime.now(),
|
||||
online: true,
|
||||
banned: false,
|
||||
extraData: {
|
||||
'name': '${alphabets[index]}-testName',
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test(
|
||||
'should throw assertion error in case listBuilder is null',
|
||||
() {
|
||||
final userListCore = () => UserListCore(
|
||||
listBuilder: null,
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
);
|
||||
expect(userListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case loadingBuilder is null',
|
||||
() {
|
||||
final userListCore = () => UserListCore(
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: null,
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
);
|
||||
expect(userListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case emptyBuilder is null',
|
||||
() {
|
||||
final userListCore = () => UserListCore(
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: null,
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
);
|
||||
expect(userListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should throw assertion error in case errorBuilder is null',
|
||||
() {
|
||||
final userListCore = () => UserListCore(
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: null,
|
||||
);
|
||||
expect(userListCore, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should throw if UserListCore is used where UsersBloc is not present '
|
||||
'in the widget tree',
|
||||
(tester) async {
|
||||
const userListCoreKey = Key('userListCore');
|
||||
final userListCore = UserListCore(
|
||||
key: userListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(userListCore);
|
||||
|
||||
expect(find.byKey(userListCoreKey), findsNothing);
|
||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should render UserListCore if used with UsersBloc as an ancestor',
|
||||
(tester) async {
|
||||
const userListCoreKey = Key('userListCore');
|
||||
final userListCore = UserListCore(
|
||||
key: userListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: UsersBloc(
|
||||
child: userListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(userListCoreKey), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should assign loadData and paginateData callback to '
|
||||
'UserListController if passed',
|
||||
(tester) async {
|
||||
const userListCoreKey = Key('userListCore');
|
||||
final controller = UserListController();
|
||||
final userListCore = UserListCore(
|
||||
key: userListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
userListController: controller,
|
||||
);
|
||||
|
||||
expect(controller.loadData, isNull);
|
||||
expect(controller.paginateData, isNull);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: UsersBloc(
|
||||
child: userListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(userListCoreKey), findsOneWidget);
|
||||
expect(controller.loadData, isNotNull);
|
||||
expect(controller.paginateData, isNotNull);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build error widget if usersBlocState.usersStream emits error',
|
||||
(tester) async {
|
||||
const userListCoreKey = Key('userListCore');
|
||||
const errorWidgetKey = Key('errorWidget');
|
||||
final userListCore = UserListCore(
|
||||
key: userListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Container(key: errorWidgetKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
const error = 'Error! Error! Error!';
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenThrow(error);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: UsersBloc(
|
||||
child: userListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(errorWidgetKey), findsOneWidget);
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build empty widget if usersBlocState.usersStream emits empty data',
|
||||
(tester) async {
|
||||
const userListCoreKey = Key('userListCore');
|
||||
const emptyWidgetKey = Key('emptyWidget');
|
||||
final userListCore = UserListCore(
|
||||
key: userListCoreKey,
|
||||
listBuilder: (_, __) => Offstage(),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Container(key: emptyWidgetKey),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
const users = <User>[];
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: UsersBloc(
|
||||
child: userListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(emptyWidgetKey), findsOneWidget);
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build list widget if usersBlocState.usersStream emits some data',
|
||||
(tester) async {
|
||||
const userListCoreKey = Key('userListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
final userListCore = UserListCore(
|
||||
key: userListCoreKey,
|
||||
listBuilder: (_, __) => Container(key: listWidgetKey),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
final users = _generateUsers();
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: UsersBloc(
|
||||
child: userListCore,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build list widget with grouped data if groupAlphabetically is true',
|
||||
(tester) async {
|
||||
const userListCoreKey = Key('userListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
final userListCore = UserListCore(
|
||||
key: userListCoreKey,
|
||||
listBuilder: (_, items) => Container(
|
||||
key: listWidgetKey,
|
||||
child: ListView(
|
||||
children: items.map((e) {
|
||||
return Container(
|
||||
key: Key(e.key),
|
||||
child: e.when(
|
||||
headerItem: (heading) => Text(heading),
|
||||
userItem: (user) => Text(user.id),
|
||||
),
|
||||
);
|
||||
}).toList(growable: false),
|
||||
),
|
||||
),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
groupAlphabetically: true,
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
final users = _generateUsers();
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChatCore(
|
||||
client: mockClient,
|
||||
child: UsersBloc(
|
||||
child: userListCore,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
for (final user in users) {
|
||||
expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget);
|
||||
expect(find.byKey(Key('USER-${user.id}')), findsOneWidget);
|
||||
}
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should build list widget with paginated data '
|
||||
'on calling channelListCoreState.paginateData',
|
||||
(tester) async {
|
||||
const userListCoreKey = Key('userListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
const pagination = PaginationParams();
|
||||
final userListCore = UserListCore(
|
||||
key: userListCoreKey,
|
||||
listBuilder: (_, items) => Container(
|
||||
key: listWidgetKey,
|
||||
child: ListView(
|
||||
children: items.map((e) {
|
||||
return Container(
|
||||
key: Key(e.key),
|
||||
child: e.when(
|
||||
headerItem: (heading) => Text(heading),
|
||||
userItem: (user) => Text(user.id),
|
||||
),
|
||||
);
|
||||
}).toList(growable: false),
|
||||
),
|
||||
),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
pagination: pagination,
|
||||
groupAlphabetically: true,
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
final users = _generateUsers();
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChatCore(
|
||||
client: mockClient,
|
||||
child: UsersBloc(
|
||||
child: userListCore,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
for (final user in users) {
|
||||
expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget);
|
||||
expect(find.byKey(Key('USER-${user.id}')), findsOneWidget);
|
||||
}
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
|
||||
final userListCoreState = tester.state<UserListCoreState>(
|
||||
find.byKey(userListCoreKey),
|
||||
);
|
||||
|
||||
final offset = users.length;
|
||||
final paginatedUsers = _generateUsers(offset: offset);
|
||||
final updatedPagination = pagination.copyWith(offset: offset);
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: updatedPagination,
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = paginatedUsers);
|
||||
|
||||
await userListCoreState.paginateData();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
for (final user in users + paginatedUsers) {
|
||||
expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget);
|
||||
expect(find.byKey(Key('USER-${user.id}')), findsOneWidget);
|
||||
}
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: updatedPagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should rebuild UserListCore with updated widget data '
|
||||
'on calling setState()',
|
||||
(tester) async {
|
||||
const pagination = PaginationParams();
|
||||
|
||||
StateSetter _stateSetter;
|
||||
int limit = pagination.limit;
|
||||
|
||||
const userListCoreKey = Key('userListCore');
|
||||
const listWidgetKey = Key('listWidget');
|
||||
UserListCore userListCoreBuilder(int limit) => UserListCore(
|
||||
key: userListCoreKey,
|
||||
listBuilder: (_, items) => Container(
|
||||
key: listWidgetKey,
|
||||
child: ListView(
|
||||
children: items.map((e) {
|
||||
return Container(
|
||||
key: Key(e.key),
|
||||
child: e.when(
|
||||
headerItem: (heading) => Text(heading),
|
||||
userItem: (user) => Text(user.id),
|
||||
),
|
||||
);
|
||||
}).toList(growable: false),
|
||||
),
|
||||
),
|
||||
loadingBuilder: (BuildContext context) => Offstage(),
|
||||
emptyBuilder: (BuildContext context) => Offstage(),
|
||||
errorBuilder: (Object error) => Offstage(),
|
||||
pagination: pagination.copyWith(limit: limit),
|
||||
groupAlphabetically: true,
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
final users = _generateUsers();
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamChatCore(
|
||||
client: mockClient,
|
||||
child: UsersBloc(
|
||||
child: StatefulBuilder(builder: (context, stateSetter) {
|
||||
// Assigning stateSetter for rebuilding UserListCore
|
||||
_stateSetter = stateSetter;
|
||||
return userListCoreBuilder(limit);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(listWidgetKey), findsOneWidget);
|
||||
for (final user in users) {
|
||||
expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget);
|
||||
expect(find.byKey(Key('USER-${user.id}')), findsOneWidget);
|
||||
}
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
|
||||
// Rebuilding UserListCore with new pagination limit
|
||||
_stateSetter(() => limit = 6);
|
||||
|
||||
final updatedUsers = _generateUsers(count: limit);
|
||||
final updatedPagination = pagination.copyWith(limit: limit);
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: updatedPagination,
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = updatedUsers);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
for (final user in updatedUsers) {
|
||||
expect(find.byKey(Key('HEADER-${user.name[0]}')), findsOneWidget);
|
||||
expect(find.byKey(Key('USER-${user.id}')), findsOneWidget);
|
||||
}
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: updatedPagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
|
||||
import 'package:stream_chat_flutter_core/src/users_bloc.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'matchers/users_matcher.dart';
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
List<User> _generateUsers({
|
||||
int count = 3,
|
||||
int offset = 0,
|
||||
}) {
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
index = index + offset;
|
||||
return User(
|
||||
id: 'testId$index',
|
||||
role: 'testRole$index',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
lastActive: DateTime.now(),
|
||||
online: true,
|
||||
banned: false,
|
||||
extraData: {'extra_data_key': 'extraDataValue'},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test(
|
||||
'should throw assertion error if child is null',
|
||||
() async {
|
||||
const usersBlocKey = Key('usersBloc');
|
||||
final usersBloc = () => UsersBloc(
|
||||
key: usersBlocKey,
|
||||
child: null,
|
||||
);
|
||||
expect(usersBloc, throwsA(isA<AssertionError>()));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'usersBlocState.queryUsers() should throw if used where '
|
||||
'StreamChat is not present in the widget tree',
|
||||
(tester) async {
|
||||
const usersBlocKey = Key('usersBloc');
|
||||
const childKey = Key('child');
|
||||
final usersBloc = UsersBloc(
|
||||
key: usersBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(usersBloc);
|
||||
|
||||
expect(find.byKey(usersBlocKey), findsOneWidget);
|
||||
expect(find.byKey(childKey), findsOneWidget);
|
||||
|
||||
final usersBlocState = tester.state<UsersBlocState>(
|
||||
find.byKey(usersBlocKey),
|
||||
);
|
||||
|
||||
try {
|
||||
await usersBlocState.queryUsers();
|
||||
} catch (e) {
|
||||
expect(e, isInstanceOf<Exception>());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'usersBlocState.queryUsers() should emit data through usersStream',
|
||||
(tester) async {
|
||||
const usersBlocKey = Key('usersBloc');
|
||||
const childKey = Key('child');
|
||||
final usersBloc = UsersBloc(
|
||||
key: usersBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: usersBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final usersBlocState = tester.state<UsersBlocState>(
|
||||
find.byKey(usersBlocKey),
|
||||
);
|
||||
|
||||
final users = _generateUsers();
|
||||
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||
|
||||
usersBlocState.queryUsers();
|
||||
|
||||
await expectLater(
|
||||
usersBlocState.usersStream,
|
||||
emits(isSameUserListAs(users)),
|
||||
);
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'usersBlocState.usersStream should emit error '
|
||||
'if client.queryUsers() throws',
|
||||
(tester) async {
|
||||
const usersBlocKey = Key('usersBloc');
|
||||
const childKey = Key('child');
|
||||
final usersBloc = UsersBloc(
|
||||
key: usersBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: usersBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final usersBlocState = tester.state<UsersBlocState>(
|
||||
find.byKey(usersBlocKey),
|
||||
);
|
||||
|
||||
final error = 'Error! Error! Error!';
|
||||
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenThrow(error);
|
||||
|
||||
usersBlocState.queryUsers();
|
||||
|
||||
await expectLater(
|
||||
usersBlocState.usersStream,
|
||||
emitsError(error),
|
||||
);
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'calling usersBlocState.queryUsers() again with an offset '
|
||||
'should emit new data through usersStream and also emit loading state '
|
||||
'through queryUsersLoading',
|
||||
(tester) async {
|
||||
const usersBlocKey = Key('usersBloc');
|
||||
const childKey = Key('child');
|
||||
final usersBloc = UsersBloc(
|
||||
key: usersBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: usersBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final usersBlocState = tester.state<UsersBlocState>(
|
||||
find.byKey(usersBlocKey),
|
||||
);
|
||||
|
||||
final users = _generateUsers();
|
||||
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||
|
||||
usersBlocState.queryUsers();
|
||||
|
||||
await expectLater(
|
||||
usersBlocState.usersStream,
|
||||
emits(isSameUserListAs(users)),
|
||||
);
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
|
||||
final offset = users.length;
|
||||
final paginatedUsers = _generateUsers(offset: offset);
|
||||
final pagination = PaginationParams(offset: offset);
|
||||
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: pagination,
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = paginatedUsers);
|
||||
|
||||
usersBlocState.queryUsers(pagination: pagination);
|
||||
|
||||
await Future.wait([
|
||||
expectLater(
|
||||
usersBlocState.queryUsersLoading,
|
||||
emitsInOrder([true, false]),
|
||||
),
|
||||
expectLater(
|
||||
usersBlocState.usersStream,
|
||||
emits(isSameUserListAs(users + paginatedUsers)),
|
||||
),
|
||||
]);
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: pagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'calling usersBlocState.queryUsers() again with an offset '
|
||||
'should emit error through queryUsersLoading if '
|
||||
'client.queryUsers() throws',
|
||||
(tester) async {
|
||||
const usersBlocKey = Key('usersBloc');
|
||||
const childKey = Key('child');
|
||||
final usersBloc = UsersBloc(
|
||||
key: usersBlocKey,
|
||||
child: Offstage(key: childKey),
|
||||
);
|
||||
|
||||
final mockClient = MockClient();
|
||||
|
||||
await tester.pumpWidget(
|
||||
StreamChatCore(
|
||||
client: mockClient,
|
||||
child: usersBloc,
|
||||
),
|
||||
);
|
||||
|
||||
final usersBlocState = tester.state<UsersBlocState>(
|
||||
find.byKey(usersBlocKey),
|
||||
);
|
||||
|
||||
final users = _generateUsers();
|
||||
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||
|
||||
usersBlocState.queryUsers();
|
||||
|
||||
await expectLater(
|
||||
usersBlocState.usersStream,
|
||||
emits(isSameUserListAs(users)),
|
||||
);
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: anyNamed('pagination'),
|
||||
)).called(1);
|
||||
|
||||
final offset = users.length;
|
||||
final pagination = PaginationParams(offset: offset);
|
||||
|
||||
final error = 'Error! Error! Error!';
|
||||
|
||||
when(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: pagination,
|
||||
)).thenThrow(error);
|
||||
|
||||
usersBlocState.queryUsers(pagination: pagination);
|
||||
|
||||
await expectLater(
|
||||
usersBlocState.queryUsersLoading,
|
||||
emitsError(error),
|
||||
);
|
||||
|
||||
verify(mockClient.queryUsers(
|
||||
filter: anyNamed('filter'),
|
||||
sort: anyNamed('sort'),
|
||||
options: anyNamed('options'),
|
||||
pagination: pagination,
|
||||
)).called(1);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user