From e529b4d0870483273345d23f0ee8e9517069272c Mon Sep 17 00:00:00 2001 From: Keshav Bhatt Date: Wed, 12 Mar 2025 22:29:08 +0530 Subject: [PATCH 01/12] test(editor-slice-utils): add unit tests for retrieveCC function --- src/store/tests/editor-slice-utils.test.ts | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/store/tests/editor-slice-utils.test.ts diff --git a/src/store/tests/editor-slice-utils.test.ts b/src/store/tests/editor-slice-utils.test.ts new file mode 100644 index 000000000..7621e75c4 --- /dev/null +++ b/src/store/tests/editor-slice-utils.test.ts @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: 2025 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as shellHooks from '@zextras/carbonio-shell-ui'; + +import { generateAccount } from '../../carbonio-ui-commons/test/mocks/accounts/account-generator'; +import { generateMessage } from '../../tests/generators/generateMessage'; +import { retrieveCC } from '../editor-slice-utils'; + +describe('retrieveCC', () => { + it('should retrieveCC', () => { + const defaultIdentity = { + id: '3b778c1d-529f-45b7-b131-5162c83551f7', + name: 'DEFAULT', + _attrs: [] + } as shellHooks.Identity; + + const sendAsIdentity = { + id: '80c3aba1-f2e9-4492-9447-cabdbf08a2e8', + name: 'sendAsIdentity', + _attrs: [ + { + zimbraPrefIdentityName: 'sendAsIdentity', + zimbraPrefFromDisplay: 'Homer Simpson', + zimbraPrefFromAddress: 'delegator@email.com', + zimbraPrefFromAddressType: 'sendAs', + zimbraPrefReplyToEnabled: 'FALSE' + } + ] + } as shellHooks.Identity; + + const accountRights = { + targets: [ + { + right: 'sendAs', + target: [ + { + id: '0', + name: 'Homer Simpson', + type: 'account', + email: [{ addr: 'delegator@email.com' }], + d: 'Homer Simpson' + } + ] + } + ] + }; + + const dummyAccount = generateAccount(); + const account = { + ...dummyAccount, + id: defaultIdentity.id, + email: 'default@test.com', + identities: { identity: [defaultIdentity, sendAsIdentity], rights: accountRights } + }; + + // console.log(account?.rights.targets[0].target[0].email); + + jest.spyOn(shellHooks, 'getUserAccount').mockReturnValue(account); + + const senderAccountName = 'delegator@email.com'; + const message = generateMessage({ + from: { + type: 'f', + address: senderAccountName + }, + cc: [ + { + type: 'c', + address: 'delegator@email.com' + } + ], + to: [] + }); + const participants = retrieveCC(message, senderAccountName); + expect(participants).toEqual([ + { + type: 'c', + address: 'delegator@email.com' + } + ]); + }); +}); From c99a11f123abbaeb69ec6f1668f20de404481c27 Mon Sep 17 00:00:00 2001 From: Keshav Bhatt Date: Thu, 20 Mar 2025 15:08:24 +0530 Subject: [PATCH 02/12] test(editor-slice-utils): enhance unit tests for retrieveCC function with additional scenarios --- src/store/tests/editor-slice-utils.test.ts | 251 +++++++++++++++------ 1 file changed, 186 insertions(+), 65 deletions(-) diff --git a/src/store/tests/editor-slice-utils.test.ts b/src/store/tests/editor-slice-utils.test.ts index 7621e75c4..60c79d73d 100644 --- a/src/store/tests/editor-slice-utils.test.ts +++ b/src/store/tests/editor-slice-utils.test.ts @@ -11,76 +11,197 @@ import { generateMessage } from '../../tests/generators/generateMessage'; import { retrieveCC } from '../editor-slice-utils'; describe('retrieveCC', () => { - it('should retrieveCC', () => { - const defaultIdentity = { - id: '3b778c1d-529f-45b7-b131-5162c83551f7', - name: 'DEFAULT', - _attrs: [] - } as shellHooks.Identity; - - const sendAsIdentity = { - id: '80c3aba1-f2e9-4492-9447-cabdbf08a2e8', - name: 'sendAsIdentity', - _attrs: [ - { - zimbraPrefIdentityName: 'sendAsIdentity', - zimbraPrefFromDisplay: 'Homer Simpson', - zimbraPrefFromAddress: 'delegator@email.com', - zimbraPrefFromAddressType: 'sendAs', - zimbraPrefReplyToEnabled: 'FALSE' - } - ] - } as shellHooks.Identity; - - const accountRights = { - targets: [ - { - right: 'sendAs', - target: [ - { - id: '0', - name: 'Homer Simpson', - type: 'account', - email: [{ addr: 'delegator@email.com' }], - d: 'Homer Simpson' - } - ] - } - ] - }; - - const dummyAccount = generateAccount(); - const account = { - ...dummyAccount, - id: defaultIdentity.id, - email: 'default@test.com', - identities: { identity: [defaultIdentity, sendAsIdentity], rights: accountRights } - }; - - // console.log(account?.rights.targets[0].target[0].email); - - jest.spyOn(shellHooks, 'getUserAccount').mockReturnValue(account); - - const senderAccountName = 'delegator@email.com'; + const defaultIdentity = { + id: '3b778c1d-529f-45b7-b131-5162c83551f7', + name: 'DEFAULT', + _attrs: [] + } as shellHooks.Identity; + + const sendAsIdentityDisplayName = 'Homer Simpson'; + const delegatorAccountAddress = 'delegatoraccount@test.com'; + const sendAsIdentity = { + id: '80c3aba1-f2e9-4492-9447-cabdbf08a2e8', + name: 'sendAsIdentity', + _attrs: [ + { + zimbraPrefIdentityName: 'sendAsIdentity', + zimbraPrefFromDisplay: sendAsIdentityDisplayName, + zimbraPrefFromAddress: delegatorAccountAddress, // Delegator + zimbraPrefFromAddressType: 'sendAs', + zimbraPrefReplyToEnabled: 'FALSE' + } + ] + } as shellHooks.Identity; + + const accountRights = { + targets: [ + { + right: 'sendAs', + target: [ + { + id: sendAsIdentity.id, + name: sendAsIdentityDisplayName, + type: 'account', + email: [{ addr: delegatorAccountAddress }], + d: sendAsIdentityDisplayName + } + ] + } + ] + }; + + const mainAccount: shellHooks.Account = { + ...generateAccount(), + id: defaultIdentity.id, + name: 'default@test.com', + displayName: 'default account', + identities: { identity: [defaultIdentity, sendAsIdentity] }, + rights: accountRights as never // cannot import AccountRights from carbonio-shell-ui + }; + + const defaultIdentityForDelegator = { + id: sendAsIdentity.id, + name: 'DEFAULT', + _attrs: [] + } as shellHooks.Identity; + + const delegatorAccount = { + ...generateAccount(), + id: defaultIdentityForDelegator.id, + email: delegatorAccountAddress, + identities: { identity: [defaultIdentityForDelegator], rights: [] }, + name: delegatorAccountAddress, + displayName: sendAsIdentityDisplayName + }; + + const externalUser = 'external@test.com'; + const anotherUser = 'userC@test.com'; + + beforeEach(() => { + jest.restoreAllMocks(); + }); + + // Scenario: The main account (who has "Send As" rights) starts a conversation and adds the delegator in CC. + // Expected Behavior: On "Reply All," both the main account and delegator remain in CC. + it('TC1: Main account sends an email, Delegator in CC', () => { + jest + .spyOn(shellHooks, 'getUserAccount') + .mockImplementationOnce(() => mainAccount) + .mockImplementationOnce(() => delegatorAccount); + + const message = generateMessage({ + from: { type: 'f', address: mainAccount.name }, + cc: [{ type: 'c', address: delegatorAccount.email }], + to: [] + }); + + expect(retrieveCC(message, mainAccount.name)).toEqual([ + { type: 'c', address: delegatorAccount.email } + ]); + }); + + // Scenario: The delegator starts the conversation and includes the main account in CC. + // Expected Behavior: On "Reply All," both accounts remain in CC. + it('TC2: Delegator sends an email, Main Account in CC', () => { + jest + .spyOn(shellHooks, 'getUserAccount') + .mockImplementationOnce(() => delegatorAccount) + .mockImplementationOnce(() => mainAccount); + + const message = generateMessage({ + from: { type: 'f', address: delegatorAccount.email }, + cc: [{ type: 'c', address: mainAccount.name }], + to: [] + }); + + expect(retrieveCC(message, delegatorAccount.email)).toEqual([ + { type: 'c', address: mainAccount.name } + ]); + }); + + // Scenario: The main account sends an email using "Send As" permissions for the delegator, while also including the delegator in CC. + // Expected Behavior: On "Reply All," only Main account remains in CC. + it('TC3: Main Account sends as Delegator, Delegator in CC', () => { + jest + .spyOn(shellHooks, 'getUserAccount') + .mockImplementationOnce(() => mainAccount) + .mockImplementationOnce(() => delegatorAccount); + const message = generateMessage({ - from: { - type: 'f', - address: senderAccountName - }, + from: { type: 'f', address: delegatorAccount.email }, cc: [ - { - type: 'c', - address: 'delegator@email.com' - } + { type: 'c', address: delegatorAccount.email }, + { type: 'c', address: mainAccount.name } ], to: [] }); - const participants = retrieveCC(message, senderAccountName); - expect(participants).toEqual([ - { - type: 'c', - address: 'delegator@email.com' - } + + expect(retrieveCC(message, delegatorAccount.email)).toEqual([ + { type: 'c', address: mainAccount.name } + ]); + }); + + // Scenario: The main account sends an email on behalf of the delegator but does not include the delegator in CC. + // Expected Behavior: On "Reply All," the delegator should not be automatically added to CC. + it('TC4: Main Account sends as Delegator, Delegator NOT in CC', () => { + jest + .spyOn(shellHooks, 'getUserAccount') + .mockImplementationOnce(() => mainAccount) + .mockImplementationOnce(() => delegatorAccount); + + const message = generateMessage({ + from: { type: 'f', address: delegatorAccount.email }, + cc: [], + to: [] + }); + + expect(retrieveCC(message, delegatorAccount.email)).toEqual([]); + }); + + // Scenario: An external user replies to the email thread where both the main account and delegator were in CC. + // Expected Behavior: On "Reply All," both remain in CC. + it('TC5: External user replies to conversation with Main Account & Delegator in CC', () => { + jest + .spyOn(shellHooks, 'getUserAccount') + .mockImplementationOnce(() => mainAccount) + .mockImplementationOnce(() => delegatorAccount); + + const message = generateMessage({ + from: { type: 'f', address: externalUser }, + cc: [ + { type: 'c', address: mainAccount.name }, + { type: 'c', address: delegatorAccount.email } + ], + to: [] + }); + + expect(retrieveCC(message, externalUser)).toEqual([ + { type: 'c', address: mainAccount.name }, + { type: 'c', address: delegatorAccount.email } + ]); + }); + + // Scenario: The main account sends an email using "Send As" for the delegator and includes a third party (User C) in CC. + // Expected Behavior: On "Reply All," Main Account, User C remain in CC. + it('TC6: Main Account sends as Delegator, Another Account in CC', () => { + jest + .spyOn(shellHooks, 'getUserAccount') + .mockImplementationOnce(() => mainAccount) + .mockImplementationOnce(() => delegatorAccount); + + const message = generateMessage({ + from: { type: 'f', address: delegatorAccount.email }, + cc: [ + { type: 'c', address: anotherUser }, + { type: 'c', address: mainAccount.name }, + { type: 'c', address: delegatorAccount.email } + ], + to: [] + }); + + expect(retrieveCC(message, delegatorAccount.email)).toEqual([ + { type: 'c', address: anotherUser }, + { type: 'c', address: mainAccount.name } ]); }); }); From 1ba61bd5f6373e36070d11c5a2cf455748352576 Mon Sep 17 00:00:00 2001 From: Davide Frison Date: Fri, 21 Mar 2025 13:10:34 +0100 Subject: [PATCH 03/12] fix: do not remove from address when replying to email using a different identity - added some tests - need to do other tests to check all cases and document the behavior --- src/store/editor-slice-utils.ts | 11 +-- src/store/tests/editor-slice-utils.test.ts | 79 +++++++++++++++++++++- 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/src/store/editor-slice-utils.ts b/src/store/editor-slice-utils.ts index f8cce4940..fbd419799 100644 --- a/src/store/editor-slice-utils.ts +++ b/src/store/editor-slice-utils.ts @@ -111,10 +111,13 @@ export function retrieveALL( ); if (replyToParticipants.length === 0) { if (original.parent === FOLDERS.SENT || original.isSentByMe || isSentByMe) { - return filter(toEmails, (c) => replySenderAccountName !== getAddressOwnerAccount(c.address)) - .length === 0 - ? toEmails - : filter(toEmails, (c) => replySenderAccountName !== getAddressOwnerAccount(c.address)); + const replyToFrom = changeTypeOfParticipants(fromEmails, ParticipantRole.TO); + const replyingTo = [...replyToFrom, ...toEmails]; + const participantsWithoutSender = filter( + replyingTo, + (c) => replySenderAccountName !== getAddressOwnerAccount(c.address) + ); + return participantsWithoutSender.length === 0 ? replyToFrom : participantsWithoutSender; } return changeTypeOfParticipants(fromEmails, ParticipantRole.TO); } diff --git a/src/store/tests/editor-slice-utils.test.ts b/src/store/tests/editor-slice-utils.test.ts index 60c79d73d..4fca91472 100644 --- a/src/store/tests/editor-slice-utils.test.ts +++ b/src/store/tests/editor-slice-utils.test.ts @@ -6,9 +6,12 @@ import * as shellHooks from '@zextras/carbonio-shell-ui'; +import { FOLDERS } from '../../carbonio-ui-commons/constants/folders'; +import { ParticipantRole } from '../../carbonio-ui-commons/constants/participants'; import { generateAccount } from '../../carbonio-ui-commons/test/mocks/accounts/account-generator'; +import { getAddressOwnerAccount } from '../../helpers/identities'; import { generateMessage } from '../../tests/generators/generateMessage'; -import { retrieveCC } from '../editor-slice-utils'; +import { retrieveALL, retrieveCC } from '../editor-slice-utils'; describe('retrieveCC', () => { const defaultIdentity = { @@ -205,3 +208,77 @@ describe('retrieveCC', () => { ]); }); }); + +describe('retrieveALL', () => { + it('should return "someone@test.com" when replying as Me to a message sent to me from "someone@test.com"', () => { + const receivedMessage = { + ...generateMessage(), + participants: [ + { type: ParticipantRole.FROM, address: 'someone@test.com' }, + { type: ParticipantRole.TO, address: 'me@test.com' } + ] + }; + const result = retrieveALL(receivedMessage, 'me@test.com'); + + expect(result).toEqual([{ address: 'someone@test.com', type: 't' }]); + }); + + it('should return "me@test.com" when replying as Me to a message sent to myself', () => { + const receivedMessage = { + ...generateMessage(), + participants: [ + { type: ParticipantRole.FROM, address: 'me@test.com' }, + { type: ParticipantRole.TO, address: 'me@test.com' } + ] + }; + const result = retrieveALL(receivedMessage, 'me@test.com'); + + expect(result).toEqual([{ address: 'me@test.com', type: 't' }]); + }); + + it('should return [Me and "someoneElse"] in To when replying as "sharedAccount" to a message sent by Me To "someoneElse" and "sharedAccount" is in CC', () => { + const sharedAccount = 'sharedAccount@test.com'; + const me = 'me@test.com'; + const someoneElse = 'someoneElse@test.com'; + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.SENT, + participants: [ + { type: ParticipantRole.FROM, address: me }, + { type: ParticipantRole.TO, address: someoneElse }, + { type: ParticipantRole.CARBON_COPY, address: sharedAccount } + ] + }; + const replyMessageRecipients = retrieveALL(receivedMessage, sharedAccount); + const ccResult = retrieveCC(receivedMessage, sharedAccount); + + expect(replyMessageRecipients).toEqual([ + { address: me, type: 't' }, + { address: someoneElse, type: 't' } + ]); + expect(ccResult).toEqual([{ address: sharedAccount, type: 'c' }]); + }); + + // TODO: find a way to generate correct identities + it.skip('should remove the sender when it was in the recipients of the original message', () => { + const sharedAccount = 'sharedAccount@test.com'; + const me = 'me@test.com'; + const someoneElse = 'someoneElse@test.com'; + (getAddressOwnerAccount as jest.Mock).mockReturnValue(sharedAccount); + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.SENT, + participants: [ + { type: ParticipantRole.FROM, address: me }, + { type: ParticipantRole.TO, address: someoneElse }, + { type: ParticipantRole.TO, address: sharedAccount } + ] + }; + const replyMessageRecipients = retrieveALL(receivedMessage, sharedAccount); + + expect(replyMessageRecipients).toEqual([ + { address: me, type: 't' }, + { address: someoneElse, type: 't' } + ]); + }); +}); From e7b7fc57aba86f825bc4e41050ddec82ee3920d4 Mon Sep 17 00:00:00 2001 From: Keshav Bhatt Date: Fri, 21 Mar 2025 20:00:32 +0530 Subject: [PATCH 04/12] refactor: reorganize address retrieval logic and enhance unit tests for retrieveALL and retrieveReplyTo functions --- src/helpers/get_available_addresses.ts | 67 +++++++++++++++ src/helpers/identities.ts | 68 ++------------- src/store/editor-slice-utils.ts | 7 +- src/store/tests/editor-slice-utils.test.ts | 83 +++++++++++++++---- src/views/app/detail-panel/edit/edit-view.tsx | 7 +- 5 files changed, 144 insertions(+), 88 deletions(-) create mode 100644 src/helpers/get_available_addresses.ts diff --git a/src/helpers/get_available_addresses.ts b/src/helpers/get_available_addresses.ts new file mode 100644 index 000000000..c40d99024 --- /dev/null +++ b/src/helpers/get_available_addresses.ts @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2025 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { getUserAccount, getUserSettings } from '@zextras/carbonio-shell-ui'; +import { isArray } from 'lodash'; + +import { AvailableAddress } from '../carbonio-ui-commons/types/identities'; +import { NO_ACCOUNT_NAME } from '../constants'; + +/** + * Returns the list of all the available addresses for the account and their type + */ +export const getAvailableAddresses = (): Array => { + const account = getUserAccount(); + const settings = getUserSettings(); + const result: Array = []; + + // Adds the email address of the primary account + result.push({ + address: account?.name ?? NO_ACCOUNT_NAME, + type: 'primary', + ownerAccount: account?.name ?? NO_ACCOUNT_NAME + }); + + // Adds all the aliases + if (settings.attrs.zimbraMailAlias) { + if (isArray(settings.attrs.zimbraMailAlias)) { + result.push( + ...(settings.attrs.zimbraMailAlias as string[]).map((alias: string) => ({ + address: alias, + type: 'alias', + ownerAccount: account?.name ?? NO_ACCOUNT_NAME + })) + ); + } else { + result.push({ + address: settings.attrs.zimbraMailAlias as string, + type: 'alias', + ownerAccount: account?.name ?? NO_ACCOUNT_NAME + }); + } + } + + // Adds the email addresses of all the delegation accounts + if (account?.rights?.targets) { + account.rights.targets.forEach((target) => { + if (target.target && (target.right === 'sendAs' || target.right === 'sendOnBehalfOf')) { + target.target.forEach((user) => { + if (user.type === 'account' && user.email) { + user.email.forEach((email) => { + result.push({ + address: email.addr, + type: 'delegation', + right: target.right, + ownerAccount: email.addr + }); + }); + } + }); + } + }); + } + + return result; +}; diff --git a/src/helpers/identities.ts b/src/helpers/identities.ts index 0c1affb25..01f5cd74f 100644 --- a/src/helpers/identities.ts +++ b/src/helpers/identities.ts @@ -3,16 +3,17 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { Account, getUserAccount, getUserSettings, t } from '@zextras/carbonio-shell-ui'; +import { Account, getUserAccount, t } from '@zextras/carbonio-shell-ui'; import { TFunction } from 'i18next'; -import { filter, findIndex, flatten, isArray, map, remove } from 'lodash'; +import { filter, findIndex, flatten, map, remove } from 'lodash'; -import { getFolderIdParts, getMessageOwnerAccountName } from './folders'; import { ParticipantRole } from '../carbonio-ui-commons/constants/participants'; import { getRootsMap } from '../carbonio-ui-commons/store/zustand/folder/hooks'; import type { Folders } from '../carbonio-ui-commons/types/folder'; import { NO_ACCOUNT_NAME } from '../constants'; import type { MailMessage, Participant } from '../types'; +import { getFolderIdParts, getMessageOwnerAccountName } from './folders'; +import { getAvailableAddresses } from './get_available_addresses'; /** * The name of the primary identity @@ -127,63 +128,6 @@ const IdentityTypeWeights = { const getNoIdentityPlaceholder = (): string => t('label.no_identity_selected', ''); -/** - * Returns the list of all the available addresses for the account and their type - */ -const getAvailableAddresses = (): Array => { - const account = getUserAccount(); - const settings = getUserSettings(); - const result: Array = []; - - // Adds the email address of the primary account - result.push({ - address: account?.name ?? NO_ACCOUNT_NAME, - type: 'primary', - ownerAccount: account?.name ?? NO_ACCOUNT_NAME - }); - - // Adds all the aliases - if (settings.attrs.zimbraMailAlias) { - if (isArray(settings.attrs.zimbraMailAlias)) { - result.push( - ...(settings.attrs.zimbraMailAlias as string[]).map((alias: string) => ({ - address: alias, - type: 'alias', - ownerAccount: account?.name ?? NO_ACCOUNT_NAME - })) - ); - } else { - result.push({ - address: settings.attrs.zimbraMailAlias as string, - type: 'alias', - ownerAccount: account?.name ?? NO_ACCOUNT_NAME - }); - } - } - - // Adds the email addresses of all the delegation accounts - if (account?.rights?.targets) { - account.rights.targets.forEach((target) => { - if (target.target && (target.right === 'sendAs' || target.right === 'sendOnBehalfOf')) { - target.target.forEach((user) => { - if (user.type === 'account' && user.email) { - user.email.forEach((email) => { - result.push({ - address: email.addr, - type: 'delegation', - right: target.right, - ownerAccount: email.addr - }); - }); - } - }); - } - }); - } - - return result; -}; - /** * Returns the name of the account that owns the given address * @@ -193,7 +137,8 @@ const getAddressOwnerAccount = (address: string): string | null => { if (!address) { return null; } - const addressInfo = getAvailableAddresses().filter((info) => info.address === address); + const availableAddresses = getAvailableAddresses(); + const addressInfo = availableAddresses.filter((info) => info.address === address); if (addressInfo.length === 0) { return null; } @@ -574,7 +519,6 @@ export { computeIdentityWeight, filterMatchingRecipients, getAddressOwnerAccount, - getAvailableAddresses, getIdentitiesDescriptors, getIdentityFromParticipant, getMessageSenderAccount, diff --git a/src/store/editor-slice-utils.ts b/src/store/editor-slice-utils.ts index fbd419799..bc10837eb 100644 --- a/src/store/editor-slice-utils.ts +++ b/src/store/editor-slice-utils.ts @@ -94,12 +94,12 @@ export function retrieveALL( original.participants, (c: Participant): boolean => c.type === ParticipantRole.TO ); + const fromEmails = filter( original.participants, - (c: Participant): boolean => - c.type === ParticipantRole.FROM && - replySenderAccountName !== getAddressOwnerAccount(c.address) + (c: Participant): boolean => c.type === ParticipantRole.FROM ); + const replyToParticipants = filter( original.participants, (c: Participant): boolean => c.type === ParticipantRole.REPLY_TO @@ -109,6 +109,7 @@ export function retrieveALL( filter(original.participants, (c: Participant): boolean => c.type === ParticipantRole.FROM), (c: Participant): boolean => replySenderAccountName === getAddressOwnerAccount(c.address) ); + if (replyToParticipants.length === 0) { if (original.parent === FOLDERS.SENT || original.isSentByMe || isSentByMe) { const replyToFrom = changeTypeOfParticipants(fromEmails, ParticipantRole.TO); diff --git a/src/store/tests/editor-slice-utils.test.ts b/src/store/tests/editor-slice-utils.test.ts index 4fca91472..084a06115 100644 --- a/src/store/tests/editor-slice-utils.test.ts +++ b/src/store/tests/editor-slice-utils.test.ts @@ -9,9 +9,10 @@ import * as shellHooks from '@zextras/carbonio-shell-ui'; import { FOLDERS } from '../../carbonio-ui-commons/constants/folders'; import { ParticipantRole } from '../../carbonio-ui-commons/constants/participants'; import { generateAccount } from '../../carbonio-ui-commons/test/mocks/accounts/account-generator'; -import { getAddressOwnerAccount } from '../../helpers/identities'; +import { AvailableAddress } from '../../carbonio-ui-commons/types/identities'; +import { getAvailableAddresses } from '../../helpers/get_available_addresses'; import { generateMessage } from '../../tests/generators/generateMessage'; -import { retrieveALL, retrieveCC } from '../editor-slice-utils'; +import { retrieveALL, retrieveCC, retrieveReplyTo } from '../editor-slice-utils'; describe('retrieveCC', () => { const defaultIdentity = { @@ -209,36 +210,72 @@ describe('retrieveCC', () => { }); }); +jest.mock('../../helpers/get_available_addresses', () => ({ + getAvailableAddresses: jest.fn() +})); + describe('retrieveALL', () => { + const meAddress = 'me@test.com'; + const sharedAccount = 'sharedAccount@test.com'; + + beforeEach(() => { + const primaryAddress: AvailableAddress = { + address: meAddress, + type: 'primary', + ownerAccount: meAddress + }; + const sharedAccountAddress: AvailableAddress = { + address: sharedAccount, + type: 'delegation', + ownerAccount: sharedAccount + }; + + (getAvailableAddresses as jest.Mock).mockReturnValue([primaryAddress, sharedAccountAddress]); + }); it('should return "someone@test.com" when replying as Me to a message sent to me from "someone@test.com"', () => { const receivedMessage = { ...generateMessage(), participants: [ { type: ParticipantRole.FROM, address: 'someone@test.com' }, - { type: ParticipantRole.TO, address: 'me@test.com' } + { type: ParticipantRole.TO, address: meAddress } ] }; - const result = retrieveALL(receivedMessage, 'me@test.com'); + const result = retrieveALL(receivedMessage, meAddress); expect(result).toEqual([{ address: 'someone@test.com', type: 't' }]); }); - it('should return "me@test.com" when replying as Me to a message sent to myself', () => { + it('should return "me@test.com" when replying as Me to a message sent to myself when in INBOX folder', () => { const receivedMessage = { ...generateMessage(), + parent: FOLDERS.INBOX, participants: [ - { type: ParticipantRole.FROM, address: 'me@test.com' }, - { type: ParticipantRole.TO, address: 'me@test.com' } + { type: ParticipantRole.FROM, address: meAddress }, + { type: ParticipantRole.TO, address: meAddress } ] }; - const result = retrieveALL(receivedMessage, 'me@test.com'); + const result = retrieveALL(receivedMessage, meAddress); - expect(result).toEqual([{ address: 'me@test.com', type: 't' }]); + expect(result).toEqual([{ address: meAddress, type: 't' }]); + }); + + it('should return "me@test.com" when replying as Me to a message sent to myself when in SENT folder', () => { + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.SENT, + participants: [ + { type: ParticipantRole.FROM, address: meAddress }, + { type: ParticipantRole.TO, address: meAddress } + ] + }; + + const result = retrieveALL(receivedMessage, meAddress); + + expect(result).toEqual([{ address: meAddress, type: 't' }]); }); it('should return [Me and "someoneElse"] in To when replying as "sharedAccount" to a message sent by Me To "someoneElse" and "sharedAccount" is in CC', () => { - const sharedAccount = 'sharedAccount@test.com'; - const me = 'me@test.com'; + const me = meAddress; const someoneElse = 'someoneElse@test.com'; const receivedMessage = { ...generateMessage(), @@ -250,21 +287,16 @@ describe('retrieveALL', () => { ] }; const replyMessageRecipients = retrieveALL(receivedMessage, sharedAccount); - const ccResult = retrieveCC(receivedMessage, sharedAccount); expect(replyMessageRecipients).toEqual([ { address: me, type: 't' }, { address: someoneElse, type: 't' } ]); - expect(ccResult).toEqual([{ address: sharedAccount, type: 'c' }]); }); - // TODO: find a way to generate correct identities - it.skip('should remove the sender when it was in the recipients of the original message', () => { - const sharedAccount = 'sharedAccount@test.com'; - const me = 'me@test.com'; + it('should remove the sender when it was in the recipients of the original message', () => { + const me = meAddress; const someoneElse = 'someoneElse@test.com'; - (getAddressOwnerAccount as jest.Mock).mockReturnValue(sharedAccount); const receivedMessage = { ...generateMessage(), parent: FOLDERS.SENT, @@ -282,3 +314,18 @@ describe('retrieveALL', () => { ]); }); }); + +describe('retrieveReplyTo', () => { + it('should return "me@test.com" when replying as Me to a message sent to myself', () => { + const receivedMessage = { + ...generateMessage(), + participants: [ + { type: ParticipantRole.FROM, address: 'me@test.com' }, + { type: ParticipantRole.TO, address: 'me@test.com' } + ] + }; + const result = retrieveReplyTo(receivedMessage); + + expect(result).toEqual([{ address: 'me@test.com', type: 't' }]); + }); +}); diff --git a/src/views/app/detail-panel/edit/edit-view.tsx b/src/views/app/detail-panel/edit/edit-view.tsx index 88fc0b89d..5b8a82a3f 100644 --- a/src/views/app/detail-panel/edit/edit-view.tsx +++ b/src/views/app/detail-panel/edit/edit-view.tsx @@ -41,11 +41,8 @@ import { checkPersonalCertificateExist } from '../../../../api/check-personal-ce import { GapContainer, GapRow } from '../../../../commons/gap-container'; import { EDIT_VIEW_CLOSING_REASONS, EditViewActions, TIMEOUTS } from '../../../../constants'; import { buildArrayFromFileList } from '../../../../helpers/files'; -import { - getAvailableAddresses, - getIdentitiesDescriptors, - getIdentityDescriptor -} from '../../../../helpers/identities'; +import { getAvailableAddresses } from '../../../../helpers/get_available_addresses'; +import { getIdentitiesDescriptors, getIdentityDescriptor } from '../../../../helpers/identities'; import { useCertificatesStore, useSmimeFeatureStore, From 64aff2fd0b101c8e02a7ba77432f09fe314b018f Mon Sep 17 00:00:00 2001 From: Keshav Bhatt Date: Fri, 21 Mar 2025 20:55:12 +0530 Subject: [PATCH 05/12] test(editor-slice-utils): enhance unit tests for retrieveALL and retrieveReplyTo functions with additional scenarios --- src/store/tests/editor-slice-utils.test.ts | 66 +++++++++++++++++++--- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/src/store/tests/editor-slice-utils.test.ts b/src/store/tests/editor-slice-utils.test.ts index 084a06115..4ce2139e6 100644 --- a/src/store/tests/editor-slice-utils.test.ts +++ b/src/store/tests/editor-slice-utils.test.ts @@ -14,7 +14,15 @@ import { getAvailableAddresses } from '../../helpers/get_available_addresses'; import { generateMessage } from '../../tests/generators/generateMessage'; import { retrieveALL, retrieveCC, retrieveReplyTo } from '../editor-slice-utils'; +jest.mock('../../helpers/get_available_addresses', () => ({ + getAvailableAddresses: jest.fn() +})); + describe('retrieveCC', () => { + beforeEach(() => { + (getAvailableAddresses as jest.Mock).mockReturnValue([]); + }); + const defaultIdentity = { id: '3b778c1d-529f-45b7-b131-5162c83551f7', name: 'DEFAULT', @@ -210,10 +218,6 @@ describe('retrieveCC', () => { }); }); -jest.mock('../../helpers/get_available_addresses', () => ({ - getAvailableAddresses: jest.fn() -})); - describe('retrieveALL', () => { const meAddress = 'me@test.com'; const sharedAccount = 'sharedAccount@test.com'; @@ -313,19 +317,67 @@ describe('retrieveALL', () => { { address: someoneElse, type: 't' } ]); }); + + it('should return someone@test.com (original sender) in the TO when replying to all, moves the rest of the participants to the CC', () => { + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.INBOX, + participants: [ + { type: ParticipantRole.FROM, address: 'someone@test.com' }, + { type: ParticipantRole.TO, address: sharedAccount }, + { type: ParticipantRole.TO, address: 'another@test.com' } + ] + }; + const replyMessageRecipients = retrieveALL(receivedMessage, meAddress); + const ccMessageRecipients = retrieveCC(receivedMessage, meAddress); + expect(replyMessageRecipients).toEqual([ + { + address: 'someone@test.com', + type: 't' + } + ]); + expect(ccMessageRecipients).toEqual([ + { + address: 'sharedAccount@test.com', + type: 'c' + }, + { + address: 'another@test.com', + type: 'c' + } + ]); + }); }); describe('retrieveReplyTo', () => { + const meAddress = 'me@test.com'; + const sharedAccount = 'sharedAccount@test.com'; + + beforeEach(() => { + const primaryAddress: AvailableAddress = { + address: meAddress, + type: 'primary', + ownerAccount: meAddress + }; + const sharedAccountAddress: AvailableAddress = { + address: sharedAccount, + type: 'delegation', + ownerAccount: sharedAccount + }; + + (getAvailableAddresses as jest.Mock).mockReturnValue([primaryAddress, sharedAccountAddress]); + }); it('should return "me@test.com" when replying as Me to a message sent to myself', () => { const receivedMessage = { ...generateMessage(), + parent: FOLDERS.SENT, participants: [ - { type: ParticipantRole.FROM, address: 'me@test.com' }, - { type: ParticipantRole.TO, address: 'me@test.com' } + { type: ParticipantRole.FROM, address: meAddress }, + { type: ParticipantRole.TO, address: meAddress } ] }; const result = retrieveReplyTo(receivedMessage); - expect(result).toEqual([{ address: 'me@test.com', type: 't' }]); + expect(result).toEqual([{ address: meAddress, type: 't' }]); }); }); From 962923c64a36cfde52a6a11a63dbabcaed6ad19a Mon Sep 17 00:00:00 2001 From: Davide Frison Date: Fri, 21 Mar 2025 17:41:51 +0100 Subject: [PATCH 06/12] test: add higher-level tests on replyAll --- .../editor-generator-integration.test.ts | 144 ++++++++++++++++++ src/store/tests/editor-slice-utils.test.ts | 4 +- 2 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/store/editor/tests/editor-generator-integration.test.ts diff --git a/src/store/editor/tests/editor-generator-integration.test.ts b/src/store/editor/tests/editor-generator-integration.test.ts new file mode 100644 index 000000000..f897e028f --- /dev/null +++ b/src/store/editor/tests/editor-generator-integration.test.ts @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: 2025 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { IdentityAttrs } from '@zextras/carbonio-shell-ui'; +import * as shellHooks from '@zextras/carbonio-shell-ui'; + +import { FOLDERS } from '../../../carbonio-ui-commons/constants/folders'; +import { ParticipantRole } from '../../../carbonio-ui-commons/constants/participants'; +import { generateAccount } from '../../../carbonio-ui-commons/test/mocks/accounts/account-generator'; +import { generateMessage } from '../../../tests/generators/generateMessage'; +import { generateReplyAllMsgEditor } from '../editor-generators'; + +describe('Reply All', () => { + const meAddress = 'me@test.com'; + const sharedAccountAddress = 'sharedAccount@test.com'; + const sendAsIdentityDisplayName = 'Homer Simpson'; + + const defaultIdentity = { + id: '3b778c1d-529f-45b7-b131-5162c83551f7', + name: 'DEFAULT', + _attrs: [] as IdentityAttrs + }; + + const sendAsIdentity = { + id: '80c3aba1-f2e9-4492-9447-cabdbf08a2e8', + name: 'sendAsIdentity', + _attrs: [ + { + zimbraPrefIdentityName: 'sendAsIdentity', + zimbraPrefFromDisplay: sendAsIdentityDisplayName, + zimbraPrefFromAddress: sharedAccountAddress, // Delegator + zimbraPrefFromAddressType: 'sendAs', + zimbraPrefReplyToEnabled: 'FALSE' + } + ] as IdentityAttrs + }; + const accountRights = { + targets: [ + { + right: 'sendAs', + target: [ + { + id: sendAsIdentity.id, + name: sendAsIdentityDisplayName, + type: 'account', + email: [{ addr: sharedAccountAddress }], + d: sendAsIdentityDisplayName + } + ] + } + ] + }; + const mainAccount: shellHooks.Account = { + ...generateAccount(), + id: defaultIdentity.id, + name: meAddress, + displayName: 'default account', + identities: { identity: [defaultIdentity, sendAsIdentity] }, + rights: accountRights as never // cannot import AccountRights from carbonio-shell-ui + }; + beforeEach(() => { + jest.spyOn(shellHooks, 'getUserAccount').mockImplementation(() => mainAccount); + }); + + describe('A message sent to me, the sharedAccount and another person', () => { + const originalFrom = 'someone@test.com'; + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.INBOX, + participants: [ + { type: ParticipantRole.FROM, address: originalFrom }, + { type: ParticipantRole.TO, address: meAddress }, + { type: ParticipantRole.TO, address: sharedAccountAddress }, + { type: ParticipantRole.TO, address: 'another@test.com' } + ] + }; + it('should reply with default identity (Me)', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + const defaultIdentity = ''; + expect(replyMsgEditor.identityId).toEqual(defaultIdentity); + }); + + it('should move "To" recipients to CC but exclude myself', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.cc).toEqual([ + { + address: sharedAccountAddress, + type: 'c' + }, + { + address: 'another@test.com', + type: 'c' + } + ]); + }); + it('should include only the original From address in the "To" field', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.to).toEqual([ + { + address: originalFrom, + type: 't' + } + ]); + }); + }); + + describe('A message sent only to the sharedAccount and another person', () => { + const originalFrom = 'someone@test.com'; + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.INBOX, + participants: [ + { type: ParticipantRole.FROM, address: originalFrom }, + { type: ParticipantRole.TO, address: sharedAccountAddress }, + { type: ParticipantRole.TO, address: 'another@test.com' } + ] + }; + it('should reply as delegated account', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); + }); + it('should move "To" recipients to CC but exclude the sharedAccount', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.cc).toEqual([ + { + address: 'another@test.com', + type: 'c' + } + ]); + }); + it('should include only the original From address in the "To" field', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.to).toEqual([ + { + address: originalFrom, + type: 't' + } + ]); + }); + }); +}); diff --git a/src/store/tests/editor-slice-utils.test.ts b/src/store/tests/editor-slice-utils.test.ts index 4ce2139e6..18d154bbd 100644 --- a/src/store/tests/editor-slice-utils.test.ts +++ b/src/store/tests/editor-slice-utils.test.ts @@ -94,7 +94,7 @@ describe('retrieveCC', () => { }); // Scenario: The main account (who has "Send As" rights) starts a conversation and adds the delegator in CC. - // Expected Behavior: On "Reply All," both the main account and delegator remain in CC. + // Expected Behavior: On "Reply All," the delegator remains in CC. it('TC1: Main account sends an email, Delegator in CC', () => { jest .spyOn(shellHooks, 'getUserAccount') @@ -113,7 +113,7 @@ describe('retrieveCC', () => { }); // Scenario: The delegator starts the conversation and includes the main account in CC. - // Expected Behavior: On "Reply All," both accounts remain in CC. + // Expected Behavior: On "Reply All," the main account remains in CC. it('TC2: Delegator sends an email, Main Account in CC', () => { jest .spyOn(shellHooks, 'getUserAccount') From 37e1e732342bd08a805bbb29d4fd0ab7a20ca575 Mon Sep 17 00:00:00 2001 From: Davide Frison Date: Fri, 21 Mar 2025 17:50:05 +0100 Subject: [PATCH 07/12] test: add some other tests describing CC behavior --- .../editor-generator-integration.test.ts | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/store/editor/tests/editor-generator-integration.test.ts b/src/store/editor/tests/editor-generator-integration.test.ts index f897e028f..ac1bdbfd6 100644 --- a/src/store/editor/tests/editor-generator-integration.test.ts +++ b/src/store/editor/tests/editor-generator-integration.test.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { IdentityAttrs } from '@zextras/carbonio-shell-ui'; import * as shellHooks from '@zextras/carbonio-shell-ui'; +import { IdentityAttrs } from '@zextras/carbonio-shell-ui'; import { FOLDERS } from '../../../carbonio-ui-commons/constants/folders'; import { ParticipantRole } from '../../../carbonio-ui-commons/constants/participants'; @@ -14,8 +14,10 @@ import { generateMessage } from '../../../tests/generators/generateMessage'; import { generateReplyAllMsgEditor } from '../editor-generators'; describe('Reply All', () => { + const originalFrom = 'someone@test.com'; const meAddress = 'me@test.com'; const sharedAccountAddress = 'sharedAccount@test.com'; + const another = 'another@test.com'; const sendAsIdentityDisplayName = 'Homer Simpson'; const defaultIdentity = { @@ -64,9 +66,7 @@ describe('Reply All', () => { beforeEach(() => { jest.spyOn(shellHooks, 'getUserAccount').mockImplementation(() => mainAccount); }); - - describe('A message sent to me, the sharedAccount and another person', () => { - const originalFrom = 'someone@test.com'; + describe('A message sent To: [me, sharedAccount, another person]', () => { const receivedMessage = { ...generateMessage(), parent: FOLDERS.INBOX, @@ -74,13 +74,12 @@ describe('Reply All', () => { { type: ParticipantRole.FROM, address: originalFrom }, { type: ParticipantRole.TO, address: meAddress }, { type: ParticipantRole.TO, address: sharedAccountAddress }, - { type: ParticipantRole.TO, address: 'another@test.com' } + { type: ParticipantRole.TO, address: another } ] }; it('should reply with default identity (Me)', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); - const defaultIdentity = ''; - expect(replyMsgEditor.identityId).toEqual(defaultIdentity); + expect(replyMsgEditor.identityId).toEqual(''); }); it('should move "To" recipients to CC but exclude myself', () => { @@ -91,7 +90,7 @@ describe('Reply All', () => { type: 'c' }, { - address: 'another@test.com', + address: another, type: 'c' } ]); @@ -106,16 +105,41 @@ describe('Reply All', () => { ]); }); }); + describe('A message sent To: [sharedAccount, another], CC: [me]', () => { + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.INBOX, + participants: [ + { type: ParticipantRole.FROM, address: originalFrom }, + { type: ParticipantRole.CARBON_COPY, address: meAddress }, + { type: ParticipantRole.TO, address: sharedAccountAddress }, + { type: ParticipantRole.TO, address: another } + ] + }; + it('should reply with shared account (To weighs more than CC)', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); + }); + it('should put only me and another in the CC', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.cc).toEqual([ + { + address: another, + type: 'c' + }, + { address: meAddress, type: 'c' } + ]); + }); + }); - describe('A message sent only to the sharedAccount and another person', () => { - const originalFrom = 'someone@test.com'; + describe('A message sent To: [sharedAccount,another]', () => { const receivedMessage = { ...generateMessage(), parent: FOLDERS.INBOX, participants: [ { type: ParticipantRole.FROM, address: originalFrom }, { type: ParticipantRole.TO, address: sharedAccountAddress }, - { type: ParticipantRole.TO, address: 'another@test.com' } + { type: ParticipantRole.TO, address: another } ] }; it('should reply as delegated account', () => { @@ -126,7 +150,7 @@ describe('Reply All', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.recipients.cc).toEqual([ { - address: 'another@test.com', + address: another, type: 'c' } ]); From 09fe50758b02321aa97a12d36a64e87329e9e255 Mon Sep 17 00:00:00 2001 From: Davide Frison Date: Fri, 21 Mar 2025 17:54:31 +0100 Subject: [PATCH 08/12] test: change description of tests --- .../tests/editor-generator-integration.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/store/editor/tests/editor-generator-integration.test.ts b/src/store/editor/tests/editor-generator-integration.test.ts index ac1bdbfd6..e32787c56 100644 --- a/src/store/editor/tests/editor-generator-integration.test.ts +++ b/src/store/editor/tests/editor-generator-integration.test.ts @@ -82,7 +82,7 @@ describe('Reply All', () => { expect(replyMsgEditor.identityId).toEqual(''); }); - it('should move "To" recipients to CC but exclude myself', () => { + it('should reply with CC: [sharedAccount, another person]', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.recipients.cc).toEqual([ { @@ -95,7 +95,7 @@ describe('Reply All', () => { } ]); }); - it('should include only the original From address in the "To" field', () => { + it('should reply To: [original sender]', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.recipients.to).toEqual([ { @@ -105,7 +105,7 @@ describe('Reply All', () => { ]); }); }); - describe('A message sent To: [sharedAccount, another], CC: [me]', () => { + describe('A message sent To: [sharedAccount, another person], CC: [me]', () => { const receivedMessage = { ...generateMessage(), parent: FOLDERS.INBOX, @@ -116,11 +116,11 @@ describe('Reply All', () => { { type: ParticipantRole.TO, address: another } ] }; - it('should reply with shared account (To weighs more than CC)', () => { + it('should reply as shared account (To weighs more than CC)', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); }); - it('should put only me and another in the CC', () => { + it('should reply with CC: [another person, me]', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.recipients.cc).toEqual([ { @@ -132,7 +132,7 @@ describe('Reply All', () => { }); }); - describe('A message sent To: [sharedAccount,another]', () => { + describe('A message sent To: [sharedAccount,another person]', () => { const receivedMessage = { ...generateMessage(), parent: FOLDERS.INBOX, @@ -146,7 +146,7 @@ describe('Reply All', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); }); - it('should move "To" recipients to CC but exclude the sharedAccount', () => { + it('should reply with CC: [another person]', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.recipients.cc).toEqual([ { @@ -155,7 +155,7 @@ describe('Reply All', () => { } ]); }); - it('should include only the original From address in the "To" field', () => { + it('should reply To: [original sender]', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.recipients.to).toEqual([ { From dc53a832bcf2ecb6ec6ddb5eecdce4f50847da8c Mon Sep 17 00:00:00 2001 From: Davide Frison Date: Fri, 21 Mar 2025 17:55:41 +0100 Subject: [PATCH 09/12] test: change description of tests pt.2 --- .../editor-generator-integration.test.ts | 191 +++++++++--------- 1 file changed, 96 insertions(+), 95 deletions(-) diff --git a/src/store/editor/tests/editor-generator-integration.test.ts b/src/store/editor/tests/editor-generator-integration.test.ts index e32787c56..5fde91de9 100644 --- a/src/store/editor/tests/editor-generator-integration.test.ts +++ b/src/store/editor/tests/editor-generator-integration.test.ts @@ -14,7 +14,7 @@ import { generateMessage } from '../../../tests/generators/generateMessage'; import { generateReplyAllMsgEditor } from '../editor-generators'; describe('Reply All', () => { - const originalFrom = 'someone@test.com'; + const originalFrom = 'someoneElse@test.com'; const meAddress = 'me@test.com'; const sharedAccountAddress = 'sharedAccount@test.com'; const another = 'another@test.com'; @@ -66,103 +66,104 @@ describe('Reply All', () => { beforeEach(() => { jest.spyOn(shellHooks, 'getUserAccount').mockImplementation(() => mainAccount); }); - describe('A message sent To: [me, sharedAccount, another person]', () => { - const receivedMessage = { - ...generateMessage(), - parent: FOLDERS.INBOX, - participants: [ - { type: ParticipantRole.FROM, address: originalFrom }, - { type: ParticipantRole.TO, address: meAddress }, - { type: ParticipantRole.TO, address: sharedAccountAddress }, - { type: ParticipantRole.TO, address: another } - ] - }; - it('should reply with default identity (Me)', () => { - const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); - expect(replyMsgEditor.identityId).toEqual(''); - }); - - it('should reply with CC: [sharedAccount, another person]', () => { - const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); - expect(replyMsgEditor.recipients.cc).toEqual([ - { - address: sharedAccountAddress, - type: 'c' - }, - { - address: another, - type: 'c' - } - ]); - }); - it('should reply To: [original sender]', () => { - const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); - expect(replyMsgEditor.recipients.to).toEqual([ - { - address: originalFrom, - type: 't' - } - ]); - }); - }); - describe('A message sent To: [sharedAccount, another person], CC: [me]', () => { - const receivedMessage = { - ...generateMessage(), - parent: FOLDERS.INBOX, - participants: [ - { type: ParticipantRole.FROM, address: originalFrom }, - { type: ParticipantRole.CARBON_COPY, address: meAddress }, - { type: ParticipantRole.TO, address: sharedAccountAddress }, - { type: ParticipantRole.TO, address: another } - ] - }; - it('should reply as shared account (To weighs more than CC)', () => { - const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); - expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); - }); - it('should reply with CC: [another person, me]', () => { - const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); - expect(replyMsgEditor.recipients.cc).toEqual([ - { - address: another, - type: 'c' - }, - { address: meAddress, type: 'c' } - ]); - }); - }); + describe('Messages sent from someoneElse (outsider)', () => { + describe('A message sent To: [me, sharedAccount, another person]', () => { + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.INBOX, + participants: [ + { type: ParticipantRole.FROM, address: originalFrom }, + { type: ParticipantRole.TO, address: meAddress }, + { type: ParticipantRole.TO, address: sharedAccountAddress }, + { type: ParticipantRole.TO, address: another } + ] + }; + it('should reply with default identity (Me)', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.identityId).toEqual(''); + }); - describe('A message sent To: [sharedAccount,another person]', () => { - const receivedMessage = { - ...generateMessage(), - parent: FOLDERS.INBOX, - participants: [ - { type: ParticipantRole.FROM, address: originalFrom }, - { type: ParticipantRole.TO, address: sharedAccountAddress }, - { type: ParticipantRole.TO, address: another } - ] - }; - it('should reply as delegated account', () => { - const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); - expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); + it('should reply with CC: [sharedAccount, another person]', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.cc).toEqual([ + { + address: sharedAccountAddress, + type: 'c' + }, + { + address: another, + type: 'c' + } + ]); + }); + it('should reply To: [original sender]', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.to).toEqual([ + { + address: originalFrom, + type: 't' + } + ]); + }); }); - it('should reply with CC: [another person]', () => { - const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); - expect(replyMsgEditor.recipients.cc).toEqual([ - { - address: another, - type: 'c' - } - ]); + describe('A message sent To: [sharedAccount, another person], CC: [me]', () => { + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.INBOX, + participants: [ + { type: ParticipantRole.FROM, address: originalFrom }, + { type: ParticipantRole.CARBON_COPY, address: meAddress }, + { type: ParticipantRole.TO, address: sharedAccountAddress }, + { type: ParticipantRole.TO, address: another } + ] + }; + it('should reply as shared account (To weighs more than CC)', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); + }); + it('should reply with CC: [another person, me]', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.cc).toEqual([ + { + address: another, + type: 'c' + }, + { address: meAddress, type: 'c' } + ]); + }); }); - it('should reply To: [original sender]', () => { - const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); - expect(replyMsgEditor.recipients.to).toEqual([ - { - address: originalFrom, - type: 't' - } - ]); + describe('A message sent To: [sharedAccount,another person]', () => { + const receivedMessage = { + ...generateMessage(), + parent: FOLDERS.INBOX, + participants: [ + { type: ParticipantRole.FROM, address: originalFrom }, + { type: ParticipantRole.TO, address: sharedAccountAddress }, + { type: ParticipantRole.TO, address: another } + ] + }; + it('should reply as delegated account', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); + }); + it('should reply with CC: [another person]', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.cc).toEqual([ + { + address: another, + type: 'c' + } + ]); + }); + it('should reply To: [original sender]', () => { + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.recipients.to).toEqual([ + { + address: originalFrom, + type: 't' + } + ]); + }); }); }); }); From 187fc868f842213549354f37abbb76b9da080026 Mon Sep 17 00:00:00 2001 From: Keshav Bhatt Date: Mon, 24 Mar 2025 15:13:20 +0530 Subject: [PATCH 10/12] test(editor-generator): update participant role constants and enhance reply all message tests --- .../editor-generator-integration.test.ts | 157 ++++++++++++++++-- 1 file changed, 144 insertions(+), 13 deletions(-) diff --git a/src/store/editor/tests/editor-generator-integration.test.ts b/src/store/editor/tests/editor-generator-integration.test.ts index 5fde91de9..6d25f01eb 100644 --- a/src/store/editor/tests/editor-generator-integration.test.ts +++ b/src/store/editor/tests/editor-generator-integration.test.ts @@ -14,7 +14,7 @@ import { generateMessage } from '../../../tests/generators/generateMessage'; import { generateReplyAllMsgEditor } from '../editor-generators'; describe('Reply All', () => { - const originalFrom = 'someoneElse@test.com'; + const outsider = 'someoneElse@test.com'; const meAddress = 'me@test.com'; const sharedAccountAddress = 'sharedAccount@test.com'; const another = 'another@test.com'; @@ -72,7 +72,7 @@ describe('Reply All', () => { ...generateMessage(), parent: FOLDERS.INBOX, participants: [ - { type: ParticipantRole.FROM, address: originalFrom }, + { type: ParticipantRole.FROM, address: outsider }, { type: ParticipantRole.TO, address: meAddress }, { type: ParticipantRole.TO, address: sharedAccountAddress }, { type: ParticipantRole.TO, address: another } @@ -88,11 +88,11 @@ describe('Reply All', () => { expect(replyMsgEditor.recipients.cc).toEqual([ { address: sharedAccountAddress, - type: 'c' + type: ParticipantRole.CARBON_COPY }, { address: another, - type: 'c' + type: ParticipantRole.CARBON_COPY } ]); }); @@ -100,8 +100,8 @@ describe('Reply All', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.recipients.to).toEqual([ { - address: originalFrom, - type: 't' + address: outsider, + type: ParticipantRole.TO } ]); }); @@ -111,7 +111,7 @@ describe('Reply All', () => { ...generateMessage(), parent: FOLDERS.INBOX, participants: [ - { type: ParticipantRole.FROM, address: originalFrom }, + { type: ParticipantRole.FROM, address: outsider }, { type: ParticipantRole.CARBON_COPY, address: meAddress }, { type: ParticipantRole.TO, address: sharedAccountAddress }, { type: ParticipantRole.TO, address: another } @@ -126,9 +126,9 @@ describe('Reply All', () => { expect(replyMsgEditor.recipients.cc).toEqual([ { address: another, - type: 'c' + type: ParticipantRole.CARBON_COPY }, - { address: meAddress, type: 'c' } + { address: meAddress, type: ParticipantRole.CARBON_COPY } ]); }); }); @@ -137,7 +137,7 @@ describe('Reply All', () => { ...generateMessage(), parent: FOLDERS.INBOX, participants: [ - { type: ParticipantRole.FROM, address: originalFrom }, + { type: ParticipantRole.FROM, address: outsider }, { type: ParticipantRole.TO, address: sharedAccountAddress }, { type: ParticipantRole.TO, address: another } ] @@ -151,7 +151,7 @@ describe('Reply All', () => { expect(replyMsgEditor.recipients.cc).toEqual([ { address: another, - type: 'c' + type: ParticipantRole.CARBON_COPY } ]); }); @@ -159,11 +159,142 @@ describe('Reply All', () => { const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); expect(replyMsgEditor.recipients.to).toEqual([ { - address: originalFrom, - type: 't' + address: outsider, + type: ParticipantRole.TO } ]); }); }); }); + describe('Message sent from Me', () => { + const message = { + ...generateMessage(), + participants: [ + { type: ParticipantRole.FROM, address: meAddress }, + { type: ParticipantRole.TO, address: meAddress } + ] + }; + + it('To [me] should have identity Me when replying to the sent message', () => { + const messageInSent = { ...message, parent: FOLDERS.SENT }; + const replyMsgEditor = generateReplyAllMsgEditor(messageInSent); + expect(replyMsgEditor.identityId).toEqual(''); + }); + + it('To [me] should have identity Me when replying to the received message', () => { + const messageReceived = { ...message, parent: FOLDERS.INBOX }; + const replyMsgEditor = generateReplyAllMsgEditor(messageReceived); + expect(replyMsgEditor.identityId).toEqual(''); + }); + + it('To [me] should have only Me in TO when replying to the received message', () => { + const messageReceived = { ...message, parent: FOLDERS.INBOX }; + const replyMsgEditor = generateReplyAllMsgEditor(messageReceived); + expect(replyMsgEditor.recipients.to).toEqual([ + { + address: meAddress, + type: ParticipantRole.TO + } + ]); + }); + + it('To [me] should have only Me in TO when replying to the sent message', () => { + const messageReceived = { ...message, parent: FOLDERS.SENT }; + const replyMsgEditor = generateReplyAllMsgEditor(messageReceived); + expect(replyMsgEditor.recipients.to).toEqual([ + { + address: meAddress, + type: ParticipantRole.TO + } + ]); + }); + + it('To [me] should have nothing in CC', () => { + const messageReceived = { ...message, parent: FOLDERS.INBOX }; + const replyMsgEditor = generateReplyAllMsgEditor(messageReceived); + expect(replyMsgEditor.recipients.cc).toEqual([]); + }); + + describe('A message sent To: [sharedAccount, another person]', () => { + const messageFromMeToShared = { + ...generateMessage(), + participants: [ + { type: ParticipantRole.FROM, address: meAddress }, + { type: ParticipantRole.TO, address: sharedAccountAddress }, + { type: ParticipantRole.TO, address: another } + ] + }; + it('should reply with shared account identity when replying to the sent message', () => { + const messageInSent = { ...messageFromMeToShared, parent: FOLDERS.SENT }; + const replyMsgEditor = generateReplyAllMsgEditor(messageInSent); + expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); + }); + + it('should reply To: [Me and another] when replying to the sent message', () => { + const messageInSent = { ...messageFromMeToShared, parent: FOLDERS.SENT }; + const replyMsgEditor = generateReplyAllMsgEditor(messageInSent); + expect(replyMsgEditor.recipients.to).toEqual([ + { + address: meAddress, + type: ParticipantRole.TO + }, + { + address: another, + type: ParticipantRole.TO + } + ]); + }); + + it('should reply To: [Me] when replying to the received message', () => { + const messageInSent = { ...messageFromMeToShared, parent: FOLDERS.INBOX }; + const replyMsgEditor = generateReplyAllMsgEditor(messageInSent); + expect(replyMsgEditor.recipients.to).toEqual([ + { + address: meAddress, + type: ParticipantRole.TO + } + ]); + }); + + it('should reply CC: [another] when replying to the received message', () => { + const messageInSent = { ...messageFromMeToShared, parent: FOLDERS.INBOX }; + const replyMsgEditor = generateReplyAllMsgEditor(messageInSent); + expect(replyMsgEditor.recipients.cc).toEqual([ + { + address: another, + type: ParticipantRole.CARBON_COPY + } + ]); + }); + }); + }); + describe('Identity Selection', () => { + it('should use sharedAccount identity when replying to a message with To [sharedAccount] and CC [Me, another]', () => { + const receivedMessage = { + ...generateMessage(), + participants: [ + { type: ParticipantRole.FROM, address: outsider }, + { type: ParticipantRole.TO, address: sharedAccountAddress }, + { type: ParticipantRole.CARBON_COPY, address: meAddress }, + { type: ParticipantRole.CARBON_COPY, address: another } + ] + }; + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.identityId).toEqual(`${sharedAccountAddress}sendAs`); + }); + + it('should use default identity when replying to a message with To [sharedAccount, Me] and CC [another]', () => { + const receivedMessage = { + ...generateMessage(), + participants: [ + { type: ParticipantRole.FROM, address: outsider }, + { type: ParticipantRole.TO, address: sharedAccountAddress }, + { type: ParticipantRole.TO, address: meAddress }, + { type: ParticipantRole.CARBON_COPY, address: another } + ] + }; + const replyMsgEditor = generateReplyAllMsgEditor(receivedMessage); + expect(replyMsgEditor.identityId).toEqual(''); + }); + }); }); From 33cbe4c1d2ce821069e2e3a210e2e640be0f0fab Mon Sep 17 00:00:00 2001 From: Keshav Bhatt Date: Mon, 24 Mar 2025 15:18:27 +0530 Subject: [PATCH 11/12] refactor: rename get_available_addresses to get-available-addresses and update imports --- ...{get_available_addresses.ts => get-available-addresses.ts} | 4 ++-- src/helpers/identities.ts | 2 +- src/store/tests/editor-slice-utils.test.ts | 4 ++-- src/views/app/detail-panel/edit/edit-view.tsx | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename src/helpers/{get_available_addresses.ts => get-available-addresses.ts} (91%) diff --git a/src/helpers/get_available_addresses.ts b/src/helpers/get-available-addresses.ts similarity index 91% rename from src/helpers/get_available_addresses.ts rename to src/helpers/get-available-addresses.ts index c40d99024..818909235 100644 --- a/src/helpers/get_available_addresses.ts +++ b/src/helpers/get-available-addresses.ts @@ -28,7 +28,7 @@ export const getAvailableAddresses = (): Array => { if (settings.attrs.zimbraMailAlias) { if (isArray(settings.attrs.zimbraMailAlias)) { result.push( - ...(settings.attrs.zimbraMailAlias as string[]).map((alias: string) => ({ + ...settings.attrs.zimbraMailAlias.map((alias: string) => ({ address: alias, type: 'alias', ownerAccount: account?.name ?? NO_ACCOUNT_NAME @@ -36,7 +36,7 @@ export const getAvailableAddresses = (): Array => { ); } else { result.push({ - address: settings.attrs.zimbraMailAlias as string, + address: settings.attrs.zimbraMailAlias, type: 'alias', ownerAccount: account?.name ?? NO_ACCOUNT_NAME }); diff --git a/src/helpers/identities.ts b/src/helpers/identities.ts index 01f5cd74f..82099d8bc 100644 --- a/src/helpers/identities.ts +++ b/src/helpers/identities.ts @@ -13,7 +13,7 @@ import type { Folders } from '../carbonio-ui-commons/types/folder'; import { NO_ACCOUNT_NAME } from '../constants'; import type { MailMessage, Participant } from '../types'; import { getFolderIdParts, getMessageOwnerAccountName } from './folders'; -import { getAvailableAddresses } from './get_available_addresses'; +import { getAvailableAddresses } from './get-available-addresses'; /** * The name of the primary identity diff --git a/src/store/tests/editor-slice-utils.test.ts b/src/store/tests/editor-slice-utils.test.ts index 18d154bbd..962b2552d 100644 --- a/src/store/tests/editor-slice-utils.test.ts +++ b/src/store/tests/editor-slice-utils.test.ts @@ -10,11 +10,11 @@ import { FOLDERS } from '../../carbonio-ui-commons/constants/folders'; import { ParticipantRole } from '../../carbonio-ui-commons/constants/participants'; import { generateAccount } from '../../carbonio-ui-commons/test/mocks/accounts/account-generator'; import { AvailableAddress } from '../../carbonio-ui-commons/types/identities'; -import { getAvailableAddresses } from '../../helpers/get_available_addresses'; +import { getAvailableAddresses } from '../../helpers/get-available-addresses'; import { generateMessage } from '../../tests/generators/generateMessage'; import { retrieveALL, retrieveCC, retrieveReplyTo } from '../editor-slice-utils'; -jest.mock('../../helpers/get_available_addresses', () => ({ +jest.mock('../../helpers/get-available-addresses', () => ({ getAvailableAddresses: jest.fn() })); diff --git a/src/views/app/detail-panel/edit/edit-view.tsx b/src/views/app/detail-panel/edit/edit-view.tsx index 5b8a82a3f..ca574d739 100644 --- a/src/views/app/detail-panel/edit/edit-view.tsx +++ b/src/views/app/detail-panel/edit/edit-view.tsx @@ -41,7 +41,7 @@ import { checkPersonalCertificateExist } from '../../../../api/check-personal-ce import { GapContainer, GapRow } from '../../../../commons/gap-container'; import { EDIT_VIEW_CLOSING_REASONS, EditViewActions, TIMEOUTS } from '../../../../constants'; import { buildArrayFromFileList } from '../../../../helpers/files'; -import { getAvailableAddresses } from '../../../../helpers/get_available_addresses'; +import { getAvailableAddresses } from '../../../../helpers/get-available-addresses'; import { getIdentitiesDescriptors, getIdentityDescriptor } from '../../../../helpers/identities'; import { useCertificatesStore, From d2196ba8dfc64e33dfdcc4d9a7abe03ba170b72f Mon Sep 17 00:00:00 2001 From: Keshav Bhatt Date: Mon, 24 Mar 2025 15:44:28 +0530 Subject: [PATCH 12/12] feat: add unit tests for getAvailableAddresses function and improve documentation --- src/helpers/get-available-addresses.ts | 7 +- .../tests/get-available-addresses.test.ts | 175 ++++++++++++++++++ src/helpers/tests/identities.test.ts | 2 +- 3 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 src/helpers/tests/get-available-addresses.test.ts diff --git a/src/helpers/get-available-addresses.ts b/src/helpers/get-available-addresses.ts index 818909235..2c1884e8b 100644 --- a/src/helpers/get-available-addresses.ts +++ b/src/helpers/get-available-addresses.ts @@ -10,7 +10,12 @@ import { AvailableAddress } from '../carbonio-ui-commons/types/identities'; import { NO_ACCOUNT_NAME } from '../constants'; /** - * Returns the list of all the available addresses for the account and their type + * Retrieves the available email addresses for the user, including: + * - Primary account email + * - Email aliases + * - Delegated email addresses (with `sendAs` or `sendOnBehalfOf` rights) + * + * @returns {Array} An array of available email addresses with their types and owner accounts. */ export const getAvailableAddresses = (): Array => { const account = getUserAccount(); diff --git a/src/helpers/tests/get-available-addresses.test.ts b/src/helpers/tests/get-available-addresses.test.ts new file mode 100644 index 000000000..85424a0bc --- /dev/null +++ b/src/helpers/tests/get-available-addresses.test.ts @@ -0,0 +1,175 @@ +/* + * SPDX-FileCopyrightText: 2025 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { getUserAccount, getUserSettings } from '@zextras/carbonio-shell-ui'; + +import { NO_ACCOUNT_NAME } from '../../constants'; +import { getAvailableAddresses } from '../get-available-addresses'; + +jest.mock('@zextras/carbonio-shell-ui', () => ({ + getUserAccount: jest.fn(), + getUserSettings: jest.fn() +})); + +describe('getAvailableAddresses', () => { + const primaryAccountAddress = 'primary@example.com'; + it('should return primary account address when defined', () => { + (getUserAccount as jest.Mock).mockReturnValue({ name: primaryAccountAddress }); + (getUserSettings as jest.Mock).mockReturnValue({ attrs: {} }); + + const result = getAvailableAddresses(); + + expect(result).toEqual([ + { address: primaryAccountAddress, type: 'primary', ownerAccount: primaryAccountAddress } + ]); + }); + + it('should return primary account address with no account name(NO_ACCOUNT_NAME) when account is null', () => { + (getUserAccount as jest.Mock).mockReturnValue(null); + (getUserSettings as jest.Mock).mockReturnValue({ attrs: {} }); + + const result = getAvailableAddresses(); + + expect(result).toEqual([ + { address: NO_ACCOUNT_NAME, type: 'primary', ownerAccount: NO_ACCOUNT_NAME } + ]); + }); + + it('should return primary account address and aliases when they are defined in zimbraMailAlias', () => { + (getUserAccount as jest.Mock).mockReturnValue({ name: primaryAccountAddress }); + (getUserSettings as jest.Mock).mockReturnValue({ + attrs: { zimbraMailAlias: ['alias1@example.com', 'alias2@example.com'] } + }); + + const result = getAvailableAddresses(); + + expect(result).toEqual([ + { address: primaryAccountAddress, type: 'primary', ownerAccount: primaryAccountAddress }, + { address: 'alias1@example.com', type: 'alias', ownerAccount: primaryAccountAddress }, + { address: 'alias2@example.com', type: 'alias', ownerAccount: primaryAccountAddress } + ]); + }); + + it('should return primary account address and single alias when only one is defined in zimbraMailAlias', () => { + (getUserAccount as jest.Mock).mockReturnValue({ name: primaryAccountAddress }); + (getUserSettings as jest.Mock).mockReturnValue({ + attrs: { zimbraMailAlias: 'alias@example.com' } + }); + + const result = getAvailableAddresses(); + + expect(result).toEqual([ + { address: primaryAccountAddress, type: 'primary', ownerAccount: primaryAccountAddress }, + { address: 'alias@example.com', type: 'alias', ownerAccount: primaryAccountAddress } + ]); + }); + + it('should return primary account address and delegation addresses when the delegation rights are defined', () => { + (getUserAccount as jest.Mock).mockReturnValue({ + name: primaryAccountAddress, + rights: { + targets: [ + { + right: 'sendAs', + target: [{ type: 'account', email: [{ addr: 'delegation1@example.com' }] }] + }, + { + right: 'sendOnBehalfOf', + target: [{ type: 'account', email: [{ addr: 'delegation2@example.com' }] }] + } + ] + } + }); + (getUserSettings as jest.Mock).mockReturnValue({ attrs: {} }); + + const result = getAvailableAddresses(); + + expect(result).toEqual([ + { address: primaryAccountAddress, type: 'primary', ownerAccount: primaryAccountAddress }, + { + address: 'delegation1@example.com', + type: 'delegation', + right: 'sendAs', + ownerAccount: 'delegation1@example.com' + }, + { + address: 'delegation2@example.com', + type: 'delegation', + right: 'sendOnBehalfOf', + ownerAccount: 'delegation2@example.com' + } + ]); + }); + + it('should return primary account address, aliases, and delegation addresses', () => { + (getUserAccount as jest.Mock).mockReturnValue({ + name: primaryAccountAddress, + rights: { + targets: [ + { + right: 'sendAs', + target: [ + { type: 'account', email: [{ addr: 'delegation1@example.com' }] }, + { type: 'account', email: [{ addr: 'delegation2@example.com' }] } + ] + } + ] + } + }); + (getUserSettings as jest.Mock).mockReturnValue({ + attrs: { zimbraMailAlias: ['alias1@example.com', 'alias2@example.com'] } + }); + + const result = getAvailableAddresses(); + + expect(result).toEqual([ + { address: primaryAccountAddress, type: 'primary', ownerAccount: primaryAccountAddress }, + { address: 'alias1@example.com', type: 'alias', ownerAccount: primaryAccountAddress }, + { address: 'alias2@example.com', type: 'alias', ownerAccount: primaryAccountAddress }, + { + address: 'delegation1@example.com', + type: 'delegation', + right: 'sendAs', + ownerAccount: 'delegation1@example.com' + }, + { + address: 'delegation2@example.com', + type: 'delegation', + right: 'sendAs', + ownerAccount: 'delegation2@example.com' + } + ]); + }); + + it('should return primary account address and no delegation addresses when the delegation rights are different then sendAs and sendOnBehalfOf', () => { + (getUserAccount as jest.Mock).mockReturnValue({ + name: primaryAccountAddress, + rights: { + targets: [ + { + right: 'sendAsDistList', + target: [{ type: 'account', email: [{ addr: 'delegation1@example.com' }] }] + }, + { + right: 'viewFreeBusy', + target: [{ type: 'account', email: [{ addr: 'delegation2@example.com' }] }] + }, + { + right: 'sendOnBehalfOfDistList', + target: [{ type: 'account', email: [{ addr: 'delegation3@example.com' }] }] + } + ] + } + }); + (getUserSettings as jest.Mock).mockReturnValue({ attrs: {} }); + + const result = getAvailableAddresses(); + + expect(result).toEqual([ + { address: primaryAccountAddress, type: 'primary', ownerAccount: primaryAccountAddress } + ]); + }); +}); diff --git a/src/helpers/tests/identities.test.ts b/src/helpers/tests/identities.test.ts index 0c39e946b..a10866504 100644 --- a/src/helpers/tests/identities.test.ts +++ b/src/helpers/tests/identities.test.ts @@ -7,7 +7,7 @@ import { faker } from '@faker-js/faker'; import { FOLDERS } from '../../carbonio-ui-commons/constants/folders'; import { ParticipantRole } from '../../carbonio-ui-commons/constants/participants'; -import { getRootsMap } from '../../carbonio-ui-commons/store/zustand/folder/hooks'; +import { getRootsMap } from '../../carbonio-ui-commons/store/zustand/folder'; import { populateFoldersStore } from '../../carbonio-ui-commons/test/mocks/store/folders'; import { getMocksContext } from '../../carbonio-ui-commons/test/mocks/utils/mocks-context'; import { generateMessage } from '../../tests/generators/generateMessage';