diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index eb277844c..8b95331f4 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -70,6 +70,7 @@ flutter: uses-material-design: true dev_dependencies: + faker_dart: ^0.2.1 flutter_test: sdk: flutter golden_toolkit: ^0.15.0 diff --git a/packages/stream_chat_flutter/test/src/message_actions_modal/message_actions_modal_test.dart b/packages/stream_chat_flutter/test/src/message_actions_modal/message_actions_modal_test.dart index 05263cab8..f6739c3ae 100644 --- a/packages/stream_chat_flutter/test/src/message_actions_modal/message_actions_modal_test.dart +++ b/packages/stream_chat_flutter/test/src/message_actions_modal/message_actions_modal_test.dart @@ -61,6 +61,7 @@ void main() { expect(find.text('Edit Message'), findsOneWidget); expect(find.text('Delete Message'), findsOneWidget); expect(find.text('Copy Message'), findsOneWidget); + expect(find.text('Mark as Unread'), findsOneWidget); }, ); @@ -852,4 +853,55 @@ void main() { expect(find.text('Something went wrong'), findsOneWidget); }, ); + + testWidgets( + 'tapping on unread message should call client.unread', + (WidgetTester tester) async { + final client = MockClient(); + final clientState = MockClientState(); + final channel = MockChannel(); + + when(() => client.state).thenReturn(clientState); + when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); + + final themeData = ThemeData(); + final streamTheme = StreamChatThemeData.fromTheme(themeData); + + await tester.pumpWidget( + MaterialApp( + builder: (context, child) => StreamChat( + client: client, + streamChatThemeData: streamTheme, + child: child, + ), + theme: themeData, + home: Scaffold( + body: StreamChannel( + showLoading: false, + channel: channel, + child: SizedBox( + child: MessageActionsModal( + messageWidget: const Text('test'), + message: Message( + id: 'testid', + text: 'test', + user: User( + id: 'user-id', + ), + ), + messageTheme: streamTheme.ownMessageTheme, + ), + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.text('Mark as Unread')); + await tester.pumpAndSettle(); + + verify(() => channel.markUnread(any())).called(1); + }, + ); } diff --git a/packages/stream_chat_flutter/test/src/message_list_view/message_list_view_test.dart b/packages/stream_chat_flutter/test/src/message_list_view/message_list_view_test.dart index 7bb4a954b..1e1b1975d 100644 --- a/packages/stream_chat_flutter/test/src/message_list_view/message_list_view_test.dart +++ b/packages/stream_chat_flutter/test/src/message_list_view/message_list_view_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import '../../test_utils/data_generator.dart'; import '../mocks.dart'; void main() { @@ -128,4 +129,107 @@ void main() { findsOneWidget, ); }); + + testWidgets('renders a non empty message list view with unread messages', + (tester) async { + final user = OwnUser(id: 'testid'); + final message = Message( + id: 'message1', + text: 'Hello world!', + user: User( + id: 'testid', + name: 'Test User', + ), + ); + + when(() => channelClientState.read) + .thenReturn([Read(lastRead: DateTime.now(), user: user)]); + + when(() => channelClientState.messagesStream).thenAnswer( + (_) => Stream.value([message]), + ); + when(() => channelClientState.messages).thenReturn([message]); + + const nonEmptyWidgetKey = Key('non_empty_widget'); + await tester.runAsync(() async { + await tester.pumpWidget( + MaterialApp( + home: DefaultAssetBundle( + bundle: rootBundle, + child: StreamChat( + client: client, + streamChatThemeData: StreamChatThemeData.light().copyWith( + messageListViewTheme: const StreamMessageListViewThemeData( + backgroundColor: Colors.grey, + backgroundImage: DecorationImage( + image: AssetImage('images/placeholder.png'), + fit: BoxFit.none, + ), + ), + ), + child: StreamChannel( + channel: channel, + child: const StreamMessageListView( + key: nonEmptyWidgetKey, + ), + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + }); + + expect(find.byType(StreamMessageListView), findsOneWidget); + expect(find.byKey(nonEmptyWidgetKey), findsOneWidget); + }); + + testWidgets('scrolls to bottom when arrow button is pressed', (tester) async { + final own = OwnUser(id: 'ownid'); + final other = User(id: 'otherid'); + final messages = generateConversation(20, users: [own, other]); + + when(() => channelClientState.messagesStream).thenAnswer( + (_) => Stream.value(messages), + ); + when(() => channelClientState.messages).thenReturn(messages); + + const nonEmptyWidgetKey = Key('non_empty_widget'); + await tester.runAsync(() async { + await tester.pumpWidget( + MaterialApp( + home: DefaultAssetBundle( + bundle: rootBundle, + child: StreamChat( + client: client, + streamChatThemeData: StreamChatThemeData.light().copyWith( + messageListViewTheme: const StreamMessageListViewThemeData( + backgroundColor: Colors.grey, + backgroundImage: DecorationImage( + image: AssetImage('images/placeholder.png'), + fit: BoxFit.none, + ), + ), + ), + child: StreamChannel( + channel: channel, + child: const StreamMessageListView( + key: nonEmptyWidgetKey, + initialScrollIndex: 5, + ), + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + }); + + expect(find.byType(FloatingActionButton), findsOneWidget); + + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); + + expect(find.byType(FloatingActionButton), findsNothing); + }); } diff --git a/packages/stream_chat_flutter/test/test_utils/data_generator.dart b/packages/stream_chat_flutter/test/test_utils/data_generator.dart new file mode 100644 index 000000000..5759136ba --- /dev/null +++ b/packages/stream_chat_flutter/test/test_utils/data_generator.dart @@ -0,0 +1,63 @@ +import 'package:faker_dart/faker_dart.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +List generateConversation( + int count, { + List? users, + int? noOfUsers, + int unreadCount = 0, +}) { + assert( + users == null || noOfUsers == null, + 'Only one of users or noOfUsers ' + 'should be provided'); + assert(count > 0, 'Count should be greater than 0'); + assert(count > unreadCount, 'Count should be greater than unreadCount'); + + users ??= generateUsers(noOfUsers!); + + final faker = Faker.instance; + + final messages = []; + for (var i = 0; i < count - unreadCount; i++) { + final user = users[i % users.length]; + messages.add( + Message( + id: faker.datatype.uuid(), + text: faker.lorem.sentence(), + user: user, + createdAt: DateTime.now().subtract(Duration(minutes: i)), + ), + ); + } + + for (var i = 0; i < unreadCount; i++) { + final user = users.where((element) => element is! OwnUser).first; + messages.add( + Message( + id: faker.datatype.uuid(), + text: faker.lorem.sentence(), + user: user, + createdAt: + DateTime.now().subtract(Duration(minutes: i + count - unreadCount)), + ), + ); + } + + return messages; +} + +List generateUsers(int count) { + final faker = Faker.instance; + final users = []; + for (var i = 0; i < count; i++) { + users.add( + User( + id: faker.datatype.uuid(), + name: faker.name.fullName(), + ), + ); + } + + return users; +}