From 19c9dd62446e1f7ae4f7518421552b8c57918281 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 29 May 2024 10:15:38 +0700 Subject: [PATCH] TF-2143 Write unit test for composer controller --- .../presentation/composer_controller.dart | 15 +- .../composer_controller_test.dart | 206 ++++++++++++++++++ .../recipient_composer_widget_test.dart | 0 .../calendar_event_extension_test.dart | 48 +++- .../calendar_event_extension_test.dart | 51 ----- 5 files changed, 260 insertions(+), 60 deletions(-) create mode 100644 test/features/composer/presentation/composer_controller_test.dart rename test/features/{features/composer => composer/presentation}/recipient_composer_widget_test.dart (100%) delete mode 100644 test/features/features/email/presentation/extensions/calendar_event_extension_test.dart diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 3aff57db33..6b0b1a4e7b 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -100,7 +100,6 @@ class ComposerController extends BaseController with DragDropFileMixin { final mailboxDashBoardController = Get.find(); final networkConnectionController = Get.find(); - final _dynamicUrlInterceptors = Get.find(); final composerArguments = Rxn(); final isEnableEmailSendButton = false.obs; @@ -186,7 +185,7 @@ class ComposerController extends BaseController with DragDropFileMixin { ButtonState _closeComposerButtonState = ButtonState.enabled; ButtonState _saveToDraftButtonState = ButtonState.enabled; ButtonState _sendButtonState = ButtonState.enabled; - ButtonState _openNewTabButtonState = ButtonState.enabled; + ButtonState openNewTabButtonState = ButtonState.enabled; late Worker uploadInlineImageWorker; late Worker dashboardViewStateWorker; @@ -333,7 +332,7 @@ class ComposerController extends BaseController with DragDropFileMixin { } else if (failure is GetAlwaysReadReceiptSettingFailure) { hasRequestReadReceipt.value = false; } else if (failure is StoreComposedEmailToLocalStorageBrowserFailure) { - _openNewTabButtonState = ButtonState.enabled; + openNewTabButtonState = ButtonState.enabled; } } @@ -1109,7 +1108,7 @@ class ComposerController extends BaseController with DragDropFileMixin { final session = mailboxDashBoardController.sessionCurrent; final accountId = mailboxDashBoardController.accountId.value; if (session != null && accountId != null) { - final uploadUri = session.getUploadUri(accountId, jmapUrl: _dynamicUrlInterceptors.jmapUrl); + final uploadUri = session.getUploadUri(accountId, jmapUrl: dynamicUrlInterceptors.jmapUrl); uploadController.justUploadAttachmentsAction( uploadFiles: pickedFiles, uploadUri: uploadUri, @@ -1659,7 +1658,7 @@ class ComposerController extends BaseController with DragDropFileMixin { void _handleUploadInlineSuccess(SuccessAttachmentUploadState uploadState) { uploadController.clearUploadInlineViewState(); - final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors.jmapUrl); + final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl); final accountId = mailboxDashBoardController.accountId.value; if (baseDownloadUrl != null && accountId != null) { @@ -2202,11 +2201,11 @@ class ComposerController extends BaseController with DragDropFileMixin { } Future onOpenNewTabAction() async { - if (_openNewTabButtonState == ButtonState.disabled) { + if (openNewTabButtonState == ButtonState.disabled) { log('ComposerController::onOpenNewTabAction: OPENING NEW TAB COMPOSER'); return; } - _openNewTabButtonState = ButtonState.disabled; + openNewTabButtonState = ButtonState.disabled; final arguments = composerArguments.value; final session = mailboxDashBoardController.sessionCurrent; @@ -2248,7 +2247,7 @@ class ComposerController extends BaseController with DragDropFileMixin { } Future _handleStoreComposedEmailToLocalStorageBrowserSuccess() async { - _openNewTabButtonState = ButtonState.enabled; + openNewTabButtonState = ButtonState.enabled; await AppUtils.launchLink( RouteUtils.createUrlWebLocationBar(AppRoutes.dashboard).toString() diff --git a/test/features/composer/presentation/composer_controller_test.dart b/test/features/composer/presentation/composer_controller_test.dart new file mode 100644 index 0000000000..099bf9a0d7 --- /dev/null +++ b/test/features/composer/presentation/composer_controller_test.dart @@ -0,0 +1,206 @@ +import 'package:core/data/network/config/dynamic_url_interceptors.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/app_toast.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:core/utils/application_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:tmail_ui_user/features/base/state/button_state.dart'; +import 'package:tmail_ui_user/features/caching/caching_manager.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/create_new_and_save_email_to_drafts_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/create_new_and_send_email_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/download_image_as_base64_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; +import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_composed_email_to_local_storage_browser_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_controller.dart'; +import 'package:tmail_ui_user/features/server_settings/domain/usecases/get_always_read_receipt_setting_interactor.dart'; +import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; +import 'package:tmail_ui_user/features/upload/domain/usecases/local_image_picker_interactor.dart'; +import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.dart'; +import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; +import 'package:uuid/uuid.dart'; + +import '../../../fixtures/account_fixtures.dart'; +import '../../../fixtures/session_fixtures.dart'; +import 'composer_controller_test.mocks.dart'; + +mockControllerCallback() => InternalFinalCallback(callback: () {}); +const fallbackGenerators = { + #onStart: mockControllerCallback, + #onDelete: mockControllerCallback, +}; + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(fallbackGenerators: fallbackGenerators), + MockSpec(fallbackGenerators: fallbackGenerators), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late ComposerController composerController; + late MockLocalFilePickerInteractor mockLocalFilePickerInteractor; + late MockLocalImagePickerInteractor mockLocalImagePickerInteractor; + late MockGetEmailContentInteractor mockGetEmailContentInteractor; + late MockGetAllIdentitiesInteractor mockGetAllIdentitiesInteractor; + late MockUploadController mockUploadController; + late MockRemoveComposerCacheOnWebInteractor mockRemoveComposerCacheOnWebInteractor; + late MockSaveComposerCacheOnWebInteractor mockSaveComposerCacheOnWebInteractor; + late MockDownloadImageAsBase64Interactor mockDownloadImageAsBase64Interactor; + late MockTransformHtmlEmailContentInteractor mockTransformHtmlEmailContentInteractor; + late MockGetAlwaysReadReceiptSettingInteractor mockGetAlwaysReadReceiptSettingInteractor; + late MockCreateNewAndSendEmailInteractor mockCreateNewAndSendEmailInteractor; + late MockCreateNewAndSaveEmailToDraftsInteractor mockCreateNewAndSaveEmailToDraftsInteractor; + late MockStoreComposedEmailToLocalStorageBrowserInteractor mockStoreComposedEmailToLocalStorageBrowserInteractor; + + late MockNetworkConnectionController mockNetworkConnectionController; + late MockMailboxDashBoardController mockMailboxDashBoardController; + + late MockCachingManager mockCachingManager; + late MockLanguageCacheManager mockLanguageCacheManager; + late MockAuthorizationInterceptors mockAuthorizationInterceptors; + late MockDynamicUrlInterceptors mockDynamicUrlInterceptors; + late MockDeleteCredentialInteractor mockDeleteCredentialInteractor; + late MockLogoutOidcInteractor mockLogoutOidcInteractor; + late MockDeleteAuthorityOidcInteractor mockDeleteAuthorityOidcInteractor; + late MockAppToast mockAppToast; + late MockImagePaths mockImagePaths; + late MockResponsiveUtils mockResponsiveUtils; + late MockUuid mockUuid; + late MockApplicationManager mockApplicationManager; + + setUpAll(() { + mockLocalFilePickerInteractor = MockLocalFilePickerInteractor(); + mockLocalImagePickerInteractor = MockLocalImagePickerInteractor(); + mockGetEmailContentInteractor = MockGetEmailContentInteractor(); + mockGetAllIdentitiesInteractor = MockGetAllIdentitiesInteractor(); + mockUploadController = MockUploadController(); + mockRemoveComposerCacheOnWebInteractor = MockRemoveComposerCacheOnWebInteractor(); + mockSaveComposerCacheOnWebInteractor = MockSaveComposerCacheOnWebInteractor(); + mockDownloadImageAsBase64Interactor = MockDownloadImageAsBase64Interactor(); + mockTransformHtmlEmailContentInteractor = MockTransformHtmlEmailContentInteractor(); + mockGetAlwaysReadReceiptSettingInteractor = MockGetAlwaysReadReceiptSettingInteractor(); + mockCreateNewAndSendEmailInteractor = MockCreateNewAndSendEmailInteractor(); + mockCreateNewAndSaveEmailToDraftsInteractor = MockCreateNewAndSaveEmailToDraftsInteractor(); + mockStoreComposedEmailToLocalStorageBrowserInteractor = MockStoreComposedEmailToLocalStorageBrowserInteractor(); + + mockNetworkConnectionController = MockNetworkConnectionController(); + mockMailboxDashBoardController = MockMailboxDashBoardController(); + + // mock base controller + mockCachingManager = MockCachingManager(); + mockLanguageCacheManager = MockLanguageCacheManager(); + mockAuthorizationInterceptors = MockAuthorizationInterceptors(); + mockDynamicUrlInterceptors = MockDynamicUrlInterceptors(); + mockDeleteCredentialInteractor = MockDeleteCredentialInteractor(); + mockLogoutOidcInteractor = MockLogoutOidcInteractor(); + mockDeleteAuthorityOidcInteractor = MockDeleteAuthorityOidcInteractor(); + mockAppToast = MockAppToast(); + mockImagePaths = MockImagePaths(); + mockResponsiveUtils = MockResponsiveUtils(); + mockUuid = MockUuid(); + mockApplicationManager = MockApplicationManager(); + + Get.put(mockNetworkConnectionController); + Get.put(mockMailboxDashBoardController); + + Get.put(mockCachingManager); + Get.put(mockLanguageCacheManager); + Get.put(mockAuthorizationInterceptors); + Get.put( + mockAuthorizationInterceptors, + tag: BindingTag.isolateTag, + ); + Get.put(mockDynamicUrlInterceptors); + Get.put(mockDeleteCredentialInteractor); + Get.put(mockLogoutOidcInteractor); + Get.put(mockDeleteAuthorityOidcInteractor); + Get.put(mockAppToast); + Get.put(mockImagePaths); + Get.put(mockResponsiveUtils); + Get.put(mockUuid); + Get.put(mockApplicationManager); + + Get.testMode = true; + + composerController = ComposerController( + mockLocalFilePickerInteractor, + mockLocalImagePickerInteractor, + mockGetEmailContentInteractor, + mockGetAllIdentitiesInteractor, + mockUploadController, + mockRemoveComposerCacheOnWebInteractor, + mockSaveComposerCacheOnWebInteractor, + mockDownloadImageAsBase64Interactor, + mockTransformHtmlEmailContentInteractor, + mockGetAlwaysReadReceiptSettingInteractor, + mockCreateNewAndSendEmailInteractor, + mockCreateNewAndSaveEmailToDraftsInteractor, + mockStoreComposedEmailToLocalStorageBrowserInteractor, + ); + }); + + group("ComposerController::onOpenNewTabAction test", () { + test( + 'WHEN onOpenNewTabAction invoked\n' + 'THEN _storeComposedEmailToLocalStorageBrowserInteractor should be executed', + () async { + // Arrange + when(mockMailboxDashBoardController.accountId).thenReturn(Rxn(AccountFixtures.aliceAccountId)); + when(mockMailboxDashBoardController.sessionCurrent).thenReturn(SessionFixtures.aliceSession); + + composerController.composerArguments.value = ComposerArguments(); + composerController.openNewTabButtonState = ButtonState.enabled; + + // Act + composerController.onOpenNewTabAction(); + + await untilCalled(mockStoreComposedEmailToLocalStorageBrowserInteractor.execute(any)); + + // Assert + verify(mockStoreComposedEmailToLocalStorageBrowserInteractor.execute(any)).called(1); + }); + }); + + tearDown(() { + Get.deleteAll(); + }); +} \ No newline at end of file diff --git a/test/features/features/composer/recipient_composer_widget_test.dart b/test/features/composer/presentation/recipient_composer_widget_test.dart similarity index 100% rename from test/features/features/composer/recipient_composer_widget_test.dart rename to test/features/composer/presentation/recipient_composer_widget_test.dart diff --git a/test/features/email/presentation/extensions/calendar_event_extension_test.dart b/test/features/email/presentation/extensions/calendar_event_extension_test.dart index bd60379076..79325b2daf 100644 --- a/test/features/email/presentation/extensions/calendar_event_extension_test.dart +++ b/test/features/email/presentation/extensions/calendar_event_extension_test.dart @@ -1,10 +1,12 @@ +import 'package:date_format/date_format.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:date_format/date_format.dart' as date_format; +import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_event_extension.dart'; void main() { - group('calendar event extension test', () { + group('CalendarEventExtension::formatDateTime test', () { final dateTime = DateTime(2021, 10, 10, 10, 30, 00, 00, 00); const locale = date_format.EnglishDateLocale(); @@ -27,4 +29,48 @@ void main() { expect(formattedTime, expectedFormattedTime); }); }); + + group('CalendarEventExtension::getDateTimeEvent test', () { + test('dateTimeEventAsString should return string with format start date - end date (timezone offset) and date formatted as DD, MM dd, YYYY for all-day event for many days', () { + const expectedFormattedDateString = 'Sunday, October 10, 2021 - Sunday, October 24, 2021 (GMT+0)'; + + final startDate = DateTime(2021, 10, 10, 00, 00, 00, 00, 00); + final endDate = DateTime(2021, 10, 25, 00, 00, 00, 00, 00); + + final calendarEvent = CalendarEvent( + startDate: startDate, + endDate: endDate, + startUtcDate: UTCDate(startDate), + endUtcDate: UTCDate(endDate), + ); + + final formattedDateString = calendarEvent.getDateTimeEvent( + timeZone: 'GMT+0', + dateLocale: const EnglishDateLocale() + ); + + expect(formattedDateString, expectedFormattedDateString); + }); + + test('dateTimeEventAsString should return string with format date (timezone offset) and date formatted as DD, MM dd, YYYY for all-day event for one day', () { + const expectedFormattedDateString = 'Sunday, October 10, 2021 (GMT+0)'; + + final startDate = DateTime(2021, 10, 10, 00, 00, 00, 00, 00); + final endDate = DateTime(2021, 10, 11, 00, 00, 00, 00, 00); + + final calendarEvent = CalendarEvent( + startDate: startDate, + endDate: endDate, + startUtcDate: UTCDate(startDate), + endUtcDate: UTCDate(endDate), + ); + + final formattedDateString = calendarEvent.getDateTimeEvent( + timeZone: 'GMT+0', + dateLocale: const EnglishDateLocale() + ); + + expect(formattedDateString, expectedFormattedDateString); + }); + }); } \ No newline at end of file diff --git a/test/features/features/email/presentation/extensions/calendar_event_extension_test.dart b/test/features/features/email/presentation/extensions/calendar_event_extension_test.dart deleted file mode 100644 index 193827bd3e..0000000000 --- a/test/features/features/email/presentation/extensions/calendar_event_extension_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:date_format/date_format.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:jmap_dart_client/jmap/core/utc_date.dart'; -import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart'; -import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_event_extension.dart'; - -void main() { - group('calendar event extension test', () { - test('dateTimeEventAsString should return string with format start date - end date (timezone offset) and date formatted as DD, MM dd, YYYY for all-day event for many days', () { - const expectedFormattedDateString = 'Sunday, October 10, 2021 - Sunday, October 24, 2021 (GMT+0)'; - - final startDate = DateTime(2021, 10, 10, 00, 00, 00, 00, 00); - final endDate = DateTime(2021, 10, 25, 00, 00, 00, 00, 00); - - final calendarEvent = CalendarEvent( - startDate: startDate, - endDate: endDate, - startUtcDate: UTCDate(startDate), - endUtcDate: UTCDate(endDate), - ); - - final formattedDateString = calendarEvent.getDateTimeEvent( - timeZone: 'GMT+0', - dateLocale: const EnglishDateLocale() - ); - - expect(formattedDateString, expectedFormattedDateString); - }); - - test('dateTimeEventAsString should return string with format date (timezone offset) and date formatted as DD, MM dd, YYYY for all-day event for one day', () { - const expectedFormattedDateString = 'Sunday, October 10, 2021 (GMT+0)'; - - final startDate = DateTime(2021, 10, 10, 00, 00, 00, 00, 00); - final endDate = DateTime(2021, 10, 11, 00, 00, 00, 00, 00); - - final calendarEvent = CalendarEvent( - startDate: startDate, - endDate: endDate, - startUtcDate: UTCDate(startDate), - endUtcDate: UTCDate(endDate), - ); - - final formattedDateString = calendarEvent.getDateTimeEvent( - timeZone: 'GMT+0', - dateLocale: const EnglishDateLocale() - ); - - expect(formattedDateString, expectedFormattedDateString); - }); - }); -} \ No newline at end of file