diff --git a/.gitignore b/.gitignore index b21c33414b..2deeb54f7b 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ prime .pub-cache/ .pub/ /build/ +**/*.mocks.dart # Web related docs/build/ diff --git a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_item/chat_details_files_item_view.dart b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_item/chat_details_files_item_view.dart index d9a0a9f446..c2b682dad4 100644 --- a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_item/chat_details_files_item_view.dart +++ b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_item/chat_details_files_item_view.dart @@ -24,7 +24,8 @@ class ChatDetailsFilesView extends StatelessWidget { return ValueListenableBuilder( valueListenable: controller.downloadFileStateNotifier, builder: (context, DownloadPresentationState state, child) { - if (state is DownloadingPresentationState) { + if (state is DownloadingPresentationState || + state is DownloadErrorPresentationState) { return ChatDetailsDownloadingFileTile( mimeType: controller.event.mimeType, fileType: filetype, @@ -37,6 +38,7 @@ class ChatDetailsFilesView extends StatelessWidget { controller.downloadManager .cancelDownload(controller.event.eventId); }, + hasError: state is DownloadErrorPresentationState, ); } else if (state is DownloadedPresentationState) { return ChatDetailsDownloadedFileTile( diff --git a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_item_web/chat_details_files_item_view_web.dart b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_item_web/chat_details_files_item_view_web.dart index f7f66b4d52..406a1b0838 100644 --- a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_item_web/chat_details_files_item_view_web.dart +++ b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_item_web/chat_details_files_item_view_web.dart @@ -22,7 +22,8 @@ class ChatDetailsFilesViewWeb extends StatelessWidget { return ValueListenableBuilder( valueListenable: controller.downloadFileStateNotifier, builder: (context, DownloadPresentationState state, child) { - if (state is DownloadingPresentationState) { + if (state is DownloadingPresentationState || + state is DownloadErrorPresentationState) { return ChatDetailsFileTileRowDownloadingWeb( mimeType: controller.event.mimeType, fileType: filetype, @@ -36,6 +37,7 @@ class ChatDetailsFilesViewWeb extends StatelessWidget { controller.downloadManager .cancelDownload(controller.event.eventId); }, + hasError: state is DownloadErrorPresentationState, ); } diff --git a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_file_downloading_tile.dart b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_file_downloading_tile.dart index e37f764eb9..fdc212d05e 100644 --- a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_file_downloading_tile.dart +++ b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_file_downloading_tile.dart @@ -14,6 +14,7 @@ class ChatDetailsDownloadingFileTile extends StatelessWidget { required this.fileType, required this.sizeString, required this.downloadFileStateNotifier, + this.hasError = false, }); final GestureTapCallback onTap; @@ -22,6 +23,7 @@ class ChatDetailsDownloadingFileTile extends StatelessWidget { final String? fileType; final String? sizeString; final ValueNotifier downloadFileStateNotifier; + final bool hasError; @override Widget build(BuildContext context) { @@ -29,6 +31,7 @@ class ChatDetailsDownloadingFileTile extends StatelessWidget { hoverColor: LinagoraSysColors.material().surfaceVariant, onTap: onTap, child: ChatDetailsFileRowDownloadingWrapper( + hasError: hasError, mimeType: mimeType, fileType: fileType, downloadFileStateNotifier: downloadFileStateNotifier, diff --git a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_file_row_downloading_web.dart b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_file_row_downloading_web.dart index d7e45da4d1..615af0c414 100644 --- a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_file_row_downloading_web.dart +++ b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_file_row_downloading_web.dart @@ -17,6 +17,7 @@ class ChatDetailsFileTileRowDownloadingWeb extends StatelessWidget { required this.sizeString, required this.onTap, required this.downloadFileStateNotifier, + this.hasError = false, }); final GestureTapCallback onTap; @@ -26,6 +27,7 @@ class ChatDetailsFileTileRowDownloadingWeb extends StatelessWidget { final String? fileType; final DateTime sentDate; final ValueNotifier downloadFileStateNotifier; + final bool hasError; @override Widget build(BuildContext context) { @@ -33,6 +35,7 @@ class ChatDetailsFileTileRowDownloadingWeb extends StatelessWidget { hoverColor: LinagoraSysColors.material().surfaceVariant, onTap: onTap, child: ChatDetailsFileRowDownloadingWrapper( + hasError: hasError, mimeType: mimeType, fileType: fileType, downloadFileStateNotifier: downloadFileStateNotifier, diff --git a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_row_downloading_wrapper.dart b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_row_downloading_wrapper.dart index 955d963ec5..577a6a09fa 100644 --- a/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_row_downloading_wrapper.dart +++ b/lib/pages/chat_details/chat_details_page_view/files/chat_details_files_row/chat_details_row_downloading_wrapper.dart @@ -9,6 +9,7 @@ class ChatDetailsFileRowDownloadingWrapper extends StatelessWidget { final String? mimeType; final String? fileType; final ValueNotifier downloadFileStateNotifier; + final bool hasError; const ChatDetailsFileRowDownloadingWrapper({ super.key, @@ -16,6 +17,7 @@ class ChatDetailsFileRowDownloadingWrapper extends StatelessWidget { required this.mimeType, required this.fileType, required this.downloadFileStateNotifier, + this.hasError = false, }); final style = const MessageFileTileStyle(); @@ -50,11 +52,14 @@ class ChatDetailsFileRowDownloadingWrapper extends StatelessWidget { width: style.iconSize, height: style.iconSize, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, + color: style.iconBackgroundColor( + hasError: hasError, + context: context, + ), shape: BoxShape.circle, ), ), - if (downloadProgress != 0) + if (downloadProgress != 0 && !hasError) SizedBox( width: style.circularProgressLoadingSize, height: style.circularProgressLoadingSize, @@ -66,11 +71,18 @@ class ChatDetailsFileRowDownloadingWrapper extends StatelessWidget { Container( width: style.downloadIconSize, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, + color: style.iconBackgroundColor( + hasError: hasError, + context: context, + ), shape: BoxShape.circle, ), child: Icon( - downloadProgress == 0 ? Icons.arrow_downward : Icons.close, + hasError + ? Icons.error_outline + : downloadProgress == 0 + ? Icons.arrow_downward + : Icons.close, key: ValueKey(downloadProgress), color: Theme.of(context).colorScheme.surface, size: style.downloadIconSize, diff --git a/lib/utils/manager/download_manager/download_manager.dart b/lib/utils/manager/download_manager/download_manager.dart index a283163d0a..a03a1dd793 100644 --- a/lib/utils/manager/download_manager/download_manager.dart +++ b/lib/utils/manager/download_manager/download_manager.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dartz/dartz.dart' hide Task; import 'package:dio/dio.dart'; import 'package:fluffychat/app_state/failure.dart'; @@ -11,6 +12,7 @@ import 'package:fluffychat/utils/manager/download_manager/download_file_state.da import 'package:fluffychat/utils/manager/download_manager/downloading_worker_queue.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/download_file_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/download_file_web_extension.dart'; +import 'package:fluffychat/utils/network_connection_service.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/task_queue/task.dart'; import 'package:matrix/matrix.dart'; @@ -28,6 +30,9 @@ class DownloadManager { final Map _eventIdMapDownloadFileInfo = {}; + final NetworkConnectionService _connectivityService = + getIt.get(); + void cancelDownload(String eventId) { final cancelToken = _eventIdMapDownloadFileInfo[eventId]?.cancelToken; if (cancelToken != null) { @@ -132,6 +137,26 @@ class DownloadManager { cancelToken: cancelToken, isFirstPriority: isFirstPriority, ); + + _connectivityService.connectivity.onConnectivityChanged + .listen((connectivity) async { + if (connectivity == ConnectivityResult.none) { + Logs().e( + 'DownloadManager::download(): No internet connectivity', + ); + streamController.add( + Left( + DownloadFileFailureState( + exception: Exception( + 'No internet connectivity', + ), + ), + ), + ); + cancelDownload(event.eventId); + return; + } + }); } void _addTaskToWorkerQueue({ diff --git a/lib/widgets/mixins/download_file_on_mobile_mixin.dart b/lib/widgets/mixins/download_file_on_mobile_mixin.dart index 4a2a438835..31e2d12579 100644 --- a/lib/widgets/mixins/download_file_on_mobile_mixin.dart +++ b/lib/widgets/mixins/download_file_on_mobile_mixin.dart @@ -6,6 +6,7 @@ import 'package:fluffychat/app_state/failure.dart'; import 'package:fluffychat/app_state/success.dart'; import 'package:fluffychat/di/global/get_it_initializer.dart'; import 'package:fluffychat/presentation/model/chat/downloading_state_presentation_model.dart'; +import 'package:fluffychat/utils/exception/downloading_exception.dart'; import 'package:fluffychat/utils/manager/download_manager/download_file_state.dart'; import 'package:fluffychat/utils/manager/storage_directory_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/download_file_extension.dart'; @@ -71,10 +72,13 @@ mixin DownloadFileOnMobileMixin on State { eventId: event.eventId, fileName: event.filename, ); - final file = File(filePath); + updateStateIfFileExists(File(filePath)); + } + + Future updateStateIfFileExists(File file) async { if (await file.exists() && await file.length() == event.getFileSize()) { downloadFileStateNotifier.value = DownloadedPresentationState( - filePath: filePath, + filePath: file.path, ); return; } @@ -89,9 +93,15 @@ mixin DownloadFileOnMobileMixin on State { void setupDownloadingProcess(Either resultEvent) { resultEvent.fold( (failure) { - Logs() - .e('$T::setupDownloadingProcess::onDownloadingProcess(): $failure'); - downloadFileStateNotifier.value = const NotDownloadPresentationState(); + Logs().e('MessageDownloadContent::onDownloadingProcess(): $failure'); + if (failure is DownloadFileFailureState && + failure.exception is CancelDownloadingException) { + downloadFileStateNotifier.value = + const NotDownloadPresentationState(); + } else { + downloadFileStateNotifier.value = + DownloadErrorPresentationState(error: failure); + } streamSubscription?.cancel(); }, (success) { diff --git a/lib/widgets/mixins/download_file_on_web_mixin.dart b/lib/widgets/mixins/download_file_on_web_mixin.dart index 9532d762da..a0ea3b6a9d 100644 --- a/lib/widgets/mixins/download_file_on_web_mixin.dart +++ b/lib/widgets/mixins/download_file_on_web_mixin.dart @@ -5,6 +5,7 @@ import 'package:fluffychat/app_state/failure.dart'; import 'package:fluffychat/app_state/success.dart'; import 'package:fluffychat/di/global/get_it_initializer.dart'; import 'package:fluffychat/presentation/model/chat/downloading_state_presentation_model.dart'; +import 'package:fluffychat/utils/exception/downloading_exception.dart'; import 'package:fluffychat/utils/manager/download_manager/download_file_state.dart'; import 'package:fluffychat/utils/manager/download_manager/download_manager.dart'; import 'package:fluffychat/widgets/twake_app.dart'; @@ -44,8 +45,15 @@ mixin DownloadFileOnWebMixin on State { void setupDownloadingProcess(Either resultEvent) { resultEvent.fold( (failure) { - Logs().e('$T::onDownloadingProcess(): $failure'); - downloadFileStateNotifier.value = const NotDownloadPresentationState(); + Logs().e('MessageDownloadContentWeb::onDownloadingProcess(): $failure'); + if (failure is DownloadFileFailureState && + failure.exception is CancelDownloadingException) { + downloadFileStateNotifier.value = + const NotDownloadPresentationState(); + } else { + downloadFileStateNotifier.value = + DownloadErrorPresentationState(error: failure); + } }, (success) { if (success is DownloadingFileState) { diff --git a/test/mixin/download/download_file_on_mobile_mixin_test.dart b/test/mixin/download/download_file_on_mobile_mixin_test.dart new file mode 100644 index 0000000000..7cdd95386f --- /dev/null +++ b/test/mixin/download/download_file_on_mobile_mixin_test.dart @@ -0,0 +1,243 @@ +import 'dart:io'; + +import 'package:dartz/dartz.dart' hide State, OpenFile, id; +import 'package:fluffychat/di/global/get_it_initializer.dart'; +import 'package:fluffychat/presentation/model/chat/downloading_state_presentation_model.dart'; +import 'package:fluffychat/utils/exception/downloading_exception.dart'; +import 'package:fluffychat/utils/manager/download_manager/download_file_state.dart'; +import 'package:fluffychat/utils/manager/download_manager/download_manager.dart'; +import 'package:fluffychat/widgets/mixins/download_file_on_mobile_mixin.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:matrix/matrix.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'download_file_on_mobile_mixin_test.mocks.dart'; +import '../../utils/shared_mocks.dart'; + +class DummyWidget extends StatefulWidget { + const DummyWidget({required this.event, super.key}); + + final Event event; + + @override + // ignore: no_logic_in_create_state + DummyWidgetState createState() => DummyWidgetState(event); +} + +class DummyWidgetState extends State + with DownloadFileOnMobileMixin { + DummyWidgetState(this.fakeEvent); + + final Event fakeEvent; + + @override + Widget build(BuildContext context) { + return Container(); + } + + @override + Event get event => fakeEvent; +} + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), +]) +void main() { + group('DownloadFileOnMobileMixin: setupDownloadingProcess - ENV: WEB', () { + late DummyWidgetState dummyState; + late Event fakeEvent; + late Room fakeRoom; + + setUpAll(() { + getIt.registerSingleton(MockDownloadManager()); + fakeRoom = MockRoom(); + fakeEvent = MockEvent(fakeRoom); + dummyState = DummyWidgetState(fakeEvent); + }); + + tearDownAll(() { + getIt.reset(); + }); + + test( + 'WHEN the download fails because of an Exception \n' + 'THEN downloadFileStateNotifier value should be DownloadErrorPresentationState \n', + () { + final failure = DownloadFileFailureState(exception: Exception()); + dummyState.setupDownloadingProcess(Left(failure)); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + + test( + 'WHEN the user cancel the download \n' + 'THEN downloadFileStateNotifier value should be NotDownloadPresentationState \n', + () { + final failure = + DownloadFileFailureState(exception: CancelDownloadingException()); + dummyState.setupDownloadingProcess(Left(failure)); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + + test( + 'WHEN download is in progress \n' + 'THEN downloadFileStateNotifier value should be DownloadingPresentationState \n', + () { + const success = DownloadingFileState(receive: 10, total: 100); + dummyState.setupDownloadingProcess(const Right(success)); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + + test( + 'WHEN download is successful \n' + 'THEN downloadFileStateNotifier value should be DownloadedPresentationState \n', + () { + const success = DownloadNativeFileSuccessState(filePath: 'path/to/file'); + dummyState.setupDownloadingProcess(const Right(success)); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + }); + + group("DownloadFileOnMobileMixin: checkFileExistInMemory", () { + setUpAll(() { + getIt.registerSingleton(MockDownloadManager()); + }); + + tearDownAll(() { + getIt.reset(); + }); + + test( + 'WHEN file already exists \n' + 'THEN downloadFileStateNotifier value should be DownloadedPresentationState \n', + () { + final fakeRoom = MockRoom(); + final fakeEvent = MockEvent(fakeRoom); + final dummyState = DummyWidgetState(fakeEvent); + + when(fakeRoom.sendingFilePlaceholders).thenReturn({ + MockEvent.fakeEventId: MatrixFile( + name: fakeFilename, + filePath: "path/to/file", + ), + }); + + expect(dummyState.checkFileExistInMemory(), isTrue); + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + + test( + 'WHEN file do not exists \n' + 'THEN downloadFileStateNotifier value should be NotDownloadPresentationState \n', + () { + final fakeRoom = MockRoom(); + final fakeEvent = MockEvent(fakeRoom); + final dummyState = DummyWidgetState(fakeEvent); + + expect(dummyState.checkFileExistInMemory(), isFalse); + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + }); + + group("updateStateIfFileExists", () { + setUpAll(() { + getIt.registerSingleton(MockDownloadManager()); + }); + + tearDownAll(() { + getIt.reset(); + }); + + test( + 'WHEN file already exists \n' + 'AND file size is the same as the event size \n' + 'THEN downloadFileStateNotifier value should be DownloadedPresentationState \n', + () async { + final fakeRoom = MockRoom(); + final fakeEvent = MockEvent(fakeRoom); + final dummyState = DummyWidgetState(fakeEvent); + + final file = MockFile(); + when(file.exists()).thenAnswer((_) async => true); + when(file.length()).thenAnswer((_) async => 123); + when(fakeEvent.infoMap).thenReturn({'size': 123}); + when(file.path).thenReturn('path/to/file'); + + await dummyState.updateStateIfFileExists(file); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + + test( + 'WHEN file does not exists \n' + 'THEN downloadFileStateNotifier value should be NotDownloadPresentationState \n', + () async { + final fakeRoom = MockRoom(); + final fakeEvent = MockEvent(fakeRoom); + final dummyState = DummyWidgetState(fakeEvent); + + final file = MockFile(); + when(file.exists()).thenAnswer((_) async => false); + when(file.length()).thenAnswer((_) async => 123); + when(fakeEvent.infoMap).thenReturn({'size': 123}); + when(file.path).thenReturn('path/to/file'); + + await dummyState.updateStateIfFileExists(file); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + + test( + 'WHEN file already exists \n' + 'BUT file size is the not same as the event size \n' + 'THEN downloadFileStateNotifier value should be NotDownloadPresentationState \n', + () async { + final fakeRoom = MockRoom(); + final fakeEvent = MockEvent(fakeRoom); + final dummyState = DummyWidgetState(fakeEvent); + + final file = MockFile(); + when(file.exists()).thenAnswer((_) async => true); + when(file.length()).thenAnswer((_) async => 1234); + when(fakeEvent.infoMap).thenReturn({'size': 123}); + when(file.path).thenReturn('path/to/file'); + + await dummyState.updateStateIfFileExists(file); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + }); +} diff --git a/test/mixin/download/download_file_on_web_mixin_test.dart b/test/mixin/download/download_file_on_web_mixin_test.dart new file mode 100644 index 0000000000..7c79ee880b --- /dev/null +++ b/test/mixin/download/download_file_on_web_mixin_test.dart @@ -0,0 +1,125 @@ +import 'dart:io'; + +import 'package:dartz/dartz.dart' hide State, OpenFile, id; +import 'package:fluffychat/di/global/get_it_initializer.dart'; +import 'package:fluffychat/presentation/model/chat/downloading_state_presentation_model.dart'; +import 'package:fluffychat/utils/exception/downloading_exception.dart'; +import 'package:fluffychat/utils/manager/download_manager/download_file_state.dart'; +import 'package:fluffychat/utils/manager/download_manager/download_manager.dart'; +import 'package:fluffychat/widgets/mixins/download_file_on_web_mixin.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:matrix/matrix.dart'; +import 'package:mockito/annotations.dart'; +import '../../utils/shared_mocks.dart'; +import 'download_file_on_web_mixin_test.mocks.dart'; + +class DummyWidget extends StatefulWidget { + const DummyWidget({required this.event, super.key}); + + final Event event; + + @override + // ignore: no_logic_in_create_state + DummyWidgetState createState() => DummyWidgetState(event); +} + +class DummyWidgetState extends State with DownloadFileOnWebMixin { + DummyWidgetState(this.fakeEvent); + + final Event fakeEvent; + + @override + Widget build(BuildContext context) { + return Container(); + } + + @override + Event get event => fakeEvent; + + @override + void handleDownloadMatrixFileSuccessDone({ + required DownloadMatrixFileSuccessState success, + }) {} +} + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), +]) +void main() { + group('DownloadFileOnWebMixin: setupDownloadingProcess', () { + late DummyWidgetState dummyState; + late Event fakeEvent; + late Room fakeRoom; + + setUp(() { + getIt.registerSingleton(MockDownloadManager()); + fakeRoom = MockRoom(); + fakeEvent = MockEvent(fakeRoom); + dummyState = DummyWidgetState(fakeEvent); + }); + + tearDown(() { + getIt.reset(); + }); + + test( + 'WHEN the download fails because of an Exception \n' + 'THEN downloadFileStateNotifier value should be DownloadErrorPresentationState \n', + () { + final failure = DownloadFileFailureState(exception: Exception()); + dummyState.setupDownloadingProcess(Left(failure)); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + + test( + 'WHEN the user cancel the download \n' + 'THEN downloadFileStateNotifier value should be NotDownloadPresentationState \n', + () { + final failure = DownloadFileFailureState( + exception: CancelDownloadingException(), + ); + dummyState.setupDownloadingProcess(Left(failure)); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + + test( + 'WHEN download is in progress \n' + 'THEN downloadFileStateNotifier value should be DownloadingPresentationState \n', + () { + const success = DownloadingFileState(receive: 10, total: 100); + dummyState.setupDownloadingProcess(const Right(success)); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + + test( + 'WHEN download is successful \n' + 'THEN downloadFileStateNotifier value should be NotDownloadPresentationState \n', + () { + TestWidgetsFlutterBinding.ensureInitialized(); + + final success = DownloadMatrixFileSuccessState( + matrixFile: MatrixFile(name: "name"), + ); + dummyState.setupDownloadingProcess(Right(success)); + + expect( + dummyState.downloadFileStateNotifier.value, + isInstanceOf(), + ); + }); + }); +} diff --git a/test/pages/chat/events/message/message_content_builder_mixin_test.dart b/test/pages/chat/events/message/message_content_builder_mixin_test.dart index adc2fdcf03..6fc3106fd1 100644 --- a/test/pages/chat/events/message/message_content_builder_mixin_test.dart +++ b/test/pages/chat/events/message/message_content_builder_mixin_test.dart @@ -16,6 +16,8 @@ import 'package:matrix/matrix.dart'; import 'package:matrix_api_lite/fake_matrix_api.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import '../../../../utils/shared_mocks.dart'; + class MockUpMessageContentBuilder with MessageContentBuilderMixin {} void main() { @@ -29,70 +31,6 @@ void main() { final client = Client('client', httpClient: FakeMatrixApi()); final room = Room(id: '!room:example.abc', client: client); - final fileEvent = Event( - content: { - 'body': 'something-important.doc', - 'filename': 'something-important.doc', - 'info': {'mimetype': 'application/msword', 'size': 46144}, - 'msgtype': 'm.file', - 'url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe', - }, - type: 'm.room.message', - eventId: '\$143273582443PhrSn:example.org', - senderId: '@example:example.org', - originServerTs: DateTime.fromMillisecondsSinceEpoch(1894270481925), - room: room, - ); - final imageEvent = Event( - content: { - 'body': 'filename.jpg', - 'info': {'h': 398, 'mimetype': 'image/jpeg', 'size': 31037, 'w': 394}, - 'msgtype': 'm.image', - 'url': 'mxc://example.org/JWEIFJgwEIhweiWJE', - }, - type: 'm.room.message', - eventId: '\$143273582443PhrSn:example.org', - senderId: '@example:example.org', - originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), - room: room, - ); - final videoEvent = Event( - content: { - 'body': 'Gangnam Style', - 'info': { - 'duration': 2140786, - 'h': 320, - 'mimetype': 'video/mp4', - 'size': 1563685, - 'thumbnail_info': { - 'h': 300, - 'mimetype': 'image/jpeg', - 'size': 46144, - 'w': 300, - }, - 'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe', - 'w': 480, - }, - 'msgtype': 'm.video', - 'url': 'mxc://example.org/a526eYUSFFxlgbQYZmo442', - }, - type: 'm.room.message', - eventId: '\$143273582443PhrSn:example.org', - senderId: '@example:example.org', - originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), - room: room, - ); - final emptyTextEvent = Event( - content: { - 'body': '', - 'msgtype': 'm.text', - }, - type: 'm.room.message', - eventId: '\$143273582443PhrSn:example.org', - senderId: '@example:example.org', - originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), - room: room, - ); group( '[MessageContentBuilderMixin] TEST\n', @@ -184,7 +122,7 @@ void main() { (WidgetTester tester) async { await runTest( tester, - event: fileEvent, + event: EventGenerator.fileEvent(room), maxWidth: messageMaxWidthWeb, ); }, @@ -195,7 +133,7 @@ void main() { (WidgetTester tester) async { await runTest( tester, - event: imageEvent, + event: EventGenerator.imageEvent(room), maxWidth: messageMaxWidthWeb, ); }, @@ -206,7 +144,7 @@ void main() { (WidgetTester tester) async { await runTest( tester, - event: videoEvent, + event: EventGenerator.videoEvent(room), maxWidth: messageMaxWidthWeb, ); }, @@ -219,7 +157,7 @@ void main() { (WidgetTester tester) async { await runTest( tester, - event: emptyTextEvent, + event: EventGenerator.emptyTextEvent(room), maxWidth: messageMaxWidthWeb, ); }, @@ -439,7 +377,7 @@ void main() { (WidgetTester tester) async { await runTest( tester, - event: fileEvent, + event: EventGenerator.fileEvent(room), maxWidth: messageMaxWidthMobile, ); }, @@ -450,7 +388,7 @@ void main() { (WidgetTester tester) async { await runTest( tester, - event: imageEvent, + event: EventGenerator.imageEvent(room), maxWidth: messageMaxWidthMobile, ); }, @@ -461,7 +399,7 @@ void main() { (WidgetTester tester) async { await runTest( tester, - event: videoEvent, + event: EventGenerator.videoEvent(room), maxWidth: messageMaxWidthMobile, ); }, @@ -474,7 +412,7 @@ void main() { (WidgetTester tester) async { await runTest( tester, - event: emptyTextEvent, + event: EventGenerator.emptyTextEvent(room), maxWidth: messageMaxWidthMobile, ); }, diff --git a/test/utils/shared_mocks.dart b/test/utils/shared_mocks.dart new file mode 100644 index 0000000000..5c3a09b6a1 --- /dev/null +++ b/test/utils/shared_mocks.dart @@ -0,0 +1,108 @@ +import 'package:matrix/matrix.dart'; +import 'package:mockito/mockito.dart'; + +const fakeFilename = "fakeFilename"; + +class MockRoom extends Mock implements Room { + @override + bool operator ==(Object other) => (other is Room && other.id == id); + + @override + int get hashCode => Object.hashAll([id]); + + @override + Map get sendingFilePlaceholders => super.noSuchMethod( + Invocation.getter(#sendingFilePlaceholders), + returnValue: {}, + returnValueForMissingStub: {}, + ); +} + +class MockEvent extends Mock implements Event { + MockEvent(this.fakeRoom); + final Room fakeRoom; + static const fakeEventId = "fakeEventId"; + + @override + String get eventId => fakeEventId; + + @override + Room get room => fakeRoom; + + @override + Map get infoMap => super.noSuchMethod( + Invocation.getter(#infoMap), + returnValue: {}, + returnValueForMissingStub: {}, + ); +} + +class EventGenerator { + static Event fileEvent(Room room) => Event( + content: { + 'body': 'something-important.doc', + 'filename': 'something-important.doc', + 'info': {'mimetype': 'application/msword', 'size': 46144}, + 'msgtype': 'm.file', + 'url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe', + }, + type: 'm.room.message', + eventId: '\$143273582443PhrSn:example.org', + senderId: '@example:example.org', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1894270481925), + room: room, + ); + + static Event imageEvent(Room room) => Event( + content: { + 'body': 'filename.jpg', + 'info': {'h': 398, 'mimetype': 'image/jpeg', 'size': 31037, 'w': 394}, + 'msgtype': 'm.image', + 'url': 'mxc://example.org/JWEIFJgwEIhweiWJE', + }, + type: 'm.room.message', + eventId: '\$143273582443PhrSn:example.org', + senderId: '@example:example.org', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + room: room, + ); + + static Event videoEvent(Room room) => Event( + content: { + 'body': 'Gangnam Style', + 'info': { + 'duration': 2140786, + 'h': 320, + 'mimetype': 'video/mp4', + 'size': 1563685, + 'thumbnail_info': { + 'h': 300, + 'mimetype': 'image/jpeg', + 'size': 46144, + 'w': 300, + }, + 'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe', + 'w': 480, + }, + 'msgtype': 'm.video', + 'url': 'mxc://example.org/a526eYUSFFxlgbQYZmo442', + }, + type: 'm.room.message', + eventId: '\$143273582443PhrSn:example.org', + senderId: '@example:example.org', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + room: room, + ); + + static Event emptyTextEvent(Room room) => Event( + content: { + 'body': '', + 'msgtype': 'm.text', + }, + type: 'm.room.message', + eventId: '\$143273582443PhrSn:example.org', + senderId: '@example:example.org', + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + room: room, + ); +}