diff --git a/lib/features/composer/presentation/extensions/email_action_type_extension.dart b/lib/features/composer/presentation/extensions/email_action_type_extension.dart index 10acb8f79..2b17a837b 100644 --- a/lib/features/composer/presentation/extensions/email_action_type_extension.dart +++ b/lib/features/composer/presentation/extensions/email_action_type_extension.dart @@ -47,7 +47,9 @@ extension EmailActionTypeExtension on EmailActionType { case EmailActionType.moveToMailbox: return AppLocalizations.of(context).movedToFolder(destinationPath ?? ''); case EmailActionType.moveToTrash: - return AppLocalizations.of(context).moved_to_trash; + return destinationPath?.trim().isNotEmpty == true + ? AppLocalizations.of(context).movedToFolder(destinationPath!) + : AppLocalizations.of(context).moved_to_trash; case EmailActionType.moveToSpam: return AppLocalizations.of(context).marked_as_spam; case EmailActionType.unSpam: diff --git a/lib/features/mailbox/domain/exceptions/mailbox_exception.dart b/lib/features/mailbox/domain/exceptions/mailbox_exception.dart index 0d6b89f8d..380b819c8 100644 --- a/lib/features/mailbox/domain/exceptions/mailbox_exception.dart +++ b/lib/features/mailbox/domain/exceptions/mailbox_exception.dart @@ -27,3 +27,17 @@ class CannotMoveAllEmailException extends AppBaseException { @override String get exceptionName => 'CannotMoveAllEmailException'; } + +class NotFoundMailboxOfEmailException extends AppBaseException { + NotFoundMailboxOfEmailException([super.message]); + + @override + String get exceptionName => 'NotFoundMailboxOfEmailException'; +} + +class NotFoundTrashMailboxException extends AppBaseException { + NotFoundTrashMailboxException([super.message]); + + @override + String get exceptionName => 'NotFoundTrashMailboxException'; +} \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mixin/handle_team_mailbox_mixin.dart b/lib/features/mailbox/presentation/mixin/handle_team_mailbox_mixin.dart new file mode 100644 index 000000000..267fcdaf9 --- /dev/null +++ b/lib/features/mailbox/presentation/mixin/handle_team_mailbox_mixin.dart @@ -0,0 +1,55 @@ +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/namespace.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +mixin HandleTeamMailboxMixin { + MailboxTree? get _teamMailboxesTree => + getBinding()?.teamMailboxesTree.value; + + MailboxNode? _findTeamMailboxNodeByNamespaceOnFirstLevel( + Namespace namespace, + ) { + return _teamMailboxesTree?.findNodeOnFirstLevel( + (node) => node.item.namespace == namespace, + ); + } + + PresentationMailbox? _findDefaultMailboxInTeamMailbox({ + required Namespace namespace, + required String mailboxName, + }) { + final teamMailboxNode = + _findTeamMailboxNodeByNamespaceOnFirstLevel(namespace); + if (teamMailboxNode == null) return null; + + final mailboxNode = teamMailboxNode.findNodeOnFirstLevel( + (node) => + node.mailboxNameAsString.toLowerCase() == mailboxName.toLowerCase(), + ); + if (mailboxNode == null) return null; + + return mailboxNode.item; + } + + MailboxId? findDefaultMailboxIdInTeamMailbox({ + required Namespace namespace, + required String mailboxName, + }) { + final teamMailbox = _findDefaultMailboxInTeamMailbox( + namespace: namespace, + mailboxName: mailboxName, + ); + return teamMailbox?.id; + } + + String? getTeamMailboxNodePathWithSeparator({ + required MailboxId mailboxId, + String pathSeparator = '/', + }) { + return _teamMailboxesTree?.getNodePath(mailboxId, pathSeparator); + } +} diff --git a/lib/features/mailbox/presentation/model/mailbox_node.dart b/lib/features/mailbox/presentation/model/mailbox_node.dart index 59250cf4c..0846f9bfd 100644 --- a/lib/features/mailbox/presentation/model/mailbox_node.dart +++ b/lib/features/mailbox/presentation/model/mailbox_node.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; @@ -6,6 +7,7 @@ import 'package:model/mailbox/expand_mode.dart'; import 'package:model/mailbox/mailbox_state.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/select_mode.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; class MailboxNode with EquatableMixin { static final PresentationMailbox _root = PresentationMailbox(MailboxId(Id('root'))); @@ -172,4 +174,7 @@ extension MailboxNodeExtension on MailboxNode { return item.sortOrder!.value.value.compareTo(other.item.sortOrder!.value.value); } + + MailboxNode? findNodeOnFirstLevel(NodeQuery nodeQuery) => + childrenItems?.firstWhereOrNull(nodeQuery); } diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 730f8b9f6..01f65d46f 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -141,6 +141,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_reactive_obx_variable_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_save_email_as_draft_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_store_email_sort_order_extension.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/mixin/handle_team_mailbox_mixin.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/initialize_app_language.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/labels/handle_logic_label_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/notify_thread_detail_setting_updated.dart'; @@ -239,7 +240,8 @@ class MailboxDashBoardController extends ReloadableController SaaSPremiumMixin, AiScribeMixin, SearchLabelFilterModalMixin, - AddLabelToEmailMixin { + AddLabelToEmailMixin, + HandleTeamMailboxMixin { final RemoveEmailDraftsInteractor _removeEmailDraftsInteractor = Get.find(); final EmailReceiveManager _emailReceiveManager = Get.find(); diff --git a/lib/features/mailbox_dashboard/presentation/extensions/get_trash_mailbox_id_and_path_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/get_trash_mailbox_id_and_path_extension.dart new file mode 100644 index 000000000..102e64a4f --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/extensions/get_trash_mailbox_id_and_path_extension.dart @@ -0,0 +1,33 @@ +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/extensions/presentation_mailbox_extension.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; + +extension GetTrashMailboxIdAndPathExtension on MailboxDashBoardController { + ({MailboxId? trashId, String? trashPath}) getTrashMailboxIdAndPath( + PresentationMailbox emailMailbox, + ) { + final defaultResult = ( + trashId: mapDefaultMailboxIdByRole[PresentationMailbox.roleTrash], + trashPath: null as String?, + ); + + final mailbox = selectedMailbox.value ?? emailMailbox; + + if (mailbox.isPersonal) return defaultResult; + + final namespace = mailbox.namespace; + if (namespace == null) return defaultResult; + + final trashId = findDefaultMailboxIdInTeamMailbox( + namespace: namespace, + mailboxName: PresentationMailbox.trashRole, + ); + if (trashId == null) return defaultResult; + + final trashPath = getTeamMailboxNodePathWithSeparator( + mailboxId: trashId, + ); + return (trashId: trashId, trashPath: trashPath); + } +} diff --git a/lib/features/thread/presentation/mixin/email_action_controller.dart b/lib/features/thread/presentation/mixin/email_action_controller.dart index 4a800f60e..faadc5829 100644 --- a/lib/features/thread/presentation/mixin/email_action_controller.dart +++ b/lib/features/thread/presentation/mixin/email_action_controller.dart @@ -20,9 +20,13 @@ import 'package:tmail_ui_user/features/destination_picker/presentation/model/des import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; +import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; +import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_exception.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/get_trash_mailbox_id_and_path_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_type_for_email_selection.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; @@ -59,23 +63,56 @@ mixin EmailActionController { mailboxDashBoardController.openEmailDetailedView(presentationEmail); } - void moveToTrash(PresentationEmail email, {PresentationMailbox? mailboxContain}) async { - final session = mailboxDashBoardController.sessionCurrent; - final accountId = mailboxDashBoardController.accountId.value; - final trashMailboxId = mailboxDashBoardController.mapDefaultMailboxIdByRole[PresentationMailbox.roleTrash]; - - if (session != null && mailboxContain != null && accountId != null && trashMailboxId != null) { - _moveToTrashAction( - session, - accountId, - MoveToMailboxRequest( - {mailboxContain.id: email.id != null ? [email.id!] : []}, - trashMailboxId, - MoveAction.moving, - EmailActionType.moveToTrash), - email.id != null ? {email.id! : email.hasRead} : {}, - ); + void moveToTrash( + PresentationEmail email, { + PresentationMailbox? mailboxContain, + }) { + if (mailboxContain == null) { + _emitMoveToTrashFailure(NotFoundMailboxOfEmailException()); + return; } + + final session = mailboxDashBoardController.sessionCurrent; + if (session == null) { + _emitMoveToTrashFailure(NotFoundSessionException()); + return; + } + + final accountId = mailboxDashBoardController.accountId.value; + if (accountId == null) { + _emitMoveToTrashFailure(NotFoundAccountIdException()); + return; + } + + final (:trashId, :trashPath) = + mailboxDashBoardController.getTrashMailboxIdAndPath(mailboxContain); + if (trashId == null) { + _emitMoveToTrashFailure(NotFoundTrashMailboxException()); + return; + } + + _moveToTrashAction( + session, + accountId, + MoveToMailboxRequest( + {mailboxContain.id: email.id != null ? [email.id!] : []}, + trashId, + MoveAction.moving, + EmailActionType.moveToTrash, + destinationPath: trashPath, + ), + email.id != null ? {email.id!: email.hasRead} : {}, + ); + } + + void _emitMoveToTrashFailure(Exception exception) { + mailboxDashBoardController.emitFailure( + controller: mailboxDashBoardController, + failure: MoveToMailboxFailure( + EmailActionType.moveToTrash, + exception: exception, + ), + ); } void _moveToTrashAction( diff --git a/test/features/mailbox/presentation/extensions/handle_team_mailbox_extension_test.dart b/test/features/mailbox/presentation/extensions/handle_team_mailbox_extension_test.dart new file mode 100644 index 000000000..b5c79eb46 --- /dev/null +++ b/test/features/mailbox/presentation/extensions/handle_team_mailbox_extension_test.dart @@ -0,0 +1,301 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/namespace.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/mixin/handle_team_mailbox_mixin.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; + +import 'handle_team_mailbox_extension_test.mocks.dart'; + +class TestHandleTeamMailboxMixin with HandleTeamMailboxMixin {} + +@GenerateNiceMocks([ + MockSpec(), +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late TestHandleTeamMailboxMixin testMixin; + late MockMailboxController mockMailboxController; + + final teamNamespace = Namespace('#TeamMailbox'); + final teamMailboxId = MailboxId(Id('team-root')); + final teamTrashId = MailboxId(Id('team-trash')); + final teamInboxId = MailboxId(Id('team-inbox')); + final teamSentId = MailboxId(Id('team-sent')); + + MailboxTree buildTeamMailboxTree({ + List? teamChildren, + }) { + return MailboxTree( + MailboxNode( + MailboxNode.rootItem(), + childrenItems: [ + MailboxNode( + PresentationMailbox( + teamMailboxId, + name: MailboxName('Team'), + namespace: teamNamespace, + ), + childrenItems: teamChildren ?? + [ + MailboxNode( + PresentationMailbox( + teamTrashId, + name: MailboxName('Trash'), + namespace: teamNamespace, + parentId: teamMailboxId, + ), + ), + MailboxNode( + PresentationMailbox( + teamInboxId, + name: MailboxName('Inbox'), + namespace: teamNamespace, + parentId: teamMailboxId, + ), + ), + MailboxNode( + PresentationMailbox( + teamSentId, + name: MailboxName('Sent'), + namespace: teamNamespace, + parentId: teamMailboxId, + ), + ), + ], + ), + ], + ), + ); + } + + setUp(() { + Get.testMode = true; + + mockMailboxController = MockMailboxController(); + when(mockMailboxController.onStart) + .thenReturn(InternalFinalCallback(callback: () {})); + when(mockMailboxController.onDelete) + .thenReturn(InternalFinalCallback(callback: () {})); + + Get.put(mockMailboxController); + + testMixin = TestHandleTeamMailboxMixin(); + }); + + tearDown(() { + Get.reset(); + }); + + group('HandleTeamMailboxMixin::findDefaultMailboxIdInTeamMailbox', () { + test( + 'SHOULD return trash mailbox id ' + 'WHEN namespace matches and trash mailbox exists', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree().obs); + + final result = testMixin.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: PresentationMailbox.trashRole, + ); + + expect(result, equals(teamTrashId)); + }, + ); + + test( + 'SHOULD return null ' + 'WHEN namespace does not match any team mailbox', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree().obs); + + final result = testMixin.findDefaultMailboxIdInTeamMailbox( + namespace: Namespace('#UnknownTeam'), + mailboxName: PresentationMailbox.trashRole, + ); + + expect(result, isNull); + }, + ); + + test( + 'SHOULD return null ' + 'WHEN mailbox name does not match any child', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree().obs); + + final result = testMixin.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: 'archive', + ); + + expect(result, isNull); + }, + ); + + test( + 'SHOULD match mailbox name case-insensitively', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree().obs); + + final result = testMixin.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: 'TRASH', + ); + + expect(result, equals(teamTrashId)); + }, + ); + + test( + 'SHOULD return null ' + 'WHEN team mailbox tree is empty', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(MailboxTree(MailboxNode.root()).obs); + + final result = testMixin.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: PresentationMailbox.trashRole, + ); + + expect(result, isNull); + }, + ); + + test( + 'SHOULD return null ' + 'WHEN team mailbox has no children', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree(teamChildren: []).obs); + + final result = testMixin.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: PresentationMailbox.trashRole, + ); + + expect(result, isNull); + }, + ); + + test( + 'SHOULD return correct mailbox id ' + 'WHEN searching for inbox in team mailbox', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree().obs); + + final result = testMixin.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: PresentationMailbox.inboxRole, + ); + + expect(result, equals(teamInboxId)); + }, + ); + + test( + 'SHOULD return null ' + 'WHEN MailboxController is not registered', + () { + Get.delete(); + + final result = testMixin.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: PresentationMailbox.trashRole, + ); + + expect(result, isNull); + }, + ); + }); + + group('HandleTeamMailboxMixin::getTeamMailboxNodePathWithSeparator', () { + test( + 'SHOULD return path with parent separator ' + 'WHEN mailbox has a parent', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree().obs); + + final result = testMixin.getTeamMailboxNodePathWithSeparator( + mailboxId: teamTrashId, + ); + + expect(result, equals('Team/Trash')); + }, + ); + + test( + 'SHOULD return path with custom separator ' + 'WHEN custom pathSeparator is provided', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree().obs); + + final result = testMixin.getTeamMailboxNodePathWithSeparator( + mailboxId: teamTrashId, + pathSeparator: '.', + ); + + expect(result, equals('Team.Trash')); + }, + ); + + test( + 'SHOULD return null ' + 'WHEN mailbox id does not exist in tree', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree().obs); + + final result = testMixin.getTeamMailboxNodePathWithSeparator( + mailboxId: MailboxId(Id('non-existent')), + ); + + expect(result, isNull); + }, + ); + + test( + 'SHOULD return mailbox name only ' + 'WHEN mailbox has no parent', + () { + when(mockMailboxController.teamMailboxesTree) + .thenReturn(buildTeamMailboxTree().obs); + + final result = testMixin.getTeamMailboxNodePathWithSeparator( + mailboxId: teamMailboxId, + ); + + expect(result, equals('Team')); + }, + ); + + test( + 'SHOULD return null ' + 'WHEN MailboxController is not registered', + () { + Get.delete(); + + final result = testMixin.getTeamMailboxNodePathWithSeparator( + mailboxId: teamTrashId, + ); + + expect(result, isNull); + }, + ); + }); +} diff --git a/test/features/thread/presentation/mixin/get_trash_mailbox_id_and_path_test.dart b/test/features/thread/presentation/mixin/get_trash_mailbox_id_and_path_test.dart new file mode 100644 index 000000000..b528eeceb --- /dev/null +++ b/test/features/thread/presentation/mixin/get_trash_mailbox_id_and_path_test.dart @@ -0,0 +1,189 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/namespace.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/get_trash_mailbox_id_and_path_extension.dart'; + +import 'get_trash_mailbox_id_and_path_test.mocks.dart'; + +@GenerateNiceMocks([ + MockSpec(), +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final defaultTrashId = MailboxId(Id('default-trash')); + final teamNamespace = Namespace('#TeamMailbox'); + final teamMailboxId = MailboxId(Id('team-root')); + final teamTrashId = MailboxId(Id('team-trash')); + + final personalMailbox = PresentationMailbox( + MailboxId(Id('personal-inbox')), + name: MailboxName('Inbox'), + ); + + final teamMailbox = PresentationMailbox( + teamMailboxId, + name: MailboxName('Team'), + namespace: teamNamespace, + ); + + final teamMailboxWithNullNamespace = PresentationMailbox( + MailboxId(Id('team-no-ns')), + name: MailboxName('Team No NS'), + ); + + late MockMailboxDashBoardController mockDashBoardController; + + setUp(() { + Get.testMode = true; + + mockDashBoardController = MockMailboxDashBoardController(); + + when(mockDashBoardController.mapDefaultMailboxIdByRole) + .thenReturn({PresentationMailbox.roleTrash: defaultTrashId}); + when(mockDashBoardController.selectedMailbox) + .thenReturn(Rxn()); + }); + + group('GetTrashMailboxIdAndPathExtension::getTrashMailboxIdAndPath', () { + test( + 'SHOULD return default trash id with null path ' + 'WHEN emailMailbox is personal (no namespace)', + () { + final result = mockDashBoardController.getTrashMailboxIdAndPath( + personalMailbox, + ); + + expect(result.trashId, equals(defaultTrashId)); + expect(result.trashPath, isNull); + }, + ); + + test( + 'SHOULD return default trash id with null path ' + 'WHEN selectedMailbox is personal', + () { + when(mockDashBoardController.selectedMailbox) + .thenReturn(Rxn(personalMailbox)); + + final result = mockDashBoardController.getTrashMailboxIdAndPath( + teamMailbox, + ); + + expect(result.trashId, equals(defaultTrashId)); + expect(result.trashPath, isNull); + }, + ); + + test( + 'SHOULD use selectedMailbox over emailMailbox ' + 'WHEN selectedMailbox is not null', + () { + when(mockDashBoardController.selectedMailbox) + .thenReturn(Rxn(personalMailbox)); + + final result = mockDashBoardController.getTrashMailboxIdAndPath( + teamMailbox, + ); + + expect(result.trashId, equals(defaultTrashId)); + expect(result.trashPath, isNull); + }, + ); + + test( + 'SHOULD return default trash id with null path ' + 'WHEN mailbox namespace is null', + () { + final result = mockDashBoardController.getTrashMailboxIdAndPath( + teamMailboxWithNullNamespace, + ); + + expect(result.trashId, equals(defaultTrashId)); + expect(result.trashPath, isNull); + }, + ); + + test( + 'SHOULD return default trash id with null path ' + 'WHEN findDefaultMailboxIdInTeamMailbox returns null', + () { + when(mockDashBoardController.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: PresentationMailbox.trashRole, + )).thenReturn(null); + + final result = mockDashBoardController.getTrashMailboxIdAndPath( + teamMailbox, + ); + + expect(result.trashId, equals(defaultTrashId)); + expect(result.trashPath, isNull); + }, + ); + + test( + 'SHOULD return team trash id with path ' + 'WHEN team mailbox has trash folder', + () { + when(mockDashBoardController.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: PresentationMailbox.trashRole, + )).thenReturn(teamTrashId); + when(mockDashBoardController.getTeamMailboxNodePathWithSeparator( + mailboxId: teamTrashId, + )).thenReturn('Team/Trash'); + + final result = mockDashBoardController.getTrashMailboxIdAndPath( + teamMailbox, + ); + + expect(result.trashId, equals(teamTrashId)); + expect(result.trashPath, equals('Team/Trash')); + }, + ); + + test( + 'SHOULD return team trash id with null path ' + 'WHEN getTeamMailboxNodePathWithSeparator returns null', + () { + when(mockDashBoardController.findDefaultMailboxIdInTeamMailbox( + namespace: teamNamespace, + mailboxName: PresentationMailbox.trashRole, + )).thenReturn(teamTrashId); + when(mockDashBoardController.getTeamMailboxNodePathWithSeparator( + mailboxId: teamTrashId, + )).thenReturn(null); + + final result = mockDashBoardController.getTrashMailboxIdAndPath( + teamMailbox, + ); + + expect(result.trashId, equals(teamTrashId)); + expect(result.trashPath, isNull); + }, + ); + + test( + 'SHOULD return null trash id with null path ' + 'WHEN mapDefaultMailboxIdByRole has no trash entry ' + 'AND mailbox is personal', + () { + when(mockDashBoardController.mapDefaultMailboxIdByRole).thenReturn({}); + + final result = mockDashBoardController.getTrashMailboxIdAndPath( + personalMailbox, + ); + + expect(result.trashId, isNull); + expect(result.trashPath, isNull); + }, + ); + }); +}