From d6e742e2e1d80c674834c4501fd79e5fc171a1a6 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 2 Apr 2026 21:59:44 +0700 Subject: [PATCH 1/4] refactor handleMoneyRequestStepDistanceNavigation to use concirgeReportID from useOnyx --- .../MovedTransactionAction.tsx | 3 +- src/libs/ReportNameUtils.ts | 5 +- src/libs/ReportUtils.ts | 3 +- src/libs/SidebarUtils.ts | 2 +- src/libs/actions/IOU/MoneyRequest.ts | 8 +- .../report/ContextMenu/ContextMenuActions.tsx | 2 +- .../request/step/IOURequestStepDistance.tsx | 3 + .../index.native.tsx | 2 + .../step/IOURequestStepDistanceManual.tsx | 3 + .../step/IOURequestStepDistanceMap.tsx | 3 + .../step/IOURequestStepDistanceOdometer.tsx | 2 + .../hooks/useReceiptScan.ts | 14 ++- tests/actions/IOU/MoneyRequestTest.ts | 96 ++++++++++++++++++- tests/unit/OptionsListUtilsTest.tsx | 2 +- 14 files changed, 132 insertions(+), 16 deletions(-) diff --git a/src/components/ReportActionItem/MovedTransactionAction.tsx b/src/components/ReportActionItem/MovedTransactionAction.tsx index c8fa0f60446e7..7f55834aa67dc 100644 --- a/src/components/ReportActionItem/MovedTransactionAction.tsx +++ b/src/components/ReportActionItem/MovedTransactionAction.tsx @@ -34,6 +34,7 @@ function MovedTransactionAction({action, emptyHTML, childReport, originalReport} const [toReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${toReportID}`); const [fromReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isPendingDelete = fromReport?.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; // When the transaction is moved from personal space (unreported), fromReportID will be "0" which doesn't exist in allReports @@ -46,7 +47,7 @@ function MovedTransactionAction({action, emptyHTML, childReport, originalReport} return emptyHTML; } - const message = getMovedTransactionMessage(translate, action); + const message = getMovedTransactionMessage(translate, action, conciergeReportID); if (hasReasoning(action)) { return ( diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index 64b80d7a7af09..75830c5fce4b5 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -415,8 +415,7 @@ function computeReportNameBasedOnReportAction( report: Report | undefined, reportPolicy: Policy | undefined, parentReport: Report | undefined, - // TODO: Make this required when https://github.com/Expensify/App/issues/66411 is done - conciergeReportID?: string, + conciergeReportID: string | undefined, ): string | undefined { if (!parentReportAction) { return undefined; @@ -500,7 +499,7 @@ function computeReportNameBasedOnReportAction( } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION)) { - return Parser.htmlToText(getMovedTransactionMessage(translate, parentReportAction)); + return Parser.htmlToText(getMovedTransactionMessage(translate, parentReportAction, conciergeReportID)); } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_MAX_EXPENSE_AMOUNT)) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 82f6a0269e37a..1aa4fafd55db2 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -7038,8 +7038,7 @@ function getDeletedTransactionMessage(translate: LocalizedTranslate, action: Rep return message; } -// TODO: conciergeReportID will be required eventually. Refactor issue: https://github.com/Expensify/App/issues/66411 -function getMovedTransactionMessage(translate: LocalizedTranslate, action: ReportAction, conciergeReportID?: string) { +function getMovedTransactionMessage(translate: LocalizedTranslate, action: ReportAction, conciergeReportID: string | undefined) { const movedTransactionOriginalMessage = getOriginalMessage(action) ?? {}; const {toReportID, fromReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 37957f85c1a6e..7a7e9534dfc43 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1131,7 +1131,7 @@ function getOptionData({ } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_OWNERSHIP) { result.alternateText = Parser.htmlToText(getUpdatedOwnershipMessage(translate, lastAction, policy)); } else if (isActionOfType(lastAction, CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION)) { - result.alternateText = Parser.htmlToText(getMovedTransactionMessage(translate, lastAction)); + result.alternateText = Parser.htmlToText(getMovedTransactionMessage(translate, lastAction, conciergeReportID)); } else if (isActionOfType(lastAction, CONST.REPORT.ACTIONS.TYPE.SETTLEMENT_ACCOUNT_LOCKED)) { result.alternateText = Parser.htmlToText(getSettlementAccountLockedMessage(translate, lastAction)); } else if (lastAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && lastActorDisplayName && lastMessageTextFromReport) { diff --git a/src/libs/actions/IOU/MoneyRequest.ts b/src/libs/actions/IOU/MoneyRequest.ts index 5063a95348837..30fbbed220038 100644 --- a/src/libs/actions/IOU/MoneyRequest.ts +++ b/src/libs/actions/IOU/MoneyRequest.ts @@ -178,6 +178,7 @@ type MoneyRequestStepDistanceNavigationParams = { amountOwed: OnyxEntry; userBillingGracePeriodEnds: OnyxCollection; ownerBillingGracePeriodEnd?: OnyxEntry; + conciergeReportID: string | undefined; }; function createTransaction({ @@ -291,6 +292,7 @@ function getMoneyRequestParticipantOptions( report: OnyxEntry, policy: OnyxEntry, personalDetails: OnyxEntry, + conciergeReportID: string | undefined, privateIsArchived?: string, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], ): Array { @@ -299,8 +301,7 @@ function getMoneyRequestParticipantOptions( const participantAccountID = participant?.accountID ?? CONST.DEFAULT_NUMBER_ID; return participantAccountID ? getParticipantsOption(participant, personalDetails) - : // TODO: We'll pass the conciergeReportID in the next PR. Refactor issue: https://github.com/Expensify/App/issues/66411 - getReportOption(participant, privateIsArchived, policy, personalDetails, undefined, reportAttributesDerived); + : getReportOption(participant, privateIsArchived, policy, personalDetails, conciergeReportID, reportAttributesDerived); }); } @@ -599,6 +600,7 @@ function handleMoneyRequestStepDistanceNavigation({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }: MoneyRequestStepDistanceNavigationParams) { const isManualDistance = manualDistance !== undefined; const isOdometerDistance = odometerDistance !== undefined; @@ -621,7 +623,7 @@ function handleMoneyRequestStepDistanceNavigation({ // to the confirm step. // If the user started this flow using the Create expense option (combined submit/track flow), they should be redirected to the participants page. if (report?.reportID && !isArchivedExpenseReport && iouType !== CONST.IOU.TYPE.CREATE) { - const participants = getMoneyRequestParticipantOptions(currentUserAccountID, report, policy, personalDetails, privateIsArchived, reportAttributesDerived); + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, report, policy, personalDetails, conciergeReportID, privateIsArchived, reportAttributesDerived); setDistanceRequestData?.(participants); if (shouldSkipConfirmation) { diff --git a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx index 3753fa96f4293..5117668139b01 100644 --- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx @@ -1112,7 +1112,7 @@ const ContextMenuActions: ContextMenuAction[] = [ } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.TAKE_CONTROL) || isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.REROUTE)) { setClipboardMessage(getChangedApproverActionMessage(translate, reportAction)); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION)) { - setClipboardMessage(getMovedTransactionMessage(translate, reportAction)); + setClipboardMessage(getMovedTransactionMessage(translate, reportAction, conciergeReportID)); } else if (isMovedAction(reportAction)) { setClipboardMessage(getMovedActionMessage(translate, reportAction, originalReport)); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_CARD_FRAUD_ALERT)) { diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 29a7df183a2ee..45f2392b7684a 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -108,6 +108,7 @@ function IOURequestStepDistance({ const [betas] = useOnyx(ONYXKEYS.BETAS); const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isEditing = action === CONST.IOU.ACTION.EDIT; const isEditingSplit = (iouType === CONST.IOU.TYPE.SPLIT || iouType === CONST.IOU.TYPE.SPLIT_EXPENSE) && isEditing; @@ -343,6 +344,7 @@ function IOURequestStepDistance({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }, [ iouType, @@ -382,6 +384,7 @@ function IOURequestStepDistance({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, ]); const getError = () => { diff --git a/src/pages/iou/request/step/IOURequestStepDistanceGPS/index.native.tsx b/src/pages/iou/request/step/IOURequestStepDistanceGPS/index.native.tsx index 8dcb914420983..3ac2e06f9c0f2 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceGPS/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceGPS/index.native.tsx @@ -61,6 +61,7 @@ function IOURequestStepDistanceGPS({ const {policyForMovingExpenses} = usePolicyForMovingExpenses(); const [betas] = useOnyx(ONYXKEYS.BETAS); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isEditing = action === CONST.IOU.ACTION.EDIT; const isCreatingNewRequest = !isEditing; // eslint-disable-next-line rulesdir/no-negated-variables @@ -135,6 +136,7 @@ function IOURequestStepDistanceGPS({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }; diff --git a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx index d0fc0ec1a1b6d..9428e340d9a9d 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx @@ -98,6 +98,7 @@ function IOURequestStepDistanceManual({ const [betas] = useOnyx(ONYXKEYS.BETAS); const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`); @@ -252,6 +253,7 @@ function IOURequestStepDistanceManual({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }, [ @@ -300,6 +302,7 @@ function IOURequestStepDistanceManual({ isSelfTourViewed, amountOwed, ownerBillingGracePeriodEnd, + conciergeReportID, ], ); diff --git a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx index c65efd968f8fc..32d4c83ece534 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx @@ -103,6 +103,7 @@ function IOURequestStepDistanceMap({ const [policyRecentlyUsedCurrencies] = useOnyx(ONYXKEYS.RECENTLY_USED_CURRENCIES); const [betas] = useOnyx(ONYXKEYS.BETAS); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isEditing = action === CONST.IOU.ACTION.EDIT; const isEditingSplit = (iouType === CONST.IOU.TYPE.SPLIT || iouType === CONST.IOU.TYPE.SPLIT_EXPENSE) && isEditing; const currentTransaction = isEditingSplit && !isEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction; @@ -335,6 +336,7 @@ function IOURequestStepDistanceMap({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }, [ iouType, @@ -374,6 +376,7 @@ function IOURequestStepDistanceMap({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, ]); const getError = () => { diff --git a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx index 4195488a12c36..e1fafd6ca2272 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx @@ -124,6 +124,7 @@ function IOURequestStepDistanceOdometer({ const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const [selectedTab, selectedTabResult] = useOnyx(`${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.DISTANCE_REQUEST_TYPE}`); const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isLoadingSelectedTab = isLoadingOnyxValue(selectedTabResult); // isEditing: we're changing an already existing odometer expense; isEditingConfirmation: we navigated here by pressing 'Distance' field from the confirmation step during the creation of a new odometer expense to adjust the input before submitting @@ -506,6 +507,7 @@ function IOURequestStepDistanceOdometer({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }; diff --git a/src/pages/iou/request/step/IOURequestStepScan/hooks/useReceiptScan.ts b/src/pages/iou/request/step/IOURequestStepScan/hooks/useReceiptScan.ts index 435a60a97f6a8..628ba0d9b3d48 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/hooks/useReceiptScan.ts +++ b/src/pages/iou/request/step/IOURequestStepScan/hooks/useReceiptScan.ts @@ -100,10 +100,20 @@ function useReceiptScan({ }, [isMultiScanEnabled]); const [recentWaypoints] = useOnyx(ONYXKEYS.NVP_RECENT_WAYPOINTS); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const participants = useMemo( - () => getMoneyRequestParticipantOptions(currentUserPersonalDetails.accountID, report, policy, personalDetails, reportNameValuePairs?.private_isArchived, reportAttributesDerived), - [currentUserPersonalDetails.accountID, report, policy, personalDetails, reportNameValuePairs?.private_isArchived, reportAttributesDerived], + () => + getMoneyRequestParticipantOptions( + currentUserPersonalDetails.accountID, + report, + policy, + personalDetails, + conciergeReportID, + reportNameValuePairs?.private_isArchived, + reportAttributesDerived, + ), + [currentUserPersonalDetails.accountID, report, policy, personalDetails, conciergeReportID, reportNameValuePairs?.private_isArchived, reportAttributesDerived], ); const participantsPolicyTags = useParticipantsPolicyTags(participants); diff --git a/tests/actions/IOU/MoneyRequestTest.ts b/tests/actions/IOU/MoneyRequestTest.ts index e9214fa7987dc..8899f8224d1ef 100644 --- a/tests/actions/IOU/MoneyRequestTest.ts +++ b/tests/actions/IOU/MoneyRequestTest.ts @@ -449,7 +449,14 @@ describe('MoneyRequest', () => { }, }); baseParams.recentWaypoints = (await getOnyxValue(ONYXKEYS.NVP_RECENT_WAYPOINTS)) ?? []; - baseParams.participants = getMoneyRequestParticipantOptions(baseParams.currentUserAccountID, baseParams.report, baseParams.policy, baseParams.personalDetails, undefined, {}); + baseParams.participants = getMoneyRequestParticipantOptions( + baseParams.currentUserAccountID, + baseParams.report, + baseParams.policy, + baseParams.personalDetails, + undefined, + undefined, + ); await getOnyxData({ key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}`, waitForCollectionCallback: true, @@ -568,7 +575,7 @@ describe('MoneyRequest', () => { ...fakeReport, chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, }; - baseParams.participants = getMoneyRequestParticipantOptions(baseParams.currentUserAccountID, report, baseParams.policy, baseParams.personalDetails, undefined, {}); + baseParams.participants = getMoneyRequestParticipantOptions(baseParams.currentUserAccountID, report, baseParams.policy, baseParams.personalDetails, undefined, undefined); await getOnyxData({ key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}`, @@ -941,6 +948,7 @@ describe('MoneyRequest', () => { amountOwed: 0, draftTransactionIDs: undefined, userBillingGracePeriodEnds: undefined, + conciergeReportID: undefined, }; const splitShares: SplitShares = { [firstSplitParticipantID]: { @@ -1332,6 +1340,90 @@ describe('MoneyRequest', () => { expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(CONST.IOU.TYPE.CREATE, baseParams.transactionID, baseParams.reportID)); }); + + it('should pass conciergeReportID through to getMoneyRequestParticipantOptions when report exists', async () => { + const conciergeReportID = 'concierge789'; + handleMoneyRequestStepDistanceNavigation({ + ...baseParams, + iouType: CONST.IOU.TYPE.SUBMIT, + shouldSkipConfirmation: false, + isArchivedExpenseReport: false, + draftTransactionIDs: [baseParams.transactionID], + conciergeReportID, + }); + + // When report exists and iouType is not CREATE, the function calls getMoneyRequestParticipantOptions + // with conciergeReportID, sets distance request data, and then navigates to confirmation page + await waitForBatchedUpdates(); + expect(baseParams.setDistanceRequestData).toHaveBeenCalled(); + }); + + it('should work correctly when conciergeReportID is undefined', async () => { + handleMoneyRequestStepDistanceNavigation({ + ...baseParams, + iouType: CONST.IOU.TYPE.SUBMIT, + shouldSkipConfirmation: false, + isArchivedExpenseReport: false, + draftTransactionIDs: [baseParams.transactionID], + conciergeReportID: undefined, + }); + + await waitForBatchedUpdates(); + expect(baseParams.setDistanceRequestData).toHaveBeenCalled(); + }); + }); + + describe('getMoneyRequestParticipantOptions', () => { + const fakeReport = createRandomReport(1, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT); + const fakePolicy = createRandomPolicy(1, CONST.POLICY.TYPE.TEAM); + + beforeEach(async () => { + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`, { + ...fakeReport, + participants: { + [TEST_USER_ACCOUNT_ID]: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, + role: CONST.REPORT.ROLE.MEMBER, + }, + }, + }); + }); + + afterEach(async () => { + await Onyx.clear(); + }); + + it('should return participants when conciergeReportID is undefined', () => { + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, undefined); + expect(Array.isArray(participants)).toBe(true); + }); + + it('should return participants when conciergeReportID is provided', () => { + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, 'concierge123'); + expect(Array.isArray(participants)).toBe(true); + }); + + it('should pass conciergeReportID through to getReportOption for policy expense chat participants', () => { + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, 'concierge456'); + // For policy expense chat, participants have accountID 0 and go through getReportOption + // which uses conciergeReportID for identifying concierge chat + expect(Array.isArray(participants)).toBe(true); + expect(participants.length).toBeGreaterThan(0); + }); + + it('should return participants with privateIsArchived passed through', () => { + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, undefined, 'archived'); + expect(Array.isArray(participants)).toBe(true); + }); + + it('should return participants for report with no chat participants (DM-like)', () => { + const dmReport = { + ...createRandomReport(2, undefined), + participants: {}, + }; + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, dmReport, fakePolicy, {}, undefined); + expect(Array.isArray(participants)).toBe(true); + }); }); describe('shouldUseDefaultExpensePolicy', () => { diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 68ccca660fcd3..65deb7ae968ef 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -4088,7 +4088,7 @@ describe('OptionsListUtils', () => { currentUserLogin: CURRENT_USER_EMAIL, }); - expect(lastMessage).toBe(Parser.htmlToText(getMovedTransactionMessage(translateLocal, movedTransactionAction))); + expect(lastMessage).toBe(Parser.htmlToText(getMovedTransactionMessage(translateLocal, movedTransactionAction, undefined))); }); describe('SUBMITTED action', () => { it('should return automatic submitted message if submitted via harvesting', async () => { From b4d97182eef24b76ba628eab38d35ccff5b820fc Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 3 Apr 2026 11:21:38 +0700 Subject: [PATCH 2/4] add test --- tests/unit/ReportUtilsTest.ts | 130 ++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 30e79cc4b489c..955e2edbad75f 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -79,6 +79,7 @@ import { getChatListItemReportName, getChatRoomSubtitle, getDefaultWorkspaceAvatar, + getDeletedTransactionMessage, getDisplayNameForParticipant, getDisplayNamesWithTooltips, getHarvestOriginalReportID, @@ -87,6 +88,8 @@ import { getIOUReportActionDisplayMessage, getLinkedIOUTransaction, getMostRecentlyVisitedReport, + getMovedActionMessage, + getMovedTransactionMessage, getOriginalReportID, getOutstandingChildRequest, getParentNavigationSubtitle, @@ -15832,4 +15835,131 @@ describe('ReportUtils', () => { expect(action.reportActionID).toBeTruthy(); }); }); + + describe('getDeletedTransactionMessage', () => { + it('should return a formatted deleted transaction message', () => { + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.DELETED_TRANSACTION, + originalMessage: { + amount: -5000, + currency: 'USD', + merchant: 'Starbucks', + }, + } as unknown as ReportAction; + + const result = getDeletedTransactionMessage(translateLocal, action); + expect(typeof result).toBe('string'); + expect(result.length).toBeGreaterThan(0); + }); + + it('should handle missing amount and currency gracefully', () => { + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.DELETED_TRANSACTION, + originalMessage: {}, + } as unknown as ReportAction; + + const result = getDeletedTransactionMessage(translateLocal, action); + expect(typeof result).toBe('string'); + }); + }); + + describe('getMovedTransactionMessage', () => { + it('should return movedTransactionTo message when fromReportID is undefined', async () => { + const toReport = LHNTestUtils.getFakeReport(); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${toReport.reportID}`, toReport); + + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, + originalMessage: { + toReportID: toReport.reportID, + }, + } as unknown as ReportAction; + + const result = getMovedTransactionMessage(translateLocal, action, undefined); + expect(typeof result).toBe('string'); + expect(result.length).toBeGreaterThan(0); + }); + + it('should return movedTransactionFrom message when fromReportID is defined', async () => { + const fromReport = LHNTestUtils.getFakeReport(); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${fromReport.reportID}`, fromReport); + + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, + originalMessage: { + fromReportID: fromReport.reportID, + toReportID: '99999', + }, + } as unknown as ReportAction; + + const result = getMovedTransactionMessage(translateLocal, action, undefined); + expect(typeof result).toBe('string'); + expect(result.length).toBeGreaterThan(0); + }); + + it('should handle conciergeReportID parameter', async () => { + const toReport = LHNTestUtils.getFakeReport(); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${toReport.reportID}`, toReport); + + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, + originalMessage: { + toReportID: toReport.reportID, + }, + } as unknown as ReportAction; + + const result = getMovedTransactionMessage(translateLocal, action, '12345'); + expect(typeof result).toBe('string'); + }); + }); + + describe('getMovedActionMessage', () => { + it('should return empty string for non-moved action', () => { + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, + } as unknown as ReportAction; + + const report = LHNTestUtils.getFakeReport(); + const result = getMovedActionMessage(translateLocal, action, report); + expect(result).toBe(''); + }); + + it('should return empty string when original message is missing', () => { + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED, + originalMessage: undefined, + } as unknown as ReportAction; + + const report = LHNTestUtils.getFakeReport(); + const result = getMovedActionMessage(translateLocal, action, report); + expect(result).toBe(''); + }); + + it('should return moved action message with policy name', async () => { + const policy = createRandomPolicy(0); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); + + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED, + originalMessage: { + toPolicyID: policy.id, + newParentReportID: '11111', + movedReportID: '22222', + }, + } as unknown as ReportAction; + + const report = LHNTestUtils.getFakeReport(); + const result = getMovedActionMessage(translateLocal, action, report); + expect(typeof result).toBe('string'); + expect(result.length).toBeGreaterThan(0); + }); + }); }); From 82f13579b4b1d35bfebf2e857e3bff88fa2dced6 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 3 Apr 2026 11:57:46 +0700 Subject: [PATCH 3/4] lint fix --- tests/unit/ReportUtilsTest.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 955e2edbad75f..69d7f4a88f535 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -15943,14 +15943,14 @@ describe('ReportUtils', () => { }); it('should return moved action message with policy name', async () => { - const policy = createRandomPolicy(0); - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); + const testPolicy = createRandomPolicy(0); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${testPolicy.id}`, testPolicy); const action = { ...LHNTestUtils.getFakeReportAction(), actionName: CONST.REPORT.ACTIONS.TYPE.MOVED, originalMessage: { - toPolicyID: policy.id, + toPolicyID: testPolicy.id, newParentReportID: '11111', movedReportID: '22222', }, From 43e4cc7938dd6ad83d1d5782217c716a69fc1ff5 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 7 Apr 2026 09:11:31 +0700 Subject: [PATCH 4/4] ts fix --- tests/actions/IOU/MoneyRequestTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/actions/IOU/MoneyRequestTest.ts b/tests/actions/IOU/MoneyRequestTest.ts index c4d0ed737457b..b3b48f4055004 100644 --- a/tests/actions/IOU/MoneyRequestTest.ts +++ b/tests/actions/IOU/MoneyRequestTest.ts @@ -1451,7 +1451,7 @@ describe('MoneyRequest', () => { }); it('should return participants with privateIsArchived passed through', () => { - const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, undefined, 'archived'); + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, undefined, true); expect(Array.isArray(participants)).toBe(true); });