From d4a06cdae937bfe9593a9f67e19278310fe42fbc Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Tue, 21 Jul 2026 16:09:47 +0700 Subject: [PATCH 1/2] Refactor getReportOption and related functions to include translate parameter for improved localization support across various components. --- src/libs/IOUAmountSubmission.ts | 12 +- src/libs/OptionsListUtils/index.ts | 32 ++--- src/libs/actions/IOU/MoneyRequest.ts | 2 +- src/pages/Share/ShareDetailsPage.tsx | 4 +- src/pages/Share/SubmitDetailsPage.tsx | 12 +- src/pages/iou/DynamicSplitBillDetailsPage.tsx | 2 +- .../request/MoneyRequestAttendeeSelector.tsx | 10 +- .../iou/request/ParticipantSearchResults.tsx | 10 +- .../iou/request/step/IOURequestStepAmount.tsx | 12 +- .../step/IOURequestStepConfirmation.tsx | 1 + tests/unit/OptionsListUtilsTest.tsx | 119 +++++++++++------- 11 files changed, 145 insertions(+), 71 deletions(-) diff --git a/src/libs/IOUAmountSubmission.ts b/src/libs/IOUAmountSubmission.ts index c9fec5ad3ddf..312f7057c0da 100644 --- a/src/libs/IOUAmountSubmission.ts +++ b/src/libs/IOUAmountSubmission.ts @@ -228,7 +228,17 @@ function buildReportParticipants(args: SubmitAmountArgs) { const privateIsArchived = !!allReportNVPs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${participant.reportID}`]?.private_isArchived; return participantAccountID ? getParticipantsOption(participant, allPersonalDetails, translate) - : getReportOption(participant, privateIsArchived, policy, allPersonalDetails, conciergeReportID, reportAttributesReports, reportDraft, currentUserPersonalDetails.accountID); + : getReportOption( + participant, + privateIsArchived, + policy, + allPersonalDetails, + conciergeReportID, + reportAttributesReports, + reportDraft, + currentUserPersonalDetails.accountID, + translate, + ); }); } diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 8dad17e3bd94..a1191ac8e1e5 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -1268,8 +1268,8 @@ function getReportOption( reportAttributesDerived: ReportAttributesDerivedValue['reports'] | undefined, reportDraft: OnyxEntry, currentUserAccountID: number, + translate: LocalizedTranslate, policyTags?: OnyxCollection, - visibleReportActionsData: VisibleReportActionsDerivedValue = {}, ): OptionData { const report = getReportOrDraftReport(participant.reportID, undefined, undefined, reportDraft); const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); @@ -1287,20 +1287,20 @@ function getReportOption( }, reportAttributesDerived, policyTags: reportPolicyTags, - visibleReportActionsData, + visibleReportActionsData: {}, conciergeReportID, currentUserAccountID, }); // Update text & alternateText because createOption returns workspace name only if report is owned by the user if (option.isSelfDM) { - option.alternateText = translateLocal('reportActionsView.yourSpace'); + option.alternateText = translate('reportActionsView.yourSpace'); } else if (option.isInvoiceRoom) { option.text = deprecatedGetReportName(report, reportAttributesDerived); - option.alternateText = translateLocal('workspace.common.invoices'); + option.alternateText = translate('workspace.common.invoices'); } else { - option.text = getPolicyName({report, policy}); - option.alternateText = translateLocal('workspace.common.workspace'); + option.text = getPolicyName({report, policy, unavailableTranslation: translate('workspace.common.unavailable')}); + option.alternateText = translate('workspace.common.workspace'); if (report?.policyID) { const submitToAccountID = getSubmitToAccountID(policy, report, getLoginByAccountID(report?.ownerAccountID, personalDetails)); @@ -1308,7 +1308,7 @@ function getReportOption( const subtitle = submitsToAccountDetails?.displayName ?? submitsToAccountDetails?.login; if (subtitle) { - option.alternateText = translateLocal('iou.submitsTo', subtitle ?? ''); + option.alternateText = translate('iou.submitsTo', subtitle ?? ''); } } } @@ -1328,6 +1328,7 @@ function getReportDisplayOption( personalDetails: OnyxEntry, privateIsArchived: boolean | undefined, policy: OnyxEntry, + translate: LocalizedTranslate, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], policyTags?: OnyxEntry, visibleReportActionsData: VisibleReportActionsDerivedValue = {}, @@ -1351,17 +1352,17 @@ function getReportDisplayOption( // Update text & alternateText because createOption returns workspace name only if report is owned by the user if (option.isSelfDM) { - option.alternateText = translateLocal('reportActionsView.yourSpace'); + option.alternateText = translate('reportActionsView.yourSpace'); } else if (option.isInvoiceRoom) { option.text = deprecatedGetReportName(report, reportAttributesDerived); - option.alternateText = translateLocal('workspace.common.invoices'); + option.alternateText = translate('workspace.common.invoices'); } else if (unknownUserDetails) { option.text = unknownUserDetails.text ?? unknownUserDetails.login; option.alternateText = unknownUserDetails.login; option.participantsList = [{...unknownUserDetails, displayName: unknownUserDetails.login, accountID: unknownUserDetails.accountID ?? CONST.DEFAULT_NUMBER_ID}]; } else if (report?.ownerAccountID !== 0 || !option.text) { - option.text = getPolicyName({report, policy}); - option.alternateText = translateLocal('workspace.common.workspace'); + option.text = getPolicyName({report, policy, unavailableTranslation: translate('workspace.common.unavailable')}); + option.alternateText = translate('workspace.common.workspace'); } option.isDisabled = true; option.isSelected = false; @@ -1378,6 +1379,7 @@ function getPolicyExpenseReportOption( personalDetails: OnyxEntry, expenseReport: OnyxEntry, policy: OnyxEntry, + translate: LocalizedTranslate, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], policyTags?: OnyxEntry, visibleReportActionsData: VisibleReportActionsDerivedValue = {}, @@ -1402,8 +1404,8 @@ function getPolicyExpenseReportOption( }); // Update text & alternateText because createOption returns workspace name only if report is owned by the user - option.text = getPolicyName({report: expenseReport, policy}); - option.alternateText = translateLocal('workspace.common.workspace'); + option.text = getPolicyName({report: expenseReport, policy, unavailableTranslation: translate('workspace.common.unavailable')}); + option.alternateText = translate('workspace.common.workspace'); option.isSelected = participant.selected; option.selected = participant.selected; // Keep for backwards compatibility return option; @@ -3011,7 +3013,7 @@ function formatSectionsFromSearchTerm( const expenseReport = getReportByID(participant.reportID); const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${expenseReport?.reportID}`]; const expenseReportPolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${expenseReport?.policyID}`]; - return getPolicyExpenseReportOption(participant, privateIsArchived, personalDetails, expenseReport, expenseReportPolicy, reportAttributesDerived); + return getPolicyExpenseReportOption(participant, privateIsArchived, personalDetails, expenseReport, expenseReportPolicy, translate, reportAttributesDerived); } return getParticipantsOption(participant, personalDetails, translate); }) @@ -3043,7 +3045,7 @@ function formatSectionsFromSearchTerm( const expenseReport = getReportByID(participant.reportID); const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${expenseReport?.reportID}`]; const expenseReportPolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${expenseReport?.policyID}`]; - return getPolicyExpenseReportOption(participant, privateIsArchived, personalDetails, expenseReport, expenseReportPolicy, reportAttributesDerived); + return getPolicyExpenseReportOption(participant, privateIsArchived, personalDetails, expenseReport, expenseReportPolicy, translate, reportAttributesDerived); } return getParticipantsOption(participant, personalDetails, translate); }) diff --git a/src/libs/actions/IOU/MoneyRequest.ts b/src/libs/actions/IOU/MoneyRequest.ts index e3c3fec5690c..91b95fbb74df 100644 --- a/src/libs/actions/IOU/MoneyRequest.ts +++ b/src/libs/actions/IOU/MoneyRequest.ts @@ -255,7 +255,7 @@ function getMoneyRequestParticipantOptions( const participantAccountID = participant?.accountID ?? CONST.DEFAULT_NUMBER_ID; return participantAccountID ? getParticipantsOption(participant, personalDetails, translate) - : getReportOption(participant, privateIsArchived, policy, personalDetails, conciergeReportID, reportAttributesDerived, reportDraft, currentUserAccountID); + : getReportOption(participant, privateIsArchived, policy, personalDetails, conciergeReportID, reportAttributesDerived, reportDraft, currentUserAccountID, translate); }); } diff --git a/src/pages/Share/ShareDetailsPage.tsx b/src/pages/Share/ShareDetailsPage.tsx index 64e23c563e9f..c75fa7081a0a 100644 --- a/src/pages/Share/ShareDetailsPage.tsx +++ b/src/pages/Share/ShareDetailsPage.tsx @@ -82,8 +82,8 @@ function ShareDetailsPage({route}: ShareDetailsPageProps) { const ancestors = useAncestors(report); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`); const displayReport = useMemo( - () => getReportDisplayOption(report, unknownUserDetails, personalDetails, privateIsArchived, policy, reportAttributesDerived), - [report, unknownUserDetails, personalDetails, privateIsArchived, reportAttributesDerived, policy], + () => getReportDisplayOption(report, unknownUserDetails, personalDetails, privateIsArchived, policy, translate, reportAttributesDerived), + [report, unknownUserDetails, personalDetails, privateIsArchived, reportAttributesDerived, policy, translate], ); const shouldShowAttachment = !isTextShared; diff --git a/src/pages/Share/SubmitDetailsPage.tsx b/src/pages/Share/SubmitDetailsPage.tsx index 095da0bdf8f5..c4f8704f5d46 100644 --- a/src/pages/Share/SubmitDetailsPage.tsx +++ b/src/pages/Share/SubmitDetailsPage.tsx @@ -212,7 +212,17 @@ function SubmitDetailsPage({ const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${participant.reportID}`]; return participant?.accountID ? getParticipantsOption(participant, personalDetails, translate) - : getReportOption(participant, privateIsArchived, policy, personalDetails, conciergeReportID, reportAttributesDerived, reportDraft, currentUserPersonalDetails.accountID); + : getReportOption( + participant, + privateIsArchived, + policy, + personalDetails, + conciergeReportID, + reportAttributesDerived, + reportDraft, + currentUserPersonalDetails.accountID, + translate, + ); }); const isPolicyExpenseChat = participants?.some((participant) => participant.isPolicyExpenseChat); diff --git a/src/pages/iou/DynamicSplitBillDetailsPage.tsx b/src/pages/iou/DynamicSplitBillDetailsPage.tsx index e6285867e022..3b2b5e65fe33 100644 --- a/src/pages/iou/DynamicSplitBillDetailsPage.tsx +++ b/src/pages/iou/DynamicSplitBillDetailsPage.tsx @@ -76,7 +76,7 @@ function DynamicSplitBillDetailsPage({report, reportAction}: SplitBillDetailsPag if (isPolicyExpenseChat(report)) { participants = [ getParticipantsOption({accountID: participantAccountIDs.at(0), selected: true, reportID: ''}, personalDetails, translate), - getPolicyExpenseReportOption({...report, selected: true, reportID}, privateIsArchived, personalDetails, report, policy, reportAttributesDerived), + getPolicyExpenseReportOption({...report, selected: true, reportID}, privateIsArchived, personalDetails, report, policy, translate, reportAttributesDerived), ]; } else { participants = participantAccountIDs.map((accountID) => getParticipantsOption({accountID, selected: true, reportID: ''}, personalDetails, translate)); diff --git a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx index 2e226d38e7fb..e5a4050eb1a7 100644 --- a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx +++ b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx @@ -269,7 +269,15 @@ function MoneyRequestAttendeeSelector({attendees = [], onFinish, onAttendeesAdde const isPolicyExpenseChat = participant?.isPolicyExpenseChat ?? false; const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${userToInviteExpenseReport?.reportID}`]; return isPolicyExpenseChat - ? getPolicyExpenseReportOption(participant, privateIsArchived, personalDetails, userToInviteExpenseReport, userToInviteExpenseReportPolicy, reportAttributesDerived) + ? getPolicyExpenseReportOption( + participant, + privateIsArchived, + personalDetails, + userToInviteExpenseReport, + userToInviteExpenseReportPolicy, + translate, + reportAttributesDerived, + ) : getParticipantsOption(participant, personalDetails, translate); }) as OptionData[], sectionIndex: 3, diff --git a/src/pages/iou/request/ParticipantSearchResults.tsx b/src/pages/iou/request/ParticipantSearchResults.tsx index b3f758c2555e..4cec59a7e776 100644 --- a/src/pages/iou/request/ParticipantSearchResults.tsx +++ b/src/pages/iou/request/ParticipantSearchResults.tsx @@ -369,7 +369,15 @@ function ParticipantSearchResults({ const isPolicyExpenseChat = participant?.isPolicyExpenseChat ?? false; const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${userToInviteExpenseReport?.reportID}`]; return isPolicyExpenseChat - ? getPolicyExpenseReportOption(participant, privateIsArchived, personalDetails, userToInviteExpenseReport, userToInviteExpenseReportPolicy, reportAttributesDerived) + ? getPolicyExpenseReportOption( + participant, + privateIsArchived, + personalDetails, + userToInviteExpenseReport, + userToInviteExpenseReportPolicy, + translate, + reportAttributesDerived, + ) : getParticipantsOption(participant, personalDetails, translate); }), sectionIndex: 5, diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index f04c6da2126f..1919e129cdf7 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -195,7 +195,17 @@ function IOURequestStepAmount({ const privateIsArchived = !!allReportNVPs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${participant.reportID}`]?.private_isArchived; return participantAccountID ? getParticipantsOption(participant, personalDetails, translate) - : getReportOption(participant, privateIsArchived, policy, personalDetails, conciergeReportID, reportAttributesDerived, reportDraft, currentUserPersonalDetails.accountID); + : getReportOption( + participant, + privateIsArchived, + policy, + personalDetails, + conciergeReportID, + reportAttributesDerived, + reportDraft, + currentUserPersonalDetails.accountID, + translate, + ); }); const participant = participants.at(0); const policyTags = useMoneyRequestPolicyTags({ diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index b6987009e403..2eeb6658c50b 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -288,6 +288,7 @@ function IOURequestStepConfirmation({ reportAttributesDerived, participantReportDraft, currentUserPersonalDetails.accountID, + translate, ); }) ?? [], [ diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index b596708228d0..dcb0df91d114 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -6336,7 +6336,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, personalDetails, undefined, undefined); + const result = getReportDisplayOption(report, undefined, personalDetails, undefined, undefined, translateLocal); // Then it should return an option with isSelfDM and alternateText set expect(result.isSelfDM).toBe(true); @@ -6356,7 +6356,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, personalDetails, undefined, undefined); + const result = getReportDisplayOption(report, undefined, personalDetails, undefined, undefined, translateLocal); // Then it should return an option with invoice room text and alternateText expect(result.isInvoiceRoom).toBe(true); @@ -6378,7 +6378,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, unknownUserDetails, personalDetails, undefined, undefined); + const result = getReportDisplayOption(report, unknownUserDetails, personalDetails, undefined, undefined, translateLocal); // Then it should return an option with unknownUserDetails data expect(result.text).toBe('Unknown User'); @@ -6399,7 +6399,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, personalDetails, undefined, undefined); + const result = getReportDisplayOption(report, undefined, personalDetails, undefined, undefined, translateLocal); // Then it should return an option with workspace name expect(result.text).toBe(POLICY.name); @@ -6428,7 +6428,7 @@ describe('OptionsListUtils', () => { }; // When we call getReportDisplayOption with custom personalDetails - const result = getReportDisplayOption(report, undefined, customPersonalDetails, undefined, undefined); + const result = getReportDisplayOption(report, undefined, customPersonalDetails, undefined, undefined, translateLocal); // Then it should use the custom personalDetails parameter expect(result).toBeDefined(); @@ -6445,7 +6445,7 @@ describe('OptionsListUtils', () => { const emptyPersonalDetails: PersonalDetailsList = {}; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, emptyPersonalDetails, undefined, undefined); + const result = getReportDisplayOption(report, undefined, emptyPersonalDetails, undefined, undefined, translateLocal); // Then it should not throw and return a valid option expect(result).toBeDefined(); @@ -6457,7 +6457,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption with undefined report - const result = getReportDisplayOption(undefined, undefined, personalDetails, undefined, undefined); + const result = getReportDisplayOption(undefined, undefined, personalDetails, undefined, undefined, translateLocal); // Then it should return a valid option (createOption handles undefined) expect(result).toBeDefined(); @@ -6580,7 +6580,7 @@ describe('OptionsListUtils', () => { isPolicyExpenseChat: true, }; - const option = getReportOption(participant, undefined, policy, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, policy, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.text).toBe('Test Workspace'); expect(option.alternateText).toBe(translateLocal('workspace.common.workspace')); @@ -6635,7 +6635,7 @@ describe('OptionsListUtils', () => { isPolicyExpenseChat: true, }; - const option = getReportOption(participant, undefined, policy, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, policy, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.text).toBe('Test Workspace with Submit'); // The submitsTo logic may or may not apply depending on complex approval rules @@ -6658,7 +6658,7 @@ describe('OptionsListUtils', () => { reportID, }; - const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, report, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, report, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.isDisabled).toBe(true); }); @@ -6699,7 +6699,7 @@ describe('OptionsListUtils', () => { isSelfDM: true, }; - const option = getReportOption(participant, undefined, POLICY, personalDetails, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, personalDetails, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); // The option.isSelfDM is set by createOption based on the report type // Just verify the alternateText is correct for self DM @@ -6734,7 +6734,7 @@ describe('OptionsListUtils', () => { isInvoiceRoom: true, }; - const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.isInvoiceRoom).toBe(true); expect(option.alternateText).toBe(translateLocal('workspace.common.invoices')); @@ -6777,7 +6777,7 @@ describe('OptionsListUtils', () => { }); await waitForBatchedUpdates(); - const option = getReportOption(participant, !!reportNameValuePair?.private_isArchived, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, !!reportNameValuePair?.private_isArchived, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.text).toBe(POLICY.name); expect(option.alternateText).toBeTruthy(); @@ -6821,7 +6821,7 @@ describe('OptionsListUtils', () => { }); await waitForBatchedUpdates(); - const option = getReportOption(participant, !!reportNameValuePair?.private_isArchived, POLICY, {}, undefined, {}, draftReport, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, !!reportNameValuePair?.private_isArchived, POLICY, {}, undefined, {}, draftReport, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.isDisabled).toBe(true); }); @@ -6840,7 +6840,7 @@ describe('OptionsListUtils', () => { const participant: Participant = {reportID, selected: false}; // Pass reportDraft = undefined → not a draft, should NOT be disabled - const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.isDisabled).toBeFalsy(); }); @@ -6856,7 +6856,7 @@ describe('OptionsListUtils', () => { const participant: Participant = {reportID, selected: false}; // Pass reportDraft explicitly → should be disabled regardless of Onyx state - const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, draftReport, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, draftReport, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.isDisabled).toBe(true); }); @@ -6876,7 +6876,7 @@ describe('OptionsListUtils', () => { const participant: Participant = {reportID, selected: false}; // Callers are responsible for passing reportDraft explicitly — undefined means not disabled - const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.isDisabled).toBeFalsy(); }); @@ -6898,7 +6898,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, !!reportNameValuePair?.private_isArchived, undefined); + const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, !!reportNameValuePair?.private_isArchived, undefined, translateLocal); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -6921,7 +6921,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, !!reportNameValuePair?.private_isArchived, undefined); + const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, !!reportNameValuePair?.private_isArchived, undefined, translateLocal); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -6941,7 +6941,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, undefined); + const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, undefined, translateLocal); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -6965,7 +6965,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, !!reportNameValuePair?.private_isArchived, undefined); + const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, !!reportNameValuePair?.private_isArchived, undefined, translateLocal); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -6989,7 +6989,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, !!reportNameValuePair?.private_isArchived, undefined); + const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, !!reportNameValuePair?.private_isArchived, undefined, translateLocal); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -7012,7 +7012,7 @@ describe('OptionsListUtils', () => { selected: true, }; - const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.isSelected).toBe(true); expect(option.selected).toBe(true); @@ -7033,7 +7033,7 @@ describe('OptionsListUtils', () => { reportID, }; - const option = getReportOption(participant, undefined, undefined, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, undefined, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); expect(option.text).toBeDefined(); @@ -7055,7 +7055,7 @@ describe('OptionsListUtils', () => { }; // Test that the function works with reportAttributesDerived parameter (optional) - const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); }); @@ -7091,7 +7091,7 @@ describe('OptionsListUtils', () => { reportID, }; - const option = getReportOption(participant, undefined, POLICY, testPersonalDetails, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, testPersonalDetails, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); // The createOption function uses personalDetails to build display names @@ -7154,7 +7154,7 @@ describe('OptionsListUtils', () => { isPolicyExpenseChat: true, }; - const option = getReportOption(participant, undefined, policy, testPersonalDetails, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, policy, testPersonalDetails, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); expect(option.text).toBe('Test Workspace with Approver'); @@ -7179,7 +7179,7 @@ describe('OptionsListUtils', () => { }; // Pass empty personalDetails - const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); expect(option.text).toBeDefined(); @@ -7201,7 +7201,7 @@ describe('OptionsListUtils', () => { }; // Pass undefined personalDetails - const option = getReportOption(participant, undefined, POLICY, undefined, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, undefined, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); expect(option.text).toBeDefined(); @@ -7251,7 +7251,7 @@ describe('OptionsListUtils', () => { isInvoiceRoom: true, }; - const option = getReportOption(participant, undefined, POLICY, testPersonalDetails, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, testPersonalDetails, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); expect(option.isInvoiceRoom).toBe(true); @@ -7273,7 +7273,7 @@ describe('OptionsListUtils', () => { const participant = {reportID}; - const option = getReportOption(participant, undefined, POLICY, {}, conciergeReportID, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, conciergeReportID, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -7292,7 +7292,7 @@ describe('OptionsListUtils', () => { const participant = {reportID}; - const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -7329,7 +7329,7 @@ describe('OptionsListUtils', () => { // Passing conciergeReportID matching the reportID identifies this as the Concierge chat, // which affects getMovedTransactionMessage to use CONST.CONCIERGE_DISPLAY_NAME ('Concierge') - const option = getReportOption(participant, undefined, POLICY, testPersonalDetails, conciergeReportID, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const option = getReportOption(participant, undefined, POLICY, testPersonalDetails, conciergeReportID, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -7349,8 +7349,8 @@ describe('OptionsListUtils', () => { const participant = {reportID}; - const optionWithConcierge = getReportOption(participant, undefined, POLICY, {}, differentConciergeReportID, undefined, undefined, CONST.DEFAULT_NUMBER_ID); - const optionWithoutConcierge = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID); + const optionWithConcierge = getReportOption(participant, undefined, POLICY, {}, differentConciergeReportID, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); + const optionWithoutConcierge = getReportOption(participant, undefined, POLICY, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); // Both should produce the same result since the IDs don't match expect(optionWithConcierge.reportID).toBe(optionWithoutConcierge.reportID); @@ -7408,7 +7408,7 @@ describe('OptionsListUtils', () => { selected: true, }; - const option = getPolicyExpenseReportOption(participant, undefined, testPersonalDetails, report, undefined); + const option = getPolicyExpenseReportOption(participant, undefined, testPersonalDetails, report, undefined, translateLocal); expect(option).toBeDefined(); expect(option.text).toBe('Test Workspace Policy'); @@ -7416,6 +7416,31 @@ describe('OptionsListUtils', () => { expect(option.isSelected).toBe(true); }); + it('routes the workspace subtitle through the injected translate function', async () => { + const reportID = '9001'; + const testPolicyID = 'policyMarker'; + const report: Report = { + reportID, + reportName: 'Marker Workspace Policy', + type: CONST.REPORT.TYPE.CHAT, + chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + policyID: testPolicyID, + isOwnPolicyExpenseChat: true, + ownerAccountID: 1, + }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); + await waitForBatchedUpdates(); + + // A translate that tags the workspace subtitle so we can prove the option used the injected translate + const translateWithMarker: LocalizedTranslate = (path, ...parameters) => (path === 'workspace.common.workspace' ? 'WorkspaceMarker' : translateLocal(path, ...parameters)); + + const participant = {reportID, policyID: testPolicyID, isPolicyExpenseChat: true, selected: false}; + const option = getPolicyExpenseReportOption(participant, undefined, {}, report, undefined, translateWithMarker); + + // The subtitle resolves to the marker, proving the option builder used the injected translate (not translateLocal) + expect(option.alternateText).toBe('WorkspaceMarker'); + }); + it('should use personalDetails to create option with participant info', async () => { const reportID = '202'; const testPolicyID = 'policy202'; @@ -7473,7 +7498,7 @@ describe('OptionsListUtils', () => { isPolicyExpenseChat: true, }; - const option = getPolicyExpenseReportOption(participant, undefined, testPersonalDetails, report, undefined); + const option = getPolicyExpenseReportOption(participant, undefined, testPersonalDetails, report, undefined, translateLocal); expect(option).toBeDefined(); expect(option.text).toBe('Team Workspace'); @@ -7519,7 +7544,7 @@ describe('OptionsListUtils', () => { }; // Should not throw when personalDetails is empty - const option = getPolicyExpenseReportOption(participant, undefined, {}, report, undefined); + const option = getPolicyExpenseReportOption(participant, undefined, {}, report, undefined, translateLocal); expect(option).toBeDefined(); expect(option.text).toBe('Workspace Without Details'); @@ -7565,7 +7590,7 @@ describe('OptionsListUtils', () => { }; // Should not throw when personalDetails is undefined - const option = getPolicyExpenseReportOption(participant, undefined, undefined, report, undefined); + const option = getPolicyExpenseReportOption(participant, undefined, undefined, report, undefined, translateLocal); expect(option).toBeDefined(); expect(option.text).toBe('Workspace Undefined Details'); @@ -7619,10 +7644,10 @@ describe('OptionsListUtils', () => { selected: false, }; - const optionSelected = getPolicyExpenseReportOption(participantSelected, undefined, {}, report, undefined); + const optionSelected = getPolicyExpenseReportOption(participantSelected, undefined, {}, report, undefined, translateLocal); // eslint-disable-next-line rulesdir/no-negated-variables - const optionNotSelected = getPolicyExpenseReportOption(participantNotSelected, undefined, {}, report, undefined); + const optionNotSelected = getPolicyExpenseReportOption(participantNotSelected, undefined, {}, report, undefined, translateLocal); expect(optionSelected.isSelected).toBe(true); expect(optionSelected.selected).toBe(true); @@ -7680,7 +7705,7 @@ describe('OptionsListUtils', () => { selected: false, }; - const option = getPolicyExpenseReportOption(participant, true, testPersonalDetails, report, undefined); + const option = getPolicyExpenseReportOption(participant, true, testPersonalDetails, report, undefined, translateLocal); expect(option).toBeDefined(); expect(option.private_isArchived).toBe(true); @@ -7726,7 +7751,7 @@ describe('OptionsListUtils', () => { isPolicyExpenseChat: true, }; - const option = getPolicyExpenseReportOption(participant, undefined, {}, report, undefined); + const option = getPolicyExpenseReportOption(participant, undefined, {}, report, undefined, translateLocal); expect(option).toBeDefined(); expect(option?.private_isArchived).toBeUndefined(); @@ -8271,7 +8296,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); // When we call getReportDisplayOption with chat report - const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, undefined); + const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, undefined, translateLocal); // Then the option should be created successfully using the reports collection expect(option).toBeDefined(); @@ -8312,7 +8337,7 @@ describe('OptionsListUtils', () => { }; // When we call getPolicyExpenseReportOption with report passed directly - const option = getPolicyExpenseReportOption(participant, undefined, PERSONAL_DETAILS, report, undefined); + const option = getPolicyExpenseReportOption(participant, undefined, PERSONAL_DETAILS, report, undefined, translateLocal); // Then the option should be created successfully expect(option).toBeDefined(); @@ -8748,7 +8773,7 @@ describe('OptionsListUtils', () => { }, }; - const result = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, POLICY); + const result = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, POLICY, translateLocal); expect(result).toBeDefined(); expect(result.policyID).toBe(policyID); }); @@ -8777,7 +8802,7 @@ describe('OptionsListUtils', () => { selected: true, }; - const result = getPolicyExpenseReportOption(participant, undefined, PERSONAL_DETAILS, report, POLICY); + const result = getPolicyExpenseReportOption(participant, undefined, PERSONAL_DETAILS, report, POLICY, translateLocal); expect(result).toBeDefined(); expect(result.policyID).toBe(policyID); }); From 37fb7c9899ccbea5692e3587b8a710fef4138e92 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Tue, 21 Jul 2026 16:46:22 +0700 Subject: [PATCH 2/2] OptionsListUtilsTest to use real personalDetails for accurate subtitle resolution in getReportOption --- tests/unit/OptionsListUtilsTest.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index dcb0df91d114..0723599d4447 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -6635,12 +6635,13 @@ describe('OptionsListUtils', () => { isPolicyExpenseChat: true, }; - const option = getReportOption(participant, undefined, policy, {}, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); + // Pass the real personalDetails so the submits-to subtitle resolves to a name + const option = getReportOption(participant, undefined, policy, personalDetails, undefined, undefined, undefined, CONST.DEFAULT_NUMBER_ID, translateLocal); expect(option.text).toBe('Test Workspace with Submit'); - // The submitsTo logic may or may not apply depending on complex approval rules - // Just verify the option was created correctly - expect(option.alternateText).toBeDefined(); + // For a BASIC-approval policy the report submits to the default approver (the owner), + // so the subtitle is produced by the injected translate via the `iou.submitsTo` copy + expect(option.alternateText).toBe(translateLocal('iou.submitsTo', 'Report Owner')); }); it('should mark draft reports as disabled', async () => {