From 6cbdae882fef5e25a5d3d83efe8f520fbc782829 Mon Sep 17 00:00:00 2001 From: "khaled.njim" Date: Thu, 12 Dec 2024 00:22:20 +0100 Subject: [PATCH] Added widget test for message_avatar_mixin --- lib/pages/chat/events/message/message.dart | 52 +-- .../mixins/message_avatar_mixin.dart | 56 ++++ pubspec.lock | 2 +- test/mixin/message_avatar_mixin_test.dart | 298 ++++++++++++++++++ 4 files changed, 364 insertions(+), 44 deletions(-) create mode 100644 lib/presentation/mixins/message_avatar_mixin.dart create mode 100644 test/mixin/message_avatar_mixin_test.dart diff --git a/lib/pages/chat/events/message/message.dart b/lib/pages/chat/events/message/message.dart index a7560d4ba..cb2b82c3c 100644 --- a/lib/pages/chat/events/message/message.dart +++ b/lib/pages/chat/events/message/message.dart @@ -14,8 +14,8 @@ import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/extension/event_status_custom_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart'; +import 'package:fluffychat/presentation/mixins/message_avatar_mixin.dart'; import 'package:fluffychat/utils/responsive/responsive_utils.dart'; -import 'package:fluffychat/widgets/avatar/avatar.dart'; import 'package:fluffychat/widgets/context_menu/context_menu_action.dart'; import 'package:fluffychat/widgets/swipeable.dart'; import 'package:flutter/material.dart'; @@ -99,7 +99,7 @@ class Message extends StatefulWidget { State createState() => _MessageState(); } -class _MessageState extends State { +class _MessageState extends State with MessageAvatarMixin { InViewState? inViewState; final inviewNotifier = ValueNotifier(false); @@ -187,10 +187,13 @@ class _MessageState extends State { : MainAxisAlignment.start; final rowChildren = [ - _placeHolderWidget( - widget.event.isSameSenderWith(widget.previousEvent), - widget.event.isOwnMessage, - widget.event, + placeHolderWidget( + widget.onAvatarTap, + event: widget.event, + sameSender: widget.event.isSameSenderWith(widget.previousEvent), + ownMessage: widget.event.isOwnMessage, + context: context, + selectMode: widget.selectMode, ), Expanded( child: MessageContentWithTimestampBuilder( @@ -311,43 +314,6 @@ class _MessageState extends State { ); } - bool shouldDisplayAvatar(bool sameSender, bool ownMessage) { - return sameSender && - (!ownMessage || !Message.responsiveUtils.isMobile(context)); - } - - Widget _placeHolderWidget(bool sameSender, bool ownMessage, Event event) { - if (widget.selectMode || - (event.room.isDirectChat && - Message.responsiveUtils.isMobile(context))) { - return const SizedBox(); - } - - if (shouldDisplayAvatar(sameSender, ownMessage)) { - return Padding( - padding: MessageStyle.paddingAvatar, - child: FutureBuilder( - future: event.fetchSenderUser(), - builder: (context, snapshot) { - final user = snapshot.data ?? event.senderFromMemoryOrFallback; - return Avatar( - size: MessageStyle.avatarSize, - fontSize: MessageStyle.fontSize, - mxContent: user.avatarUrl, - name: user.calcDisplayname(), - onTap: () => widget.onAvatarTap!(event), - ); - }, - ), - ); - } - - return const Padding( - padding: MessageStyle.paddingAvatar, - child: SizedBox(width: MessageStyle.avatarSize), - ); - } - Widget _messageSelectedWidget( BuildContext context, Widget child, diff --git a/lib/presentation/mixins/message_avatar_mixin.dart b/lib/presentation/mixins/message_avatar_mixin.dart new file mode 100644 index 000000000..f02b794fa --- /dev/null +++ b/lib/presentation/mixins/message_avatar_mixin.dart @@ -0,0 +1,56 @@ +import 'package:fluffychat/di/global/get_it_initializer.dart'; +import 'package:fluffychat/pages/chat/events/message/message_style.dart'; +import 'package:fluffychat/utils/responsive/responsive_utils.dart'; +import 'package:fluffychat/widgets/avatar/avatar.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; + +mixin MessageAvatarMixin { + ResponsiveUtils responsive = getIt.get(); + + bool _shouldDisplayAvatar( + bool sameSender, + bool ownMessage, + BuildContext context, + ) { + return sameSender && !(ownMessage && responsive.isMobile(context)); + } + + Widget placeHolderWidget( + Function(Event)? onAvatarTap, { + required bool sameSender, + required bool ownMessage, + required Event event, + required BuildContext context, + required bool selectMode, + }) { + if (selectMode || + (event.room.isDirectChat && responsive.isMobile(context))) { + return const SizedBox(); + } + + if (_shouldDisplayAvatar(sameSender, ownMessage, context)) { + return Padding( + padding: MessageStyle.paddingAvatar, + child: FutureBuilder( + future: event.fetchSenderUser(), + builder: (context, snapshot) { + final user = snapshot.data ?? event.senderFromMemoryOrFallback; + return Avatar( + size: MessageStyle.avatarSize, + fontSize: MessageStyle.fontSize, + mxContent: user.avatarUrl, + name: user.calcDisplayname(), + onTap: () => onAvatarTap!(event), + ); + }, + ), + ); + } + + return const Padding( + padding: MessageStyle.paddingAvatar, + child: SizedBox(width: MessageStyle.avatarSize), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 4ef743147..7606d63ac 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1877,7 +1877,7 @@ packages: description: path: "." ref: "twake-supported-0.22.6" - resolved-ref: "04ec6f3b9ece8e64e031cbe5c6e8164dc2ec7f2d" + resolved-ref: "58585a19abc4693d96eab4f8dad9c56bf8699cc1" url: "git@github.com:linagora/matrix-dart-sdk.git" source: git version: "0.22.6" diff --git a/test/mixin/message_avatar_mixin_test.dart b/test/mixin/message_avatar_mixin_test.dart new file mode 100644 index 000000000..7e52e6f31 --- /dev/null +++ b/test/mixin/message_avatar_mixin_test.dart @@ -0,0 +1,298 @@ +// ignore_for_file: depend_on_referenced_packages + +import 'package:fluffychat/config/localizations/localization_service.dart'; +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/presentation/mixins/message_avatar_mixin.dart'; +import 'package:fluffychat/utils/responsive/responsive_utils.dart'; +import 'package:fluffychat/widgets/avatar/avatar.dart'; +import 'package:fluffychat/widgets/theme_builder.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_localized_locales/flutter_localized_locales.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; +import 'package:matrix/matrix.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'message_avatar_mixin_test.mocks.dart'; + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), +]) +class MockMessageAvatarUtils with MessageAvatarMixin {} + +Future main() async { + TestWidgetsFlutterBinding.ensureInitialized(); + late MockMessageAvatarUtils mockMessageAvatarUtils; + late Room room; + late User user; + late Event event; + setUpAll(() { + final getIt = GetIt.instance; + getIt.registerSingleton(ResponsiveUtils()); + mockMessageAvatarUtils = MockMessageAvatarUtils(); + }); + + group('Tests for when the avatar next to a message should be displayed ', () { + setUp(() { + room = MockRoom(); + user = MockUser(); + event = Event( + content: { + 'body': 'Test message', + 'msgtype': 'm.text', + }, + type: 'm.room.message', + eventId: '7365636s6r64300:example.com', + senderId: '@bob:example.com', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + room: room, + ); + }); + Future runTest( + WidgetTester tester, { + required Event event, + required bool selectMode, + required bool sameSender, + required bool ownMessage, + required Size screenSize, + required bool isDirectChat, + }) async { + when(room.requestUser(event.senderId, ignoreErrors: true)) + .thenAnswer((_) async => user); + when(room.unsafeGetUserFromMemoryOrFallback(event.senderId)) + .thenReturn(user); + when(user.avatarUrl).thenReturn(Uri.tryParse("fakeImage")); + when(user.calcDisplayname()).thenReturn('Test'); + when(event.room.isDirectChat).thenReturn(isDirectChat); + Widget? widget; + await tester.pumpWidget( + ThemeBuilder( + builder: (context, themeMode, primaryColor) => MaterialApp( + locale: const Locale('en'), + localizationsDelegates: const [ + LocaleNamesLocalizationsDelegate(), + L10n.delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: LocalizationService.supportedLocales, + theme: TwakeThemes.buildTheme( + context, + Brightness.light, + primaryColor, + ), + builder: (context, child) => MediaQuery( + data: MediaQueryData( + size: screenSize, + ), + child: child!, + ), + home: Scaffold( + body: Builder( + builder: (context) { + widget = mockMessageAvatarUtils.placeHolderWidget( + (_) {}, + sameSender: sameSender, + ownMessage: ownMessage, + event: event, + context: context, + selectMode: selectMode, + ); + return widget!; + }, + ), + ), + ), + ), + ); + + await tester.pumpAndSettle(); + } + + group('Web-sized screens', () { + const webSize = Size(1200, 800); + + testWidgets( + 'Should display Avatar when Own message in group chat', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: false, + sameSender: true, + ownMessage: true, + screenSize: webSize, + isDirectChat: false, + ); + verify(room.requestUser(event.senderId, ignoreErrors: true)) + .called(1); + expect(find.byType(Avatar), findsOneWidget); + }, + ); + + testWidgets( + 'Should display Avatar when Own message in direct chat', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: false, + sameSender: true, + ownMessage: true, + screenSize: webSize, + isDirectChat: true, + ); + verify(room.requestUser(event.senderId, ignoreErrors: true)) + .called(1); + expect(find.byType(Avatar), findsOneWidget); + }, + ); + + testWidgets( + 'Should return Avatar when Not my message in direct chat', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: false, + sameSender: true, + ownMessage: false, + screenSize: webSize, + isDirectChat: true, + ); + verify(room.requestUser(event.senderId, ignoreErrors: true)) + .called(1); + expect(find.byType(Avatar), findsOneWidget); + }, + ); + + testWidgets( + 'Should return Avatar when not my message in group chat', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: false, + sameSender: true, + ownMessage: false, + screenSize: webSize, + isDirectChat: false, + ); + verify(room.requestUser(event.senderId, ignoreErrors: true)) + .called(1); + expect(find.byType(Avatar), findsOneWidget); + }, + ); + + testWidgets( + 'Should return SizedBox when Select mode is active', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: true, + sameSender: true, + ownMessage: false, + screenSize: webSize, + isDirectChat: true, + ); + verifyNever(room.requestUser(event.senderId, ignoreErrors: true)); + expect(find.byType(SizedBox), findsOneWidget); + }, + ); + }); + group('Mobile-sized screens', () { + const mobileSize = Size(400, 800); + + testWidgets( + 'Should return SizedBox when Own message in group chat', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: false, + sameSender: true, + ownMessage: true, + isDirectChat: false, + screenSize: mobileSize, + ); + verifyNever(room.requestUser(event.senderId, ignoreErrors: true)); + expect(find.byType(SizedBox), findsOneWidget); + }, + ); + + testWidgets( + 'Should return SizedBox when Own message in direct chat', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: false, + sameSender: true, + ownMessage: true, + screenSize: mobileSize, + isDirectChat: true, + ); + verifyNever(room.requestUser(event.senderId, ignoreErrors: true)); + expect(find.byType(SizedBox), findsOneWidget); + }, + ); + + testWidgets( + 'Should return SizedBox when Not my message in direct chat', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: false, + sameSender: true, + ownMessage: false, + screenSize: mobileSize, + isDirectChat: true, + ); + verifyNever(room.requestUser(event.senderId, ignoreErrors: true)); + expect(find.byType(SizedBox), findsOneWidget); + }, + ); + + testWidgets( + 'Should return Avatar when Not my message in group chat', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: false, + sameSender: true, + ownMessage: false, + screenSize: mobileSize, + isDirectChat: false, + ); + verify(room.requestUser(event.senderId, ignoreErrors: true)) + .called(1); + expect(find.byType(Avatar), findsOneWidget); + }, + ); + + testWidgets( + 'Should return SizedBox when Select mode is active', + (WidgetTester tester) async { + await runTest( + tester, + event: event, + selectMode: true, + sameSender: true, + ownMessage: false, + screenSize: mobileSize, + isDirectChat: true, + ); + verifyNever(room.requestUser(event.senderId, ignoreErrors: true)); + expect(find.byType(SizedBox), findsOneWidget); + }, + ); + }); + }); +}