From 248d68f73c1991613493fba095d8e97119bfa5c8 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Sat, 25 Jul 2026 23:32:55 +0700 Subject: [PATCH 1/9] Update handling of conciergeReportID across components --- .../Search/FilterComponents/InSelector.tsx | 1 + .../Search/SearchList/ListItem/types.ts | 2 +- .../Search/SearchRouter/SearchRouter.tsx | 2 + src/components/Search/SearchStaticList.tsx | 3 +- src/hooks/useFilteredOptions.ts | 2 + src/libs/OptionsListUtils/index.ts | 19 +- src/libs/ReportNameUtils.ts | 3 +- src/libs/ReportUtils.ts | 5 +- src/libs/SidebarUtils.ts | 1 + src/libs/actions/Report/index.ts | 31 ++- src/pages/Share/ShareDetailsPage.tsx | 2 +- .../useSpendOverTimeData.ts | 3 +- .../actionContents/ChatActionableButtons.tsx | 2 + tests/actions/ReportTest.ts | 2 +- tests/unit/OptionsListUtilsTest.tsx | 217 ++++++++++++------ tests/unit/ReportNameUtilsTest.ts | 33 +++ 16 files changed, 247 insertions(+), 81 deletions(-) diff --git a/src/components/Search/FilterComponents/InSelector.tsx b/src/components/Search/FilterComponents/InSelector.tsx index 26072c9f6de8..af9ab9f742b7 100644 --- a/src/components/Search/FilterComponents/InSelector.tsx +++ b/src/components/Search/FilterComponents/InSelector.tsx @@ -87,6 +87,7 @@ function InSelector({value = [], selectionListTextInputStyle, selectionListStyle privateIsArchived, reportPolicy, sortedActions, + conciergeReportID, reportAttributesDerived, undefined, undefined, diff --git a/src/components/Search/SearchList/ListItem/types.ts b/src/components/Search/SearchList/ListItem/types.ts index 5bfd75262fa7..4d2554260df5 100644 --- a/src/components/Search/SearchList/ListItem/types.ts +++ b/src/components/Search/SearchList/ListItem/types.ts @@ -552,7 +552,7 @@ type GroupChildrenContentProps = { newTransactionID?: string; bankAccountList?: OnyxEntry; cardFeeds?: OnyxCollection; - conciergeReportID?: string; + conciergeReportID: string | undefined; }; type UnreportedExpenseListItemType = Transaction & { diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 3cd37413210a..ca8239ba4e4d 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -86,6 +86,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla const currentUserAccountID = currentUserPersonalDetails.accountID; const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [betas] = useOnyx(ONYXKEYS.BETAS); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const [searchContext] = useOnyx(ONYXKEYS.SEARCH_CONTEXT); @@ -218,6 +219,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla contextualReportNVP, contextualReportPolicy, sortedActions, + conciergeReportID, undefined, { showPersonalDetails: true, diff --git a/src/components/Search/SearchStaticList.tsx b/src/components/Search/SearchStaticList.tsx index 78e993db93e0..8970ec70a1b7 100644 --- a/src/components/Search/SearchStaticList.tsx +++ b/src/components/Search/SearchStaticList.tsx @@ -85,6 +85,7 @@ function SearchStaticList({ const email = session?.email; const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasCompletedGuidedSetupFlowSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [showPendingExpensePlaceholder, setShowPendingExpensePlaceholder] = useState( () => hasDeferredWrite(CONST.DEFERRED_LAYOUT_WRITE_KEYS.SEARCH) || Navigation.getIsFullscreenPreInsertedUnderRHP(), @@ -107,7 +108,7 @@ function SearchStaticList({ translate, formatPhoneNumber, bankAccountList: undefined, - conciergeReportID: undefined, + conciergeReportID, convertToDisplayString, reportAttributesDerivedValue: undefined, }); diff --git a/src/hooks/useFilteredOptions.ts b/src/hooks/useFilteredOptions.ts index 1ed9dc3fb037..9e4d93de1736 100644 --- a/src/hooks/useFilteredOptions.ts +++ b/src/hooks/useFilteredOptions.ts @@ -82,6 +82,7 @@ function useFilteredOptions(config: UseFilteredOptionsConfig = {}): UseFilteredO const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const reportAttributesDerived = useReportAttributes(); const [isTrackIntentUser] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {selector: isTrackIntentUserSelector}); @@ -106,6 +107,7 @@ function useFilteredOptions(config: UseFilteredOptionsConfig = {}): UseFilteredO reportAttributesDerived, privateIsArchivedMap, allPolicies, + conciergeReportID, { maxRecentReports: reportsLimit, includeP2P, diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index c24d640dc3e1..49fd45c1ecf8 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -673,8 +673,7 @@ function getLastMessageTextForReport({ reportAttributesDerived?: ReportAttributesDerivedValue['reports']; policyTags?: OnyxEntry; currentUserLogin?: string; - // TODO: conciergeReportID will be required eventually. Refactor issue: https://github.com/Expensify/App/issues/66411 - conciergeReportID?: string; + conciergeReportID: string | undefined; isTrackIntentUser?: boolean; // TODO: Remove optional (?) once all callers pass sortedActions. Refactor issue: https://github.com/Expensify/App/issues/66381 sortedActions?: Record; @@ -1079,8 +1078,7 @@ type CreateOptionParams = { visibleReportActionsData?: VisibleReportActionsDerivedValue; translate?: LocalizedTranslate; isTrackIntentUser?: boolean; - // TODO: conciergeReportID will be required eventually. Refactor issue: https://github.com/Expensify/App/issues/66411 - conciergeReportID?: string; + conciergeReportID: string | undefined; // TODO: Remove optional (?) once all callers pass sortedActions. Refactor issue: https://github.com/Expensify/App/issues/66381 sortedActions?: Record; // TODO: Remove optional (?) once all callers pass currentUserAccountID. Refactor issue: https://github.com/Expensify/App/issues/66408 @@ -1330,6 +1328,7 @@ function getReportDisplayOption( personalDetails: OnyxEntry, privateIsArchived: boolean | undefined, policy: OnyxEntry, + conciergeReportID: string | undefined, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], policyTags?: OnyxEntry, visibleReportActionsData: VisibleReportActionsDerivedValue = {}, @@ -1342,6 +1341,7 @@ function getReportDisplayOption( report: !isEmptyObject(report) ? report : undefined, policy, privateIsArchived, + conciergeReportID, config: { showChatPreviewLine: false, forcePolicyNamePreview: false, @@ -1394,6 +1394,8 @@ function getPolicyExpenseReportOption( report: !isEmptyObject(expenseReport) ? expenseReport : null, policy, privateIsArchived, + // Safe: a policy expense chat is never the Concierge chat. + conciergeReportID: undefined, config: { showChatPreviewLine: false, forcePolicyNamePreview: false, @@ -1511,6 +1513,7 @@ function processReport( personalDetails: OnyxEntry, privateIsArchived: boolean | undefined, policy: OnyxEntry, + conciergeReportID: string | undefined, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], policyTags?: OnyxEntry, visibleReportActionsData: VisibleReportActionsDerivedValue = {}, @@ -1546,6 +1549,7 @@ function processReport( report, privateIsArchived, policy, + conciergeReportID, reportAttributesDerived, policyTags, visibleReportActionsData, @@ -1659,6 +1663,7 @@ function createFilteredOptionList( reportAttributesDerived: ReportAttributesDerivedValue['reports'] | undefined, privateIsArchivedMap: PrivateIsArchivedMap, policiesCollection: OnyxCollection, + conciergeReportID: string | undefined, options: { maxRecentReports?: number; includeP2P?: boolean; @@ -1763,6 +1768,7 @@ function createFilteredOptionList( personalDetails, privateIsArchived, policy, + conciergeReportID, reportAttributesDerived, reportPolicyTags, visibleReportActionsData, @@ -1807,6 +1813,7 @@ function createFilteredOptionList( report: reportMapForAccountIDs[accountID], policy, privateIsArchived, + conciergeReportID, config: {showPersonalDetails: true}, reportAttributesDerived, policyTags: reportPolicyTags, @@ -1848,6 +1855,7 @@ function createOptionFromReport( privateIsArchived: boolean | undefined, policy: OnyxEntry, sortedActions: Record | undefined, + conciergeReportID: string | undefined, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], config?: PreviewConfig, policyTags?: OnyxEntry, @@ -1864,6 +1872,7 @@ function createOptionFromReport( report, privateIsArchived, policy, + conciergeReportID, config, reportAttributesDerived, policyTags, @@ -2204,6 +2213,8 @@ function getUserToInviteOption({ personalDetails: personalDetailsExtended, report: null, privateIsArchived: undefined, + // Safe: the invite option is built without a report, so it can never be the Concierge chat. + conciergeReportID: undefined, config: {showChatPreviewLine}, visibleReportActionsData, }); diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index 1d751575fda5..f041ef1a9a93 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -187,8 +187,7 @@ type ComputeReportName = { currentUserAccountID?: number; currentUserLogin: string; translate: LocalizedTranslate; - // TODO: Make this required when https://github.com/Expensify/App/issues/66411 is done - conciergeReportID?: string; + conciergeReportID: string | undefined; reportAttributes?: ReportAttributesDerivedValue['reports']; isTrackIntentUser: boolean | undefined; }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 280e60205550..69fd7e4c7bac 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1892,7 +1892,10 @@ function getReportNotificationPreference(report: OnyxEntry): ValueOf, conciergeReportID?: string): boolean { +function isConciergeChatReport(report: OnyxInputOrEntry, conciergeReportID: string | undefined): boolean { + // The deprecated Onyx.connect fallback stays until every conciergeReportID param in the createOptions chain is + // required and threaded (see the remaining #66411 TODOs) — removing it earlier would break Concierge detection + // on call paths that still omit the value. return !!report && report?.reportID === (conciergeReportID ?? conciergeReportIDOnyxConnect); } diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 2ce0c0284d63..884210a1cdeb 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1004,6 +1004,7 @@ function getOptionData({ report, personalDetails, lastActorDetails, + conciergeReportID, movedFromReport, movedToReport, policy, diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 3ae815e31bd9..8caceb25fcb3 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -8005,6 +8005,7 @@ function resolveConciergeOptions( selectedField: 'selectedCategory' | 'selectedDescription', currentUserAccountID: number, delegateAccountID: number | undefined, + conciergeReportID: string | undefined, ancestors: Ancestor[] = [], ) { if (!report?.reportID || !reportActionID) { @@ -8013,7 +8014,7 @@ function resolveConciergeOptions( const reportID = report.reportID; // Must thread before #66411 removes the fallback: this report CAN be the Concierge chat, so undefined would drop Concierge params. - addComment({report, notifyReportID: notifyReportID ?? reportID, ancestors, text: selectedValue, timezoneParam, currentUserAccountID, delegateAccountID, conciergeReportID: undefined}); + addComment({report, notifyReportID: notifyReportID ?? reportID, ancestors, text: selectedValue, timezoneParam, currentUserAccountID, delegateAccountID, conciergeReportID}); Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { [reportActionID]: { @@ -8041,9 +8042,21 @@ function resolveConciergeCategoryOptions( timezoneParam: Timezone, currentUserAccountID: number, delegateAccountID: number | undefined, + conciergeReportID: string | undefined, ancestors: Ancestor[] = [], ) { - resolveConciergeOptions(report, notifyReportID, reportActionID, selectedCategory, timezoneParam, 'selectedCategory', currentUserAccountID, delegateAccountID, ancestors); + resolveConciergeOptions( + report, + notifyReportID, + reportActionID, + selectedCategory, + timezoneParam, + 'selectedCategory', + currentUserAccountID, + delegateAccountID, + conciergeReportID, + ancestors, + ); } /** @@ -8063,9 +8076,21 @@ function resolveConciergeDescriptionOptions( timezoneParam: Timezone, currentUserAccountID: number, delegateAccountID: number | undefined, + conciergeReportID: string | undefined, ancestors: Ancestor[] = [], ) { - resolveConciergeOptions(report, notifyReportID, reportActionID, selectedDescription, timezoneParam, 'selectedDescription', currentUserAccountID, delegateAccountID, ancestors); + resolveConciergeOptions( + report, + notifyReportID, + reportActionID, + selectedDescription, + timezoneParam, + 'selectedDescription', + currentUserAccountID, + delegateAccountID, + conciergeReportID, + ancestors, + ); } /** diff --git a/src/pages/Share/ShareDetailsPage.tsx b/src/pages/Share/ShareDetailsPage.tsx index 64e23c563e9f..558645b16c39 100644 --- a/src/pages/Share/ShareDetailsPage.tsx +++ b/src/pages/Share/ShareDetailsPage.tsx @@ -82,7 +82,7 @@ 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), + () => getReportDisplayOption(report, unknownUserDetails, personalDetails, privateIsArchived, policy, conciergeReportID, reportAttributesDerived), [report, unknownUserDetails, personalDetails, privateIsArchived, reportAttributesDerived, policy], ); diff --git a/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts b/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts index 6665391dab84..aea253506419 100644 --- a/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts +++ b/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts @@ -60,6 +60,7 @@ function useSpendOverTimeData() { const {translate, localeCompare, formatPhoneNumber} = useLocalize(); const {convertToDisplayString} = useCurrencyListActions(); const {accountID, login} = useCurrentUserPersonalDetails(); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [searchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${queryJSON?.hash}`); const isSearchLoading = !!searchResults?.search?.isLoading; @@ -102,7 +103,7 @@ function useSpendOverTimeData() { translate, formatPhoneNumber, bankAccountList: undefined, - conciergeReportID: undefined, + conciergeReportID, convertToDisplayString, reportAttributesDerivedValue: undefined, })[0], diff --git a/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx index 7b4324109dc8..a049cb1a7779 100644 --- a/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx +++ b/src/pages/inbox/report/actionContents/ChatActionableButtons.tsx @@ -123,6 +123,7 @@ function ChatActionableButtons({action, originalReportID, reportID, hasPendingFo personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, personalDetail.accountID, delegateAccountID, + conciergeReportID, ); }, })); @@ -154,6 +155,7 @@ function ChatActionableButtons({action, originalReportID, reportID, hasPendingFo personalDetail.timezone ?? CONST.DEFAULT_TIME_ZONE, personalDetail.accountID, delegateAccountID, + conciergeReportID, ); }, })); diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 6517be85e3e1..0aa43ee2b25a 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -6551,7 +6551,7 @@ describe('actions/Report', () => { await waitForBatchedUpdates(); // When the user picks one of the Concierge-suggested category options - Report.resolveConciergeCategoryOptions(report, REPORT_ID, '5150', 'Food', CONST.DEFAULT_TIME_ZONE, 1, undefined); + Report.resolveConciergeCategoryOptions(report, REPORT_ID, '5150', 'Food', CONST.DEFAULT_TIME_ZONE, 1, undefined, REPORT_ID); await waitForBatchedUpdates(); // Then the selection is posted back to Concierge as a comment diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 4c88a27e66bb..1d8743226fd5 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -825,13 +825,14 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}10`, reportNameValuePairs); await waitForBatchedUpdates(); - OPTIONS = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, MOCK_REPORT_ATTRIBUTES_DERIVED, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, {isSearching: true}); + OPTIONS = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, MOCK_REPORT_ATTRIBUTES_DERIVED, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, undefined, {isSearching: true}); OPTIONS_WITH_CONCIERGE = createFilteredOptionList( PERSONAL_DETAILS_WITH_CONCIERGE, REPORTS_WITH_CONCIERGE, MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CONCIERGE, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, + undefined, {isSearching: true}, ); OPTIONS_WITH_CHRONOS = createFilteredOptionList( @@ -840,6 +841,7 @@ describe('OptionsListUtils', () => { MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CHRONOS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, + undefined, {isSearching: true}, ); OPTIONS_WITH_RECEIPTS = createFilteredOptionList( @@ -848,6 +850,7 @@ describe('OptionsListUtils', () => { MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_RECEIPTS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, + undefined, {isSearching: true}, ); OPTIONS_WITH_WORKSPACE_ROOM = createFilteredOptionList( @@ -856,6 +859,7 @@ describe('OptionsListUtils', () => { MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_WORKSPACE_ROOM, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, + undefined, {isSearching: true}, ); }); @@ -924,6 +928,7 @@ describe('OptionsListUtils', () => { createMockReportAttributesDerived(memberWorkspaceChat, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID), EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, + undefined, {isSearching: true}, ); @@ -2402,7 +2407,7 @@ describe('OptionsListUtils', () => { const archivedMap: PrivateIsArchivedMap = { [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}10`]: !!reportNameValuePairs.private_isArchived, }; - const OPTIONS_WITH_ARCHIVED = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, MOCK_REPORT_ATTRIBUTES_DERIVED, archivedMap, undefined, {isSearching: true}); + const OPTIONS_WITH_ARCHIVED = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, MOCK_REPORT_ATTRIBUTES_DERIVED, archivedMap, undefined, undefined, {isSearching: true}); // When we call getSearchOptions with all betas const {options} = getSearchOptions({ options: OPTIONS_WITH_ARCHIVED, @@ -2430,7 +2435,9 @@ describe('OptionsListUtils', () => { // cspell:disable-next-line const searchText = 'barryallen'; // Given a set of options created from PERSONAL_DETAILS_WITH_PERIODS - const OPTIONS_WITH_PERIODS = createFilteredOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {isSearching: true}); + const OPTIONS_WITH_PERIODS = createFilteredOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + isSearching: true, + }); // When we call getSearchOptions with all betas const {options} = getSearchOptions({ options: OPTIONS_WITH_PERIODS, @@ -2515,6 +2522,7 @@ describe('OptionsListUtils', () => { MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CHAT_ROOM, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, + undefined, {isSearching: true}, ); // When we call getSearchOptions with all betas @@ -2878,7 +2886,9 @@ describe('OptionsListUtils', () => { }, }; - const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {isSearching: true}); + const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + isSearching: true, + }); // When we call getSearchOptions with a search query that matches a participant display name const {options} = getSearchOptions({ @@ -2926,7 +2936,9 @@ describe('OptionsListUtils', () => { }, }; - const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {isSearching: true}); + const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + isSearching: true, + }); // When we call getSearchOptions with a search query that matches a participant login const {options} = getSearchOptions({ @@ -2974,7 +2986,9 @@ describe('OptionsListUtils', () => { }, }; - const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {isSearching: true}); + const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + isSearching: true, + }); // When we call getSearchOptions with a search query that matches a participant name const {options} = getSearchOptions({ @@ -3022,7 +3036,9 @@ describe('OptionsListUtils', () => { }, }; - const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {isSearching: true}); + const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + isSearching: true, + }); // When we call getSearchOptions with a search query that does not match any participant const {options} = getSearchOptions({ @@ -3071,6 +3087,7 @@ describe('OptionsListUtils', () => { undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, + undefined, {isSearching: true}, ); @@ -3436,7 +3453,9 @@ describe('OptionsListUtils', () => { .then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, PERSONAL_DETAILS_WITH_PERIODS)) .then(() => { // Given a set of options with periods - const OPTIONS_WITH_PERIODS = createFilteredOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {isSearching: true}); + const OPTIONS_WITH_PERIODS = createFilteredOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + isSearching: true, + }); // When we call getSearchOptions const {options: results} = getSearchOptions({ options: OPTIONS_WITH_PERIODS, @@ -3502,7 +3521,9 @@ describe('OptionsListUtils', () => { it('should order self dm always on top if the search matches with the self dm login', () => { const searchTerm = 'tonystark@expensify.com'; - const OPTIONS_WITH_SELF_DM = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_SELF_DM, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {isSearching: true}); + const OPTIONS_WITH_SELF_DM = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_SELF_DM, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + isSearching: true, + }); // Given a set of options with self dm and all betas const {options} = getSearchOptions({ @@ -3570,7 +3591,7 @@ describe('OptionsListUtils', () => { // Given a set of reports and personal details await Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, PERSONAL_DETAILS); // When we call createFilteredOptionList and extract the reports - const reports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, {isSearching: true}).reports; + const reports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, undefined, {isSearching: true}).reports; // Then the returned reports should match the expected values expect(reports.at(10)?.subtitle).toBe(`Submits to Mister Fantastic`); @@ -3580,7 +3601,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); // When we call createFilteredOptionList again - const newReports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, {isSearching: true}).reports; + const newReports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, undefined, {isSearching: true}).reports; // Then the returned reports should change to Spanish // cspell:disable-next-line expect(newReports.at(10)?.subtitle).toBe('Se envía a Mister Fantastic'); @@ -3663,7 +3684,7 @@ describe('OptionsListUtils', () => { const archivedMap: PrivateIsArchivedMap = { [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}10`]: !!reportNameValuePairs.private_isArchived, }; - const reports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, archivedMap, undefined, {isSearching: true}).reports; + const reports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, archivedMap, undefined, undefined, {isSearching: true}).reports; const archivedReport = reports.find((report) => report.reportID === '10'); // Then the returned report should contain default archived reason @@ -3681,7 +3702,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with this privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, {isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined, {isSearching: true}); // Then the personal detail option for account 1 (Mister Fantastic) should have private_isArchived set const misterFantasticOption = result.personalDetails.find((pd) => pd.item?.accountID === 1); @@ -3694,7 +3715,7 @@ describe('OptionsListUtils', () => { const emptyMap: PrivateIsArchivedMap = {}; // When we call createFilteredOptionList with an empty privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, emptyMap, undefined, {isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, emptyMap, undefined, undefined, {isSearching: true}); // Then no personal details options should have private_isArchived set const optionsWithArchived = result.personalDetails.filter((pd) => pd.private_isArchived); @@ -3710,7 +3731,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with this privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, {isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined, {isSearching: true}); // Then the personal detail options should have the correct private_isArchived values const misterFantasticOption = result.personalDetails.find((pd) => pd.item?.accountID === 1); @@ -3729,7 +3750,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with this privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined); // Then the report options should have the correct private_isArchived values const report3Option = result.reports.find((r) => r.item?.reportID === '3'); @@ -3748,7 +3769,7 @@ describe('OptionsListUtils', () => { const emptyMap: PrivateIsArchivedMap = {}; // When we call createFilteredOptionList with an empty privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, emptyMap, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, emptyMap, undefined, undefined); // Then reports NOT in Onyx (like report 3, 5) should not have private_isArchived set // Note: Report 10 gets private_isArchived from Onyx (set in beforeAll) @@ -3769,7 +3790,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with this privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined); // Then the report options should have the correct private_isArchived values const report1Option = result.reports.find((r) => r.item?.reportID === '1'); @@ -3796,7 +3817,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with a maxRecentReports limit that includes all reports - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, { + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined, { maxRecentReports: 20, }); @@ -4317,6 +4338,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const result = createOption({ + conciergeReportID: undefined, accountIDs: [1, 2], personalDetails: PERSONAL_DETAILS, report, @@ -4355,6 +4377,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const result = createOption({ + conciergeReportID: undefined, accountIDs: [1, 2], personalDetails: PERSONAL_DETAILS, report, @@ -4383,6 +4406,7 @@ describe('OptionsListUtils', () => { // Should not throw when reports is undefined const result = createOption({ + conciergeReportID: undefined, accountIDs: [1, 2], personalDetails: PERSONAL_DETAILS, report, @@ -4585,6 +4609,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -4650,6 +4675,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -4700,6 +4726,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -4750,6 +4777,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -4800,6 +4828,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -4849,6 +4878,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -4886,6 +4916,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -4949,6 +4980,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -4998,6 +5030,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5033,6 +5066,7 @@ describe('OptionsListUtils', () => { [movedTransactionAction.reportActionID]: movedTransactionAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5061,6 +5095,7 @@ describe('OptionsListUtils', () => { [submittedAction.reportActionID]: submittedAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5090,6 +5125,7 @@ describe('OptionsListUtils', () => { [approvedAction.reportActionID]: approvedAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5121,6 +5157,7 @@ describe('OptionsListUtils', () => { [forwardedAction.reportActionID]: forwardedAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5149,6 +5186,7 @@ describe('OptionsListUtils', () => { [forwardedAction.reportActionID]: forwardedAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5175,6 +5213,7 @@ describe('OptionsListUtils', () => { [corporateForceUpgradeAction.reportActionID]: corporateForceUpgradeAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5200,6 +5239,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5224,6 +5264,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5251,6 +5292,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5275,6 +5317,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5299,6 +5342,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5322,6 +5366,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5345,6 +5390,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5372,6 +5418,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5399,6 +5446,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5425,6 +5473,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5452,6 +5501,7 @@ describe('OptionsListUtils', () => { [action.reportActionID]: action, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5475,6 +5525,7 @@ describe('OptionsListUtils', () => { [takeControlAction.reportActionID]: takeControlAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5499,6 +5550,7 @@ describe('OptionsListUtils', () => { [rerouteAction.reportActionID]: rerouteAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5523,6 +5575,7 @@ describe('OptionsListUtils', () => { [movedAction.reportActionID]: movedAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5551,6 +5604,7 @@ describe('OptionsListUtils', () => { // When getting the last message text for the report const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5581,6 +5635,7 @@ describe('OptionsListUtils', () => { const expectedVisibleText = ''; const result = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5630,6 +5685,7 @@ describe('OptionsListUtils', () => { // When we get the last message text while the mentioned user is absent from personal details const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, personalDetails: undefined, translate: translateLocal, report, @@ -5652,6 +5708,7 @@ describe('OptionsListUtils', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, report); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5685,6 +5742,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const result = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5731,6 +5789,7 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); const result = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, report, @@ -5778,6 +5837,7 @@ describe('OptionsListUtils', () => { [submittedAction.reportActionID]: submittedAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5815,6 +5875,7 @@ describe('OptionsListUtils', () => { [dewSubmitFailedAction.reportActionID]: dewSubmitFailedAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5848,6 +5909,7 @@ describe('OptionsListUtils', () => { [dewSubmitFailedAction.reportActionID]: dewSubmitFailedAction, }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5889,6 +5951,7 @@ describe('OptionsListUtils', () => { }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5933,6 +5996,7 @@ describe('OptionsListUtils', () => { }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -5975,6 +6039,7 @@ describe('OptionsListUtils', () => { path === 'workspace.common.unavailable' ? 'UnavailableMarker' : translateLocal(path, ...parameters); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, personalDetails: undefined, translate: translateWithUnavailableMarker, report, @@ -6008,6 +6073,7 @@ describe('OptionsListUtils', () => { }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, translate: translateLocal, report, @@ -6040,6 +6106,7 @@ describe('OptionsListUtils', () => { }); const lastMessage = getLastMessageTextForReport({ + conciergeReportID: undefined, currentUserAccountID: CURRENT_USER_ACCOUNT_ID, personalDetails: undefined, translate: translateLocal, @@ -6340,7 +6407,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, undefined); // Then it should return an option with isSelfDM and alternateText set expect(result.isSelfDM).toBe(true); @@ -6360,7 +6427,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, undefined); // Then it should return an option with invoice room text and alternateText expect(result.isInvoiceRoom).toBe(true); @@ -6382,7 +6449,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, undefined); // Then it should return an option with unknownUserDetails data expect(result.text).toBe('Unknown User'); @@ -6403,7 +6470,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, undefined); // Then it should return an option with workspace name expect(result.text).toBe(POLICY.name); @@ -6432,7 +6499,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, undefined); // Then it should use the custom personalDetails parameter expect(result).toBeDefined(); @@ -6449,7 +6516,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, undefined); // Then it should not throw and return a valid option expect(result).toBeDefined(); @@ -6461,7 +6528,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, undefined); // Then it should return a valid option (createOption handles undefined) expect(result).toBeDefined(); @@ -6900,7 +6967,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, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -6923,7 +6990,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, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -6943,7 +7010,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, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -6967,7 +7034,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, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -6991,7 +7058,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, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -8237,6 +8304,7 @@ describe('OptionsListUtils', () => { // When we call createOption with the linked chat report const result = createOption({ + conciergeReportID: undefined, accountIDs: [1, 2], personalDetails: PERSONAL_DETAILS, report: expenseReport, @@ -8273,7 +8341,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, undefined); // Then the option should be created successfully using the reports collection expect(option).toBeDefined(); @@ -8323,6 +8391,30 @@ describe('OptionsListUtils', () => { }); describe('createOptionFromReport', () => { + it('should subtitle the option as Concierge support only when the threaded conciergeReportID matches the report', () => { + const report: Report = { + reportID: 'concierge-option-1', + reportName: 'Chat', + type: CONST.REPORT.TYPE.CHAT, + participants: { + [CURRENT_USER_ACCOUNT_ID]: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, + }, + 1: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, + }, + }, + }; + + // When the threaded conciergeReportID matches the report + const conciergeOption = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, undefined, report.reportID); + expect(conciergeOption.subtitle).toBe(translateLocal('reportActionsView.conciergeSupport')); + + // And an identical report with a non-matching conciergeReportID is not treated as Concierge + const regularOption = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, undefined, 'a-different-report-id'); + expect(regularOption.subtitle).not.toBe(translateLocal('reportActionsView.conciergeSupport')); + }); + it('should create an option from a report with all required parameters', () => { const report: Report = { reportID: '1', @@ -8340,7 +8432,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions); + const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions, undefined); expect(result).toBeDefined(); expect(result.reportID).toBe('1'); @@ -8364,7 +8456,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, true, undefined, sortedActions); + const result = createOptionFromReport(report, PERSONAL_DETAILS, true, undefined, sortedActions, undefined); expect(result).toBeDefined(); expect(result.private_isArchived).toBe(true); @@ -8387,7 +8479,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions); + const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions, undefined); expect(result).toBeDefined(); expect(result.private_isArchived).toBeUndefined(); @@ -8410,7 +8502,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions); + const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions, undefined); expect(result).toBeDefined(); expect(result.reportID).toBe('1'); @@ -8434,7 +8526,7 @@ describe('OptionsListUtils', () => { const sortedActions = {[report.reportID]: [reportAction]}; const config = {showPersonalDetails: true}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions, undefined, config); + const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions, undefined, undefined, config); expect(result).toBeDefined(); expect(result.reportID).toBe('1'); @@ -8443,7 +8535,7 @@ describe('OptionsListUtils', () => { describe('createFilteredOptionList', () => { it('should return report options limited by maxRecentReports', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {maxRecentReports: 5}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {maxRecentReports: 5}); expect(result).toBeDefined(); expect(result.reports.length).toBeLessThanOrEqual(5); @@ -8495,13 +8587,13 @@ describe('OptionsListUtils', () => { }, }; - const result = createFilteredOptionList(PERSONAL_DETAILS, reportsWithDates, undefined, {}, undefined, {maxRecentReports: 3}); + const result = createFilteredOptionList(PERSONAL_DETAILS, reportsWithDates, undefined, {}, undefined, undefined, {maxRecentReports: 3}); expect(result.reports.length).toBeGreaterThan(0); }); it('should include personal details when includeP2P is true', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {includeP2P: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {includeP2P: true}); expect(result).toBeDefined(); expect(result.personalDetails).toBeDefined(); @@ -8509,21 +8601,21 @@ describe('OptionsListUtils', () => { }); it('should exclude personal details when includeP2P is false', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {includeP2P: false}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {includeP2P: false}); expect(result).toBeDefined(); expect(result.personalDetails.length).toBe(0); }); it('should handle empty reports collection', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, {}, undefined, {}, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, {}, undefined, {}, undefined, undefined); expect(result).toBeDefined(); expect(result.reports.length).toBe(0); }); it('should handle undefined reports collection', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, undefined, undefined, {}, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, undefined, undefined, {}, undefined, undefined); expect(result).toBeDefined(); expect(result.reports.length).toBe(0); @@ -8553,37 +8645,30 @@ describe('OptionsListUtils', () => { [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}999`]: true, }; - const result = createFilteredOptionList(PERSONAL_DETAILS, reportsCollection, undefined, privateIsArchivedMap, undefined, {maxRecentReports: 10}); + const result = createFilteredOptionList(PERSONAL_DETAILS, reportsCollection, undefined, privateIsArchivedMap, undefined, undefined, {maxRecentReports: 10}); expect(result).toBeDefined(); }); it('should handle isSearching filtering', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {isSearching: true}); expect(result).toBeDefined(); expect(result.reports.length).toBe(Object.keys(REPORTS).length); }); it('should return all reports when isSearching is true', () => { - const result = createFilteredOptionList( - PERSONAL_DETAILS, - REPORTS, - undefined, - {}, - {}, - { - isSearching: true, - maxRecentReports: 2, - }, - ); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, {}, undefined, { + isSearching: true, + maxRecentReports: 2, + }); expect(result).toBeDefined(); expect(result.reports.length).toBe(Object.keys(REPORTS).length); }); it('should return both reports and personal details', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined); expect(result).toBeDefined(); expect(result).toHaveProperty('reports'); @@ -8593,27 +8678,27 @@ describe('OptionsListUtils', () => { // The SearchRouter relies on this: its empty-query state renders recent reports only, // so contacts must not be built until the user starts searching. it('should not build personal details when deferContactsUntilSearch is true and not searching', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {deferContactsUntilSearch: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {deferContactsUntilSearch: true}); expect(result.reports.length).toBeGreaterThan(0); expect(result.personalDetails.length).toBe(0); }); it('should build personal details when deferContactsUntilSearch is true and searching', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {deferContactsUntilSearch: true, isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {deferContactsUntilSearch: true, isSearching: true}); expect(result.personalDetails.length).toBeGreaterThan(0); }); it('should keep top-level fields of returned options mutable while the cached entry stays pristine', () => { - const first = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined); + const first = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined); const firstOption = first.personalDetails.at(0); expect(firstOption).toBeDefined(); if (firstOption) { firstOption.isSelected = true; } - const second = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined); + const second = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined); const secondOption = second.personalDetails.at(0); // Same cache entry (nested objects are shared between clones), but each caller gets fresh top-level objects. @@ -8625,7 +8710,7 @@ describe('OptionsListUtils', () => { // The cached entry is frozen in dev, so a consumer that mutates a nested object shared with the // cache throws instead of silently corrupting the results returned to every other screen. it('should throw when a nested object shared with the cache is mutated', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined); const icons = result.personalDetails.at(0)?.icons; expect(icons?.length).toBeGreaterThan(0); @@ -8635,7 +8720,7 @@ describe('OptionsListUtils', () => { // Onyx snapshot objects referenced by the options are shared with the whole app and existing code // still writes to them in place, so the dev freeze must leave them untouched (see deepFreeze). it('should not freeze the Onyx snapshot objects referenced by cached options', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined); const personalDetailItem = result.personalDetails.at(0)?.item; const reportItem = result.reports.at(0)?.item; @@ -8760,7 +8845,7 @@ describe('OptionsListUtils', () => { describe('policy parameter passing', () => { it('createFilteredOptionList should accept policiesCollection parameter', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, undefined); expect(result).toBeDefined(); expect(result.reports).toBeDefined(); expect(result.personalDetails).toBeDefined(); @@ -8784,7 +8869,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, POLICY, sortedActions); + const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, POLICY, sortedActions, undefined); expect(result).toBeDefined(); expect(result.policyID).toBe(policyID); }); @@ -8805,7 +8890,7 @@ describe('OptionsListUtils', () => { }, }; - const result = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, POLICY); + const result = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, POLICY, undefined); expect(result).toBeDefined(); expect(result.policyID).toBe(policyID); }); diff --git a/tests/unit/ReportNameUtilsTest.ts b/tests/unit/ReportNameUtilsTest.ts index 483e1d85d579..d3f1e99150a7 100644 --- a/tests/unit/ReportNameUtilsTest.ts +++ b/tests/unit/ReportNameUtilsTest.ts @@ -45,6 +45,7 @@ describe('ReportNameUtils', () => { currentUserID = currentUserAccountID, ) => computeReportNameOriginal({ + conciergeReportID: undefined, report, reports, policies, @@ -294,6 +295,7 @@ describe('ReportNameUtils', () => { await Onyx.merge(ONYXKEYS.SESSION, {accountID: currentUserAccountID, email: 'lagertha2@vikings.net', authTokenType: CONST.AUTH_TOKEN_TYPES.SUPPORT}); const translateWithYouMarker: LocalizedTranslate = (path, ...parameters) => (path === 'common.you' ? 'You Marker' : translateLocal(path, ...parameters)); const name = computeReportNameOriginal({ + conciergeReportID: undefined, report, reports: emptyCollections.reports, policies: emptyCollections.policies, @@ -581,6 +583,7 @@ describe('ReportNameUtils', () => { } as OnyxCollection; const name = computeReportNameOriginal({ + conciergeReportID: undefined, report: thread, reports: emptyCollections.reports, policies: emptyCollections.policies, @@ -1659,4 +1662,34 @@ describe('ReportNameUtils', () => { expect(name).toBe(translate(CONST.LOCALES.EN, 'iou.expense')); }); }); + describe('concierge chat name', () => { + it('names the chat Concierge only when the threaded conciergeReportID matches the report', () => { + const report: Report = { + ...createRegularChat(1, [currentUserAccountID, 1]), + reportID: 'concierge-name-1', + }; + + // When the threaded conciergeReportID matches the report + const nameWithMatchingID = computeReportNameOriginal({ + conciergeReportID: 'concierge-name-1', + report, + currentUserAccountID, + currentUserLogin, + translate: translateLocal, + isTrackIntentUser: false, + }); + expect(nameWithMatchingID).toBe(CONST.CONCIERGE_DISPLAY_NAME); + + // And an identical report with a non-matching conciergeReportID keeps its regular name + const nameWithDifferentID = computeReportNameOriginal({ + conciergeReportID: 'a-different-report-id', + report, + currentUserAccountID, + currentUserLogin, + translate: translateLocal, + isTrackIntentUser: false, + }); + expect(nameWithDifferentID).not.toBe(CONST.CONCIERGE_DISPLAY_NAME); + }); + }); }); From 8108bfb2c649f2afc485f924579a7b91c8c89295 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Sun, 26 Jul 2026 00:12:05 +0700 Subject: [PATCH 2/9] createFilteredOptionList calls to include undefined for optional parameters and add tests for conciergeReportID handling in SearchStaticList --- tests/perf-test/OptionsListUtils.perf-test.ts | 8 +- tests/unit/ReportNameUtilsTest.ts | 2 + tests/unit/ReportUtilsTest.ts | 16 +++ tests/unit/SearchAutocompleteListTest.tsx | 46 +++++- tests/unit/SearchStaticListTest.tsx | 135 ++++++++++++++++++ 5 files changed, 199 insertions(+), 8 deletions(-) create mode 100644 tests/unit/SearchStaticListTest.tsx diff --git a/tests/perf-test/OptionsListUtils.perf-test.ts b/tests/perf-test/OptionsListUtils.perf-test.ts index 99ef384a7eb9..761eddf2b5d3 100644 --- a/tests/perf-test/OptionsListUtils.perf-test.ts +++ b/tests/perf-test/OptionsListUtils.perf-test.ts @@ -98,7 +98,7 @@ jest.mock('@react-navigation/native', () => { }); const EMPTY_PRIVATE_IS_ARCHIVED_MAP: PrivateIsArchivedMap = {}; -const options = createFilteredOptionList(personalDetails, reports, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {isSearching: true}); +const options = createFilteredOptionList(personalDetails, reports, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, {isSearching: true}); const ValidOptionsConfig = { betas: mockedBetas, @@ -283,7 +283,7 @@ describe('OptionsListUtils', () => { await measureFunction(() => { // Inputs are referentially identical across measured runs, so clear the cache to measure the build path. clearFilteredOptionListCache(); - return createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + return createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { maxRecentReports: 500, isSearching: false, }); @@ -293,7 +293,7 @@ describe('OptionsListUtils', () => { test('[OptionsListUtils] createFilteredOptionList with isSearching is true', async () => { await waitForBatchedUpdates(); await measureFunction(() => - createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { maxRecentReports: 500, isSearching: true, }), @@ -302,7 +302,7 @@ describe('OptionsListUtils', () => { test('[OptionsListUtils] getSearchOptions with isSearching is true', async () => { await waitForBatchedUpdates(); - const optionLists = createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + const optionLists = createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { maxRecentReports: 500, isSearching: true, }); diff --git a/tests/unit/ReportNameUtilsTest.ts b/tests/unit/ReportNameUtilsTest.ts index d3f1e99150a7..b1c832dbe25e 100644 --- a/tests/unit/ReportNameUtilsTest.ts +++ b/tests/unit/ReportNameUtilsTest.ts @@ -1673,6 +1673,7 @@ describe('ReportNameUtils', () => { const nameWithMatchingID = computeReportNameOriginal({ conciergeReportID: 'concierge-name-1', report, + transactions: undefined, currentUserAccountID, currentUserLogin, translate: translateLocal, @@ -1684,6 +1685,7 @@ describe('ReportNameUtils', () => { const nameWithDifferentID = computeReportNameOriginal({ conciergeReportID: 'a-different-report-id', report, + transactions: undefined, currentUserAccountID, currentUserLogin, translate: translateLocal, diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 38d9a110de96..2122350dd2f3 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -3859,6 +3859,22 @@ describe('ReportUtils', () => { expect(result).toBe('Your space'); }); + it('should return the Concierge subtitle only when the threaded conciergeReportID matches the report', () => { + const report = { + ...createRandomReport(2, undefined), + reportID: 'concierge-subtitle-1', + type: CONST.REPORT.TYPE.CHAT, + }; + + // When the threaded conciergeReportID matches the report + const conciergeSubtitle = getChatRoomSubtitle(report, undefined, report.reportID, translateLocal); + expect(conciergeSubtitle).toBe(translateLocal('reportActionsView.conciergeSupport')); + + // And an identical report with a non-matching conciergeReportID is not treated as Concierge + const regularSubtitle = getChatRoomSubtitle(report, undefined, 'a-different-report-id', translateLocal); + expect(regularSubtitle).not.toBe(translateLocal('reportActionsView.conciergeSupport')); + }); + it('should return "Invoices" for invoice room', () => { const report = createInvoiceRoom(1); const result = getChatRoomSubtitle(report, policy, undefined, translateLocal); diff --git a/tests/unit/SearchAutocompleteListTest.tsx b/tests/unit/SearchAutocompleteListTest.tsx index b68e03593507..3fcc305def9b 100644 --- a/tests/unit/SearchAutocompleteListTest.tsx +++ b/tests/unit/SearchAutocompleteListTest.tsx @@ -8,6 +8,7 @@ import SearchRouter from '@components/Search/SearchRouter/SearchRouter'; import type {PrivateIsArchivedMap} from '@hooks/usePrivateIsArchivedMap'; import type * as OptionsListUtilsModule from '@libs/OptionsListUtils'; +import * as OptionsListUtils from '@libs/OptionsListUtils'; import {createFilteredOptionList} from '@libs/OptionsListUtils'; import Navigation from '@navigation/Navigation'; @@ -66,9 +67,13 @@ jest.mock('@src/libs/Navigation/Navigation', () => ({ navigate: jest.fn(), })); +const mockRootNavigationState = jest.fn((): {contextualReportID: string | undefined; isSearchRouterScreen: boolean} => ({ + contextualReportID: undefined, + isSearchRouterScreen: false, +})); jest.mock('@src/hooks/useRootNavigationState', () => ({ __esModule: true, - default: () => ({contextualReportID: undefined, isSearchRouterScreen: false}), + default: () => mockRootNavigationState(), })); jest.mock('@hooks/useExportedToFilterOptions', () => ({ @@ -139,7 +144,7 @@ const mockedReports = getMockedReports(10); const mockedBetas = Object.values(CONST.BETAS); const mockedPersonalDetails = getMockedPersonalDetails(10); const EMPTY_PRIVATE_IS_ARCHIVED_MAP: PrivateIsArchivedMap = {}; -const mockedOptions = createFilteredOptionList(mockedPersonalDetails, mockedReports, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {isSearching: true}); +const mockedOptions = createFilteredOptionList(mockedPersonalDetails, mockedReports, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, {isSearching: true}); const mockOnClose = jest.fn((afterClose?: () => void) => { afterClose?.(); @@ -200,6 +205,7 @@ describe('SearchAutocompleteList', () => { }); jest.clearAllMocks(); mockUseNavigationSuggestions.mockReturnValue([]); + mockRootNavigationState.mockReturnValue({contextualReportID: undefined, isSearchRouterScreen: false}); }); it('should display and select navigation suggestion rows', async () => { @@ -262,14 +268,46 @@ describe('SearchAutocompleteList', () => { expect(screen.getByText('type:chat')).toBeTruthy(); }); + it('builds the contextual search option with the threaded conciergeReportID', async () => { + // A report that exists in Onyx but is NOT part of the recent-report options, so the contextual + // search suggestion has to build its option through createOptionFromReport + const contextualReportID = 'contextual-99'; + const currentUserAccountID = 1; + const contextualReport = { + ...createRandomReport(99, undefined), + reportID: contextualReportID, + participants: {[currentUserAccountID]: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}}, + }; + mockRootNavigationState.mockReturnValue({contextualReportID, isSearchRouterScreen: true}); + const createOptionFromReportSpy = jest.spyOn(OptionsListUtils, 'createOptionFromReport'); + + await waitForBatchedUpdates(); + await Onyx.multiSet({ + ...mockedReports, + [ONYXKEYS.BETAS]: mockedBetas, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: mockedPersonalDetails, + }); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${contextualReportID}`, contextualReport); + await Onyx.set(ONYXKEYS.SESSION, {accountID: currentUserAccountID, email: 'test@test.com'}); + await Onyx.set(ONYXKEYS.CONCIERGE_REPORT_ID, 'concierge-router-1'); + + render(); + await flushAllUpdates(); + + // Then the contextual report (not part of recent reports) is built through createOptionFromReport + // with the conciergeReportID threaded from Onyx (#66411) + expect(createOptionFromReportSpy).toHaveBeenCalled(); + expect(createOptionFromReportSpy.mock.calls.at(0)?.at(5)).toBe('concierge-router-1'); + }); + describe('two-section chat switcher', () => { // These tests use a controlled getSearchOptions mock to verify the section splitting logic // introduced for stable two-section chat switcher results (local + server). let getSearchOptionsSpy: jest.SpyInstance; beforeEach(() => { - const OptionsListUtils = jest.requireActual('@libs/OptionsListUtils'); - getSearchOptionsSpy = jest.spyOn(OptionsListUtils, 'getSearchOptions').mockReturnValue({ + const actualOptionsListUtils = jest.requireActual('@libs/OptionsListUtils'); + getSearchOptionsSpy = jest.spyOn(actualOptionsListUtils, 'getSearchOptions').mockReturnValue({ options: { recentReports: fakeRecentReports, personalDetails: [], diff --git a/tests/unit/SearchStaticListTest.tsx b/tests/unit/SearchStaticListTest.tsx new file mode 100644 index 000000000000..4e21a6e4f0c7 --- /dev/null +++ b/tests/unit/SearchStaticListTest.tsx @@ -0,0 +1,135 @@ +import {render, screen} from '@testing-library/react-native'; + +import SearchStaticList from '@components/Search/SearchStaticList'; + +import {buildSearchQueryJSON} from '@libs/SearchQueryUtils'; +import * as SearchUIUtils from '@libs/SearchUIUtils'; + +import CONST from '@src/CONST'; +import type {SearchResults} from '@src/types/onyx'; + +import type * as NativeNavigation from '@react-navigation/native'; + +import React from 'react'; + +const mockConciergeReportID = jest.fn((): string | undefined => undefined); + +jest.mock('@components/OnyxListItemProvider', () => ({ + useSession: jest.fn(() => ({accountID: 999, email: 'me@expensify.com'})), +})); +jest.mock('@hooks/useOnyx', () => + jest.fn((key: string) => { + // ONYXKEYS.CONCIERGE_REPORT_ID — the key cannot be imported inside a jest.mock factory + if (key === 'conciergeReportID') { + return [mockConciergeReportID()]; + } + return [undefined]; + }), +); +jest.mock('@hooks/useCurrencyList', () => ({ + useCurrencyListActions: jest.fn(() => ({convertToDisplayString: (amount?: number) => String(amount ?? 0)})), +})); +jest.mock('@hooks/useLocalize', () => + jest.fn(() => ({ + translate: (...args: unknown[]) => JSON.stringify(args), + localeCompare: (a: string, b: string) => a.localeCompare(b), + formatPhoneNumber: (phone: string) => phone, + })), +); +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native'); + return { + ...actualNav, + useFocusEffect: jest.fn(), + useIsFocused: () => true, + createNavigationContainerRef: () => ({ + addListener: () => jest.fn(), + removeListener: () => jest.fn(), + isReady: () => jest.fn(), + getCurrentRoute: () => jest.fn(), + getState: () => jest.fn(), + }), + }; +}); +jest.mock('@libs/Navigation/Navigation', () => ({ + navigate: jest.fn(), + getIsFullscreenPreInsertedUnderRHP: jest.fn(() => false), +})); +jest.mock('@libs/Navigation/navigationRef', () => ({ + __esModule: true, + default: { + getRootState: jest.fn(() => ({routes: []})), + isReady: jest.fn(() => false), + addListener: jest.fn(), + removeListener: jest.fn(), + getCurrentRoute: jest.fn(), + }, +})); +jest.mock('@libs/deferredLayoutWrite', () => ({ + hasDeferredWrite: jest.fn(() => false), +})); +jest.mock('@components/TransactionItemRow', () => jest.fn(() => null)); +jest.mock('@components/Skeletons/SearchRowSkeleton', () => jest.fn(() => null)); +jest.mock('@components/StatusBadge', () => jest.fn(() => null)); + +// getSections consumes conciergeReportID (via createOption/getReportName) — keep SearchUIUtils real so the +// threading through SearchStaticList's getSections call is actually executed by this test. +const getSectionsSpy = jest.spyOn(SearchUIUtils, 'getSections'); + +const EMPTY_SEARCH_RESULTS: SearchResults = { + search: { + offset: 0, + type: CONST.SEARCH.DATA_TYPES.EXPENSE, + hash: 1, + hasMoreResults: false, + hasResults: false, + isLoading: false, + sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, + sortOrder: CONST.SEARCH.SORT_ORDER.DESC, + }, + data: { + personalDetailsList: {}, + }, +}; + +describe('SearchStaticList', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders and threads the conciergeReportID from Onyx into getSections', () => { + mockConciergeReportID.mockReturnValue('concierge-static-list-1'); + const queryJSON = buildSearchQueryJSON('type:expense'); + if (!queryJSON) { + throw new Error('failed to build queryJSON'); + } + + render( + , + ); + + // Then the component renders without crashing and passes the threaded conciergeReportID to getSections + expect(screen.toJSON()).toBeTruthy(); + expect(getSectionsSpy).toHaveBeenCalledWith(expect.objectContaining({conciergeReportID: 'concierge-static-list-1'})); + }); + + it('passes undefined to getSections while the Onyx value has not loaded yet', () => { + mockConciergeReportID.mockReturnValue(undefined); + const queryJSON = buildSearchQueryJSON('type:expense'); + if (!queryJSON) { + throw new Error('failed to build queryJSON'); + } + + render( + , + ); + + expect(getSectionsSpy).toHaveBeenCalledWith(expect.objectContaining({conciergeReportID: undefined})); + }); +}); From 62f4908ae30304859d96cf7ec283d2d013eaba8b Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Mon, 27 Jul 2026 20:50:20 +0700 Subject: [PATCH 3/9] use structured parameters, ensuring conciergeReportID is consistently handled across components --- .../Search/FilterComponents/InSelector.tsx | 11 +- .../Search/SearchRouter/SearchRouter.tsx | 16 +- src/hooks/useFilteredOptions.ts | 10 +- src/libs/OptionsListUtils/index.ts | 46 ++++-- tests/perf-test/OptionsListUtils.perf-test.ts | 11 +- tests/unit/OptionsListUtilsTest.tsx | 155 +++++++++++------- tests/unit/SearchAutocompleteListTest.tsx | 4 +- 7 files changed, 144 insertions(+), 109 deletions(-) diff --git a/src/components/Search/FilterComponents/InSelector.tsx b/src/components/Search/FilterComponents/InSelector.tsx index af9ab9f742b7..f8d68d9b2100 100644 --- a/src/components/Search/FilterComponents/InSelector.tsx +++ b/src/components/Search/FilterComponents/InSelector.tsx @@ -81,19 +81,16 @@ function InSelector({value = [], selectionListTextInputStyle, selectionListStyle const reportPolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${reportData?.policyID}`]; const report = { ...getSelectedOptionData( - createOptionFromReport( - {...reportData, reportID: id}, + createOptionFromReport({ + report: {...reportData, reportID: id}, personalDetails, privateIsArchived, - reportPolicy, + policy: reportPolicy, sortedActions, conciergeReportID, reportAttributesDerived, - undefined, - undefined, - undefined, isTrackIntentUser, - ), + }), ), isSelected, }; diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index a732c06d85b5..1c30ff9859b1 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -213,21 +213,18 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla return undefined; } - const option = createOptionFromReport( - contextualReport, + const option = createOptionFromReport({ + report: contextualReport, personalDetails, - contextualReportNVP, - contextualReportPolicy, + privateIsArchived: contextualReportNVP, + policy: contextualReportPolicy, sortedActions, conciergeReportID, - undefined, - { + config: { showPersonalDetails: true, }, - undefined, - undefined, isTrackIntentUser, - ); + }); reportForContextualSearch = option; } @@ -281,6 +278,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla }, [ contextualReportID, + conciergeReportID, navigationSuggestions, textInputValue, isSearchRouterDisplayed, diff --git a/src/hooks/useFilteredOptions.ts b/src/hooks/useFilteredOptions.ts index 9e4d93de1736..3502ef3f7a96 100644 --- a/src/hooks/useFilteredOptions.ts +++ b/src/hooks/useFilteredOptions.ts @@ -107,14 +107,7 @@ function useFilteredOptions(config: UseFilteredOptionsConfig = {}): UseFilteredO reportAttributesDerived, privateIsArchivedMap, allPolicies, - conciergeReportID, - { - maxRecentReports: reportsLimit, - includeP2P, - isSearching, - deferContactsUntilSearch, - locale: preferredLocale, - }, + {conciergeReportID, maxRecentReports: reportsLimit, includeP2P, isSearching, deferContactsUntilSearch, locale: preferredLocale}, undefined, undefined, isTrackIntentUser, @@ -128,6 +121,7 @@ function useFilteredOptions(config: UseFilteredOptionsConfig = {}): UseFilteredO reportAttributesDerived, privateIsArchivedMap, allPolicies, + conciergeReportID, reportsLimit, includeP2P, isSearching, diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 017e40ca410b..27d446f7531b 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -1664,8 +1664,8 @@ function createFilteredOptionList( reportAttributesDerived: ReportAttributesDerivedValue['reports'] | undefined, privateIsArchivedMap: PrivateIsArchivedMap, policiesCollection: OnyxCollection, - conciergeReportID: string | undefined, options: { + conciergeReportID: string | undefined; maxRecentReports?: number; includeP2P?: boolean; isSearching?: boolean; @@ -1677,14 +1677,14 @@ function createFilteredOptionList( */ deferContactsUntilSearch?: boolean; locale?: Locale; - } = {}, + }, policyTags?: OnyxCollection, visibleReportActionsData: VisibleReportActionsDerivedValue = EMPTY_VISIBLE_REPORT_ACTIONS, isTrackIntentUser?: boolean, // TODO: Remove optional (?) once all callers pass sortedActions. Refactor issue: https://github.com/Expensify/App/issues/66381 sortedActions?: Record, ): OptionList { - const {maxRecentReports = 500, includeP2P = true, isSearching = false, deferContactsUntilSearch = false, locale} = options; + const {conciergeReportID, maxRecentReports = 500, includeP2P = true, isSearching = false, deferContactsUntilSearch = false, locale} = options; // Contacts are expensive to build on large accounts (one option per personal detail). When a screen // opts into deferral and is not actively searching, skip building them entirely; the empty state @@ -1850,19 +1850,33 @@ function createFilteredOptionList( return cloneOptionList(result); } -function createOptionFromReport( - report: Report, - personalDetails: OnyxEntry, - privateIsArchived: boolean | undefined, - policy: OnyxEntry, - sortedActions: Record | undefined, - conciergeReportID: string | undefined, - reportAttributesDerived?: ReportAttributesDerivedValue['reports'], - config?: PreviewConfig, - policyTags?: OnyxEntry, - visibleReportActionsData: VisibleReportActionsDerivedValue = {}, - isTrackIntentUser?: boolean, -) { +type CreateOptionFromReportParams = { + report: Report; + personalDetails: OnyxEntry; + privateIsArchived: boolean | undefined; + policy: OnyxEntry; + sortedActions: Record | undefined; + conciergeReportID: string | undefined; + reportAttributesDerived?: ReportAttributesDerivedValue['reports']; + config?: PreviewConfig; + policyTags?: OnyxEntry; + visibleReportActionsData?: VisibleReportActionsDerivedValue; + isTrackIntentUser?: boolean; +}; + +function createOptionFromReport({ + report, + personalDetails, + privateIsArchived, + policy, + sortedActions, + conciergeReportID, + reportAttributesDerived, + config, + policyTags, + visibleReportActionsData = {}, + isTrackIntentUser, +}: CreateOptionFromReportParams) { const accountIDs = getParticipantsAccountIDsForDisplay(report); return { diff --git a/tests/perf-test/OptionsListUtils.perf-test.ts b/tests/perf-test/OptionsListUtils.perf-test.ts index 761eddf2b5d3..952be80a3e27 100644 --- a/tests/perf-test/OptionsListUtils.perf-test.ts +++ b/tests/perf-test/OptionsListUtils.perf-test.ts @@ -98,7 +98,7 @@ jest.mock('@react-navigation/native', () => { }); const EMPTY_PRIVATE_IS_ARCHIVED_MAP: PrivateIsArchivedMap = {}; -const options = createFilteredOptionList(personalDetails, reports, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, {isSearching: true}); +const options = createFilteredOptionList(personalDetails, reports, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {conciergeReportID: undefined, isSearching: true}); const ValidOptionsConfig = { betas: mockedBetas, @@ -283,7 +283,8 @@ describe('OptionsListUtils', () => { await measureFunction(() => { // Inputs are referentially identical across measured runs, so clear the cache to measure the build path. clearFilteredOptionListCache(); - return createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + return createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, maxRecentReports: 500, isSearching: false, }); @@ -293,7 +294,8 @@ describe('OptionsListUtils', () => { test('[OptionsListUtils] createFilteredOptionList with isSearching is true', async () => { await waitForBatchedUpdates(); await measureFunction(() => - createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, maxRecentReports: 500, isSearching: true, }), @@ -302,7 +304,8 @@ describe('OptionsListUtils', () => { test('[OptionsListUtils] getSearchOptions with isSearching is true', async () => { await waitForBatchedUpdates(); - const optionLists = createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + const optionLists = createFilteredOptionList(personalDetails, mockedReportsMap, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, maxRecentReports: 500, isSearching: true, }); diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 63132771066f..90b734da7d4b 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -824,15 +824,17 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}10`, reportNameValuePairs); await waitForBatchedUpdates(); - OPTIONS = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, MOCK_REPORT_ATTRIBUTES_DERIVED, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, undefined, {isSearching: true}); + OPTIONS = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, MOCK_REPORT_ATTRIBUTES_DERIVED, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, { + conciergeReportID: undefined, + isSearching: true, + }); OPTIONS_WITH_CONCIERGE = createFilteredOptionList( PERSONAL_DETAILS_WITH_CONCIERGE, REPORTS_WITH_CONCIERGE, MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CONCIERGE, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, - undefined, - {isSearching: true}, + {conciergeReportID: undefined, isSearching: true}, ); OPTIONS_WITH_CHRONOS = createFilteredOptionList( PERSONAL_DETAILS_WITH_CHRONOS, @@ -840,8 +842,7 @@ describe('OptionsListUtils', () => { MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CHRONOS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, - undefined, - {isSearching: true}, + {conciergeReportID: undefined, isSearching: true}, ); OPTIONS_WITH_RECEIPTS = createFilteredOptionList( PERSONAL_DETAILS_WITH_RECEIPTS, @@ -849,8 +850,7 @@ describe('OptionsListUtils', () => { MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_RECEIPTS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, - undefined, - {isSearching: true}, + {conciergeReportID: undefined, isSearching: true}, ); OPTIONS_WITH_WORKSPACE_ROOM = createFilteredOptionList( PERSONAL_DETAILS, @@ -858,8 +858,7 @@ describe('OptionsListUtils', () => { MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_WORKSPACE_ROOM, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, - undefined, - {isSearching: true}, + {conciergeReportID: undefined, isSearching: true}, ); }); @@ -927,8 +926,7 @@ describe('OptionsListUtils', () => { createMockReportAttributesDerived(memberWorkspaceChat, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID), EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, - undefined, - {isSearching: true}, + {conciergeReportID: undefined, isSearching: true}, ); // When we call getSearchOptions @@ -2406,7 +2404,10 @@ describe('OptionsListUtils', () => { const archivedMap: PrivateIsArchivedMap = { [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}10`]: !!reportNameValuePairs.private_isArchived, }; - const OPTIONS_WITH_ARCHIVED = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, MOCK_REPORT_ATTRIBUTES_DERIVED, archivedMap, undefined, undefined, {isSearching: true}); + const OPTIONS_WITH_ARCHIVED = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, MOCK_REPORT_ATTRIBUTES_DERIVED, archivedMap, undefined, { + conciergeReportID: undefined, + isSearching: true, + }); // When we call getSearchOptions with all betas const {options} = getSearchOptions({ options: OPTIONS_WITH_ARCHIVED, @@ -2434,7 +2435,8 @@ describe('OptionsListUtils', () => { // cspell:disable-next-line const searchText = 'barryallen'; // Given a set of options created from PERSONAL_DETAILS_WITH_PERIODS - const OPTIONS_WITH_PERIODS = createFilteredOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + const OPTIONS_WITH_PERIODS = createFilteredOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, isSearching: true, }); // When we call getSearchOptions with all betas @@ -2521,8 +2523,7 @@ describe('OptionsListUtils', () => { MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CHAT_ROOM, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, - undefined, - {isSearching: true}, + {conciergeReportID: undefined, isSearching: true}, ); // When we call getSearchOptions with all betas const {options} = getSearchOptions({ @@ -2885,7 +2886,8 @@ describe('OptionsListUtils', () => { }, }; - const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, isSearching: true, }); @@ -2935,7 +2937,8 @@ describe('OptionsListUtils', () => { }, }; - const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, isSearching: true, }); @@ -2985,7 +2988,8 @@ describe('OptionsListUtils', () => { }, }; - const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, isSearching: true, }); @@ -3035,7 +3039,8 @@ describe('OptionsListUtils', () => { }, }; - const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + const OPTIONS_WITH_GROUP_CHAT = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_GROUP_CHAT, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, isSearching: true, }); @@ -3086,8 +3091,7 @@ describe('OptionsListUtils', () => { undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, - undefined, - {isSearching: true}, + {conciergeReportID: undefined, isSearching: true}, ); // When we call getSearchOptions with all betas @@ -3452,7 +3456,8 @@ describe('OptionsListUtils', () => { .then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, PERSONAL_DETAILS_WITH_PERIODS)) .then(() => { // Given a set of options with periods - const OPTIONS_WITH_PERIODS = createFilteredOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + const OPTIONS_WITH_PERIODS = createFilteredOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, isSearching: true, }); // When we call getSearchOptions @@ -3520,7 +3525,8 @@ describe('OptionsListUtils', () => { it('should order self dm always on top if the search matches with the self dm login', () => { const searchTerm = 'tonystark@expensify.com'; - const OPTIONS_WITH_SELF_DM = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_SELF_DM, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, { + const OPTIONS_WITH_SELF_DM = createFilteredOptionList(PERSONAL_DETAILS, REPORTS_WITH_SELF_DM, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, { + conciergeReportID: undefined, isSearching: true, }); @@ -3590,7 +3596,10 @@ describe('OptionsListUtils', () => { // Given a set of reports and personal details await Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, PERSONAL_DETAILS); // When we call createFilteredOptionList and extract the reports - const reports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, undefined, {isSearching: true}).reports; + const reports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, { + conciergeReportID: undefined, + isSearching: true, + }).reports; // Then the returned reports should match the expected values expect(reports.at(10)?.subtitle).toBe(`Submits to Mister Fantastic`); @@ -3600,7 +3609,10 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); // When we call createFilteredOptionList again - const newReports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, undefined, {isSearching: true}).reports; + const newReports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, { + conciergeReportID: undefined, + isSearching: true, + }).reports; // Then the returned reports should change to Spanish // cspell:disable-next-line expect(newReports.at(10)?.subtitle).toBe('Se envía a Mister Fantastic'); @@ -3683,7 +3695,7 @@ describe('OptionsListUtils', () => { const archivedMap: PrivateIsArchivedMap = { [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}10`]: !!reportNameValuePairs.private_isArchived, }; - const reports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, archivedMap, undefined, undefined, {isSearching: true}).reports; + const reports = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, archivedMap, undefined, {conciergeReportID: undefined, isSearching: true}).reports; const archivedReport = reports.find((report) => report.reportID === '10'); // Then the returned report should contain default archived reason @@ -3701,7 +3713,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with this privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined, {isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, {conciergeReportID: undefined, isSearching: true}); // Then the personal detail option for account 1 (Mister Fantastic) should have private_isArchived set const misterFantasticOption = result.personalDetails.find((pd) => pd.item?.accountID === 1); @@ -3714,7 +3726,7 @@ describe('OptionsListUtils', () => { const emptyMap: PrivateIsArchivedMap = {}; // When we call createFilteredOptionList with an empty privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, emptyMap, undefined, undefined, {isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, emptyMap, undefined, {conciergeReportID: undefined, isSearching: true}); // Then no personal details options should have private_isArchived set const optionsWithArchived = result.personalDetails.filter((pd) => pd.private_isArchived); @@ -3730,7 +3742,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with this privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined, {isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, {conciergeReportID: undefined, isSearching: true}); // Then the personal detail options should have the correct private_isArchived values const misterFantasticOption = result.personalDetails.find((pd) => pd.item?.accountID === 1); @@ -3749,7 +3761,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with this privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, {conciergeReportID: undefined}); // Then the report options should have the correct private_isArchived values const report3Option = result.reports.find((r) => r.item?.reportID === '3'); @@ -3768,7 +3780,7 @@ describe('OptionsListUtils', () => { const emptyMap: PrivateIsArchivedMap = {}; // When we call createFilteredOptionList with an empty privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, emptyMap, undefined, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, emptyMap, undefined, {conciergeReportID: undefined}); // Then reports NOT in Onyx (like report 3, 5) should not have private_isArchived set // Note: Report 10 gets private_isArchived from Onyx (set in beforeAll) @@ -3789,7 +3801,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with this privateIsArchivedMap - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, {conciergeReportID: undefined}); // Then the report options should have the correct private_isArchived values const report1Option = result.reports.find((r) => r.item?.reportID === '1'); @@ -3816,9 +3828,7 @@ describe('OptionsListUtils', () => { }; // When we call createFilteredOptionList with a maxRecentReports limit that includes all reports - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, undefined, { - maxRecentReports: 20, - }); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, privateIsArchivedMap, undefined, {conciergeReportID: undefined, maxRecentReports: 20}); // Then the report 7 (most recent) should still have private_isArchived set const report7Option = result.reports.find((r) => r.item?.reportID === '7'); @@ -8432,11 +8442,25 @@ describe('OptionsListUtils', () => { }; // When the threaded conciergeReportID matches the report - const conciergeOption = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, undefined, report.reportID); + const conciergeOption = createOptionFromReport({ + report, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: undefined, + policy: undefined, + sortedActions: undefined, + conciergeReportID: report.reportID, + }); expect(conciergeOption.subtitle).toBe(translateLocal('reportActionsView.conciergeSupport')); // And an identical report with a non-matching conciergeReportID is not treated as Concierge - const regularOption = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, undefined, 'a-different-report-id'); + const regularOption = createOptionFromReport({ + report, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: undefined, + policy: undefined, + sortedActions: undefined, + conciergeReportID: 'a-different-report-id', + }); expect(regularOption.subtitle).not.toBe(translateLocal('reportActionsView.conciergeSupport')); }); @@ -8457,7 +8481,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions, undefined); + const result = createOptionFromReport({report, personalDetails: PERSONAL_DETAILS, privateIsArchived: undefined, policy: undefined, sortedActions, conciergeReportID: undefined}); expect(result).toBeDefined(); expect(result.reportID).toBe('1'); @@ -8481,7 +8505,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, true, undefined, sortedActions, undefined); + const result = createOptionFromReport({report, personalDetails: PERSONAL_DETAILS, privateIsArchived: true, policy: undefined, sortedActions, conciergeReportID: undefined}); expect(result).toBeDefined(); expect(result.private_isArchived).toBe(true); @@ -8504,7 +8528,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions, undefined); + const result = createOptionFromReport({report, personalDetails: PERSONAL_DETAILS, privateIsArchived: undefined, policy: undefined, sortedActions, conciergeReportID: undefined}); expect(result).toBeDefined(); expect(result.private_isArchived).toBeUndefined(); @@ -8527,7 +8551,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions, undefined); + const result = createOptionFromReport({report, personalDetails: PERSONAL_DETAILS, privateIsArchived: undefined, policy: undefined, sortedActions, conciergeReportID: undefined}); expect(result).toBeDefined(); expect(result.reportID).toBe('1'); @@ -8551,7 +8575,15 @@ describe('OptionsListUtils', () => { const sortedActions = {[report.reportID]: [reportAction]}; const config = {showPersonalDetails: true}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, undefined, sortedActions, undefined, undefined, config); + const result = createOptionFromReport({ + report, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: undefined, + policy: undefined, + sortedActions, + conciergeReportID: undefined, + config, + }); expect(result).toBeDefined(); expect(result.reportID).toBe('1'); @@ -8560,7 +8592,7 @@ describe('OptionsListUtils', () => { describe('createFilteredOptionList', () => { it('should return report options limited by maxRecentReports', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {maxRecentReports: 5}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {conciergeReportID: undefined, maxRecentReports: 5}); expect(result).toBeDefined(); expect(result.reports.length).toBeLessThanOrEqual(5); @@ -8612,13 +8644,13 @@ describe('OptionsListUtils', () => { }, }; - const result = createFilteredOptionList(PERSONAL_DETAILS, reportsWithDates, undefined, {}, undefined, undefined, {maxRecentReports: 3}); + const result = createFilteredOptionList(PERSONAL_DETAILS, reportsWithDates, undefined, {}, undefined, {conciergeReportID: undefined, maxRecentReports: 3}); expect(result.reports.length).toBeGreaterThan(0); }); it('should include personal details when includeP2P is true', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {includeP2P: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {conciergeReportID: undefined, includeP2P: true}); expect(result).toBeDefined(); expect(result.personalDetails).toBeDefined(); @@ -8626,21 +8658,21 @@ describe('OptionsListUtils', () => { }); it('should exclude personal details when includeP2P is false', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {includeP2P: false}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {conciergeReportID: undefined, includeP2P: false}); expect(result).toBeDefined(); expect(result.personalDetails.length).toBe(0); }); it('should handle empty reports collection', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, {}, undefined, {}, undefined, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, {}, undefined, {}, undefined, {conciergeReportID: undefined}); expect(result).toBeDefined(); expect(result.reports.length).toBe(0); }); it('should handle undefined reports collection', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, undefined, undefined, {}, undefined, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, undefined, undefined, {}, undefined, {conciergeReportID: undefined}); expect(result).toBeDefined(); expect(result.reports.length).toBe(0); @@ -8670,30 +8702,27 @@ describe('OptionsListUtils', () => { [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}999`]: true, }; - const result = createFilteredOptionList(PERSONAL_DETAILS, reportsCollection, undefined, privateIsArchivedMap, undefined, undefined, {maxRecentReports: 10}); + const result = createFilteredOptionList(PERSONAL_DETAILS, reportsCollection, undefined, privateIsArchivedMap, undefined, {conciergeReportID: undefined, maxRecentReports: 10}); expect(result).toBeDefined(); }); it('should handle isSearching filtering', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {conciergeReportID: undefined, isSearching: true}); expect(result).toBeDefined(); expect(result.reports.length).toBe(Object.keys(REPORTS).length); }); it('should return all reports when isSearching is true', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, {}, undefined, { - isSearching: true, - maxRecentReports: 2, - }); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, {}, {conciergeReportID: undefined, isSearching: true, maxRecentReports: 2}); expect(result).toBeDefined(); expect(result.reports.length).toBe(Object.keys(REPORTS).length); }); it('should return both reports and personal details', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {conciergeReportID: undefined}); expect(result).toBeDefined(); expect(result).toHaveProperty('reports'); @@ -8703,27 +8732,27 @@ describe('OptionsListUtils', () => { // The SearchRouter relies on this: its empty-query state renders recent reports only, // so contacts must not be built until the user starts searching. it('should not build personal details when deferContactsUntilSearch is true and not searching', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {deferContactsUntilSearch: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {conciergeReportID: undefined, deferContactsUntilSearch: true}); expect(result.reports.length).toBeGreaterThan(0); expect(result.personalDetails.length).toBe(0); }); it('should build personal details when deferContactsUntilSearch is true and searching', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, undefined, {deferContactsUntilSearch: true, isSearching: true}); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, {}, undefined, {conciergeReportID: undefined, deferContactsUntilSearch: true, isSearching: true}); expect(result.personalDetails.length).toBeGreaterThan(0); }); it('should keep top-level fields of returned options mutable while the cached entry stays pristine', () => { - const first = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined); + const first = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {conciergeReportID: undefined}); const firstOption = first.personalDetails.at(0); expect(firstOption).toBeDefined(); if (firstOption) { firstOption.isSelected = true; } - const second = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined); + const second = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {conciergeReportID: undefined}); const secondOption = second.personalDetails.at(0); // Same cache entry (nested objects are shared between clones), but each caller gets fresh top-level objects. @@ -8735,7 +8764,7 @@ describe('OptionsListUtils', () => { // The cached entry is frozen in dev, so a consumer that mutates a nested object shared with the // cache throws instead of silently corrupting the results returned to every other screen. it('should throw when a nested object shared with the cache is mutated', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {conciergeReportID: undefined}); const icons = result.personalDetails.at(0)?.icons; expect(icons?.length).toBeGreaterThan(0); @@ -8745,7 +8774,7 @@ describe('OptionsListUtils', () => { // Onyx snapshot objects referenced by the options are shared with the whole app and existing code // still writes to them in place, so the dev freeze must leave them untouched (see deepFreeze). it('should not freeze the Onyx snapshot objects referenced by cached options', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {conciergeReportID: undefined}); const personalDetailItem = result.personalDetails.at(0)?.item; const reportItem = result.reports.at(0)?.item; @@ -8792,7 +8821,7 @@ describe('OptionsListUtils', () => { describe('policy parameter passing', () => { it('createFilteredOptionList should accept policiesCollection parameter', () => { - const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, undefined); + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, allPolicies, {conciergeReportID: undefined}); expect(result).toBeDefined(); expect(result.reports).toBeDefined(); expect(result.personalDetails).toBeDefined(); @@ -8816,7 +8845,7 @@ describe('OptionsListUtils', () => { const reportAction = createRandomReportAction(1); const sortedActions = {[report.reportID]: [reportAction]}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, undefined, POLICY, sortedActions, undefined); + const result = createOptionFromReport({report, personalDetails: PERSONAL_DETAILS, privateIsArchived: undefined, policy: POLICY, sortedActions, conciergeReportID: undefined}); expect(result).toBeDefined(); expect(result.policyID).toBe(policyID); }); diff --git a/tests/unit/SearchAutocompleteListTest.tsx b/tests/unit/SearchAutocompleteListTest.tsx index 3fcc305def9b..db2a167b3051 100644 --- a/tests/unit/SearchAutocompleteListTest.tsx +++ b/tests/unit/SearchAutocompleteListTest.tsx @@ -144,7 +144,7 @@ const mockedReports = getMockedReports(10); const mockedBetas = Object.values(CONST.BETAS); const mockedPersonalDetails = getMockedPersonalDetails(10); const EMPTY_PRIVATE_IS_ARCHIVED_MAP: PrivateIsArchivedMap = {}; -const mockedOptions = createFilteredOptionList(mockedPersonalDetails, mockedReports, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, undefined, {isSearching: true}); +const mockedOptions = createFilteredOptionList(mockedPersonalDetails, mockedReports, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {conciergeReportID: undefined, isSearching: true}); const mockOnClose = jest.fn((afterClose?: () => void) => { afterClose?.(); @@ -297,7 +297,7 @@ describe('SearchAutocompleteList', () => { // Then the contextual report (not part of recent reports) is built through createOptionFromReport // with the conciergeReportID threaded from Onyx (#66411) expect(createOptionFromReportSpy).toHaveBeenCalled(); - expect(createOptionFromReportSpy.mock.calls.at(0)?.at(5)).toBe('concierge-router-1'); + expect(createOptionFromReportSpy.mock.calls.at(0)?.at(0)?.conciergeReportID).toBe('concierge-router-1'); }); describe('two-section chat switcher', () => { From 9c90270a1e6ec75cc3cedf433a8e754c7903b678 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Mon, 27 Jul 2026 21:26:11 +0700 Subject: [PATCH 4/9] use structured parameters for improved clarity --- tests/ui/InSelectorTest.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/InSelectorTest.tsx b/tests/ui/InSelectorTest.tsx index cad034175c5d..8f48040f8353 100644 --- a/tests/ui/InSelectorTest.tsx +++ b/tests/ui/InSelectorTest.tsx @@ -36,7 +36,7 @@ jest.mock('@libs/actions/Report', () => ({searchInServer: jest.fn()})); jest.mock('@libs/DeviceCapabilities', () => ({canUseTouchScreen: () => false})); jest.mock('@libs/OptionsListUtils', () => ({ // Build a minimal option object from the report id the component passes in. - createOptionFromReport: (report: {reportID: string}) => ({reportID: report.reportID, keyForList: report.reportID, text: `Report ${report.reportID}`}), + createOptionFromReport: ({report}: {report: {reportID: string}}) => ({reportID: report.reportID, keyForList: report.reportID, text: `Report ${report.reportID}`}), getSearchOptions: () => ({options: {recentReports: mockDefaultReports, personalDetails: []}}), filterAndOrderOptions: () => ({recentReports: mockRecentReports, personalDetails: []}), getAlternateText: () => '', From 14f2e000f6f8424d5cab59bb48f9b3a2a03a94ce Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Mon, 27 Jul 2026 21:43:32 +0700 Subject: [PATCH 5/9] Ensure consistent handling of conciergeReportID across relevant components --- src/libs/ReportUtils.ts | 5 +---- src/libs/actions/Policy/Policy.ts | 1 - src/libs/actions/Report/index.ts | 5 ++--- tests/actions/IOUTest/TrackExpenseTest.ts | 2 -- tests/actions/PolicyTest.ts | 2 -- 5 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c5c575e749c6..8a3b56936225 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1896,10 +1896,7 @@ function getReportNotificationPreference(report: OnyxEntry): ValueOf, conciergeReportID: string | undefined): boolean { - // The deprecated Onyx.connect fallback stays until every conciergeReportID param in the createOptions chain is - // required and threaded (see the remaining #66411 TODOs) — removing it earlier would break Concierge detection - // on call paths that still omit the value. +function isConciergeChatReport(report: OnyxInputOrEntry, conciergeReportID?: string): boolean { return !!report && report?.reportID === (conciergeReportID ?? conciergeReportIDOnyxConnect); } diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 376826723b13..a659bace36c2 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -261,7 +261,6 @@ type BuildPolicyDataOptions = { currentUserAccountIDParam: number; currentUserEmailParam: string; allReportsParam?: OnyxCollection; - /** The Concierge chat report, threaded so prepareOnboardingOnyxData no longer relies on the deprecated CONCIERGE_REPORT_ID Onyx.connect (#66411). */ // TODO: Make conciergeChat required once all callers pass it. Refactor issue: https://github.com/Expensify/App/issues/66411 conciergeChat?: OnyxEntry; onboardingPurposeSelected?: OnboardingPurpose; diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index a61693d16f09..922bc16e7e61 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -2714,7 +2714,7 @@ function explain( currentUserAccountID, shouldPlaySound: true, delegateAccountID, - // Safe: the explanation posts to a thread, never the Concierge chat. Thread it if #66411 removes the fallback. + // Passing conciergeReportID as undefined is intentional, the explanation posts to a thread, never the Concierge chat. conciergeReportID: undefined, }); }); @@ -5197,7 +5197,7 @@ function inviteToRoomAction( currentUserAccountID, shouldPlaySound: false, delegateAccountID, - // Safe: this posts to the room, never the Concierge chat. Thread it if #66411 removes the fallback. + // Passing conciergeReportID as undefined is intentional, this posts to the room, never the Concierge chat. conciergeReportID: undefined, }); } @@ -8048,7 +8048,6 @@ function resolveConciergeOptions( } const reportID = report.reportID; - // Must thread before #66411 removes the fallback: this report CAN be the Concierge chat, so undefined would drop Concierge params. addComment({report, notifyReportID: notifyReportID ?? reportID, ancestors, text: selectedValue, timezoneParam, currentUserAccountID, delegateAccountID, conciergeReportID}); Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { diff --git a/tests/actions/IOUTest/TrackExpenseTest.ts b/tests/actions/IOUTest/TrackExpenseTest.ts index ef1d84c4492d..c41ced64a5e8 100644 --- a/tests/actions/IOUTest/TrackExpenseTest.ts +++ b/tests/actions/IOUTest/TrackExpenseTest.ts @@ -1255,8 +1255,6 @@ describe('actions/IOU/TrackExpense', () => { it('should post onboarding tasks to the threaded conciergeChat instead of the deprecated CONCIERGE_REPORT_ID fallback when tracking to a draft workspace', async () => { mockFetch?.pause?.(); - // Given the deprecated Onyx.connect fallback points at a DIFFERENT report than the threaded param, - // so the assertions below fail if trackExpense stops forwarding conciergeChat to buildPolicyData (#66411) await Onyx.set(ONYXKEYS.CONCIERGE_REPORT_ID, 'deprecated-fallback-track'); // And a draft workspace expense chat (getTrackExpenseInformation only creates the workspace for draft reports) diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index 6e6f1589e623..4a93e1c8efcc 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -1521,8 +1521,6 @@ describe('actions/Policy', () => { it('should post onboarding tasks to the threaded conciergeChat instead of the deprecated CONCIERGE_REPORT_ID fallback', async () => { mockFetch?.pause?.(); await Onyx.set(ONYXKEYS.SESSION, {email: ESH_EMAIL, accountID: ESH_ACCOUNT_ID}); - // Given the deprecated Onyx.connect fallback points at a DIFFERENT report than the threaded param, - // so the assertions below fail if buildPolicyData stops forwarding conciergeChat (#66411). await Onyx.set(ONYXKEYS.CONCIERGE_REPORT_ID, 'deprecated-fallback-report'); await waitForBatchedUpdates(); From 702ec963c1bf0342444b2a3cddd6d539ab9fee39 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Thu, 30 Jul 2026 01:07:28 +0700 Subject: [PATCH 6/9] Enhance createFilteredOptionList to include conciergeReportID and update tests to validate cache invalidation on changes to conciergeReportID. --- src/libs/OptionsListUtils/index.ts | 1 + tests/unit/OptionsListUtilsTest.tsx | 10 ++++++++++ tests/unit/SearchAutocompleteListTest.tsx | 1 - 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index e71e183d716a..e9339008be91 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -1707,6 +1707,7 @@ function createFilteredOptionList( policyTags, visibleReportActionsData, isTrackIntentUser, + conciergeReportID, // Option building translates strings imperatively (translateLocal), so the active locale is part of the output. locale ?? IntlStore.getCurrentLocale(), // The RAM_ONLY_SORTED_REPORT_ACTIONS derived value produces a new object on every recompute, diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 5e22336b1a8b..a63a2548d55d 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -9036,6 +9036,16 @@ describe('OptionsListUtils', () => { expect(secondOption?.isSelected).toBeFalsy(); }); + // conciergeReportID affects the Concierge option's subtitle/alternate text, so a change must + // invalidate the cache instead of serving options built with the previous value. + it('should recompute cached options when only conciergeReportID changes', () => { + const first = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {conciergeReportID: undefined}); + const second = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, undefined, EMPTY_PRIVATE_IS_ARCHIVED_MAP, undefined, {conciergeReportID: '1'}); + + // A cache hit would share nested objects between clones (see the pristine-cache test above). + expect(second.personalDetails.at(0)?.icons).not.toBe(first.personalDetails.at(0)?.icons); + }); + // The cached entry is frozen in dev, so a consumer that mutates a nested object shared with the // cache throws instead of silently corrupting the results returned to every other screen. it('should throw when a nested object shared with the cache is mutated', () => { diff --git a/tests/unit/SearchAutocompleteListTest.tsx b/tests/unit/SearchAutocompleteListTest.tsx index d0e9ce61daf8..6ff6e739e3e8 100644 --- a/tests/unit/SearchAutocompleteListTest.tsx +++ b/tests/unit/SearchAutocompleteListTest.tsx @@ -310,7 +310,6 @@ describe('SearchAutocompleteList', () => { await flushAllUpdates(); // Then the contextual report (not part of recent reports) is built through createOptionFromReport - // with the conciergeReportID threaded from Onyx (#66411) expect(createOptionFromReportSpy).toHaveBeenCalled(); expect(createOptionFromReportSpy.mock.calls.at(0)?.at(0)?.conciergeReportID).toBe('concierge-router-1'); }); From 6087b00734c882743828b2f274e5ecb23bd8b8ea Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Thu, 30 Jul 2026 01:22:07 +0700 Subject: [PATCH 7/9] modify tests to include conciergeReportID i --- src/libs/OptionsListUtils/index.ts | 4 ++-- tests/unit/OptionsListUtilsTest.tsx | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index e9339008be91..76bd062072a1 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -1396,7 +1396,7 @@ function getPolicyExpenseReportOption( report: !isEmptyObject(expenseReport) ? expenseReport : null, policy, privateIsArchived, - // Safe: a policy expense chat is never the Concierge chat. + // Passing conciergeReportID as undefined is intentional, a policy expense chat is never the Concierge chat. conciergeReportID: undefined, config: { showChatPreviewLine: false, @@ -2243,7 +2243,7 @@ function getUserToInviteOption({ personalDetails: personalDetailsExtended, report: null, privateIsArchived: undefined, - // Safe: the invite option is built without a report, so it can never be the Concierge chat. + // Passing conciergeReportID as undefined is intentional, the invite option is built without a report, so it can never be the Concierge chat. conciergeReportID: undefined, config: {showChatPreviewLine}, visibleReportActionsData, diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index a63a2548d55d..11abad1b210f 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -8823,7 +8823,7 @@ describe('OptionsListUtils', () => { it('returns the most recent reports when maxRecentReports is less than the input size', () => { const reports = [createChatReport('101', '2022-01-01 00:00:00'), createChatReport('102', '2024-01-01 00:00:00'), createChatReport('103', '2023-01-01 00:00:00')]; - const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, {}, undefined, {maxRecentReports: 2, includeP2P: false}); + const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, {}, undefined, {conciergeReportID: undefined, maxRecentReports: 2, includeP2P: false}); expect(result.reports.map((option) => option.item?.reportID)).toEqual(['102', '103']); }); @@ -8831,7 +8831,7 @@ describe('OptionsListUtils', () => { it('prioritizes self-DM over newer non-self-DM reports', () => { const reports = [createChatReport('101', '2024-01-01 00:00:00'), createChatReport('102', '2020-01-01 00:00:00', CONST.REPORT.CHAT_TYPE.SELF_DM)]; - const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, {}, undefined, {maxRecentReports: 1, includeP2P: false}); + const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, {}, undefined, {conciergeReportID: undefined, maxRecentReports: 1, includeP2P: false}); expect(result.reports.map((option) => option.item?.reportID)).toEqual(['102']); }); @@ -8842,7 +8842,11 @@ describe('OptionsListUtils', () => { [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}102`]: true, }; - const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, privateIsArchivedMap, undefined, {maxRecentReports: 1, includeP2P: false}); + const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, privateIsArchivedMap, undefined, { + conciergeReportID: undefined, + maxRecentReports: 1, + includeP2P: false, + }); expect(result.reports.map((option) => option.item?.reportID)).toEqual(['101']); }); @@ -8850,7 +8854,12 @@ describe('OptionsListUtils', () => { it('returns all reports when isSearching is true', () => { const reports = [createChatReport('101', '2022-01-01 00:00:00'), createChatReport('102', '2024-01-01 00:00:00'), createChatReport('103', '2023-01-01 00:00:00')]; - const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, {}, undefined, {maxRecentReports: 1, isSearching: true, includeP2P: false}); + const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, {}, undefined, { + conciergeReportID: undefined, + maxRecentReports: 1, + isSearching: true, + includeP2P: false, + }); expect(result.reports.map((option) => option.item?.reportID)).toEqual(['101', '102', '103']); }); @@ -8858,7 +8867,7 @@ describe('OptionsListUtils', () => { it('returns an empty array when maxRecentReports is zero', () => { const reports = [createChatReport('101', '2024-01-01 00:00:00')]; - const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, {}, undefined, {maxRecentReports: 0, includeP2P: false}); + const result = createFilteredOptionList({}, reportsToCollection(reports), undefined, {}, undefined, {conciergeReportID: undefined, maxRecentReports: 0, includeP2P: false}); expect(result.reports).toEqual([]); }); From 895de089658f6258c62f645c6d36ae31ebda4796 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Thu, 30 Jul 2026 12:02:41 +0700 Subject: [PATCH 8/9] getReportDisplayOption to use a parameter object for improved readability and maintainability --- src/libs/OptionsListUtils/index.ts | 40 ++++-- src/pages/Share/ShareDetailsPage.tsx | 4 +- tests/unit/OptionsListUtilsTest.tsx | 182 ++++++++++++++++++++------- 3 files changed, 165 insertions(+), 61 deletions(-) diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 2c564707ca96..76e839a164d5 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -1319,22 +1319,36 @@ function getReportOption( return option; } +type GetReportDisplayOptionParams = { + report: OnyxEntry; + unknownUserDetails: OnyxEntry; + personalDetails: OnyxEntry; + privateIsArchived: boolean | undefined; + policy: OnyxEntry; + conciergeReportID: string | undefined; + translate: LocalizedTranslate; + currentUserAccountID: number; + reportAttributesDerived?: ReportAttributesDerivedValue['reports']; + policyTags?: OnyxEntry; + visibleReportActionsData?: VisibleReportActionsDerivedValue; +}; + /** * Get the display option for a given report. */ -function getReportDisplayOption( - report: OnyxEntry, - unknownUserDetails: OnyxEntry, - personalDetails: OnyxEntry, - privateIsArchived: boolean | undefined, - policy: OnyxEntry, - conciergeReportID: string | undefined, - translate: LocalizedTranslate, - currentUserAccountID: number, - reportAttributesDerived?: ReportAttributesDerivedValue['reports'], - policyTags?: OnyxEntry, - visibleReportActionsData: VisibleReportActionsDerivedValue = {}, -): OptionData { +function getReportDisplayOption({ + report, + unknownUserDetails, + personalDetails, + privateIsArchived, + policy, + conciergeReportID, + translate, + currentUserAccountID, + reportAttributesDerived, + policyTags, + visibleReportActionsData = {}, +}: GetReportDisplayOptionParams): OptionData { const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); const option = createOption({ diff --git a/src/pages/Share/ShareDetailsPage.tsx b/src/pages/Share/ShareDetailsPage.tsx index 5592f2348ed2..f7a1b4494610 100644 --- a/src/pages/Share/ShareDetailsPage.tsx +++ b/src/pages/Share/ShareDetailsPage.tsx @@ -83,8 +83,8 @@ function ShareDetailsPage({route}: ShareDetailsPageProps) { const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`); const currentUserAccountID = personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID; const displayReport = useMemo( - () => getReportDisplayOption(report, unknownUserDetails, personalDetails, privateIsArchived, policy, conciergeReportID, translate, currentUserAccountID, reportAttributesDerived), - [report, unknownUserDetails, personalDetails, privateIsArchived, policy, currentUserAccountID, reportAttributesDerived, translate], + () => getReportDisplayOption({report, unknownUserDetails, personalDetails, privateIsArchived, policy, conciergeReportID, translate, currentUserAccountID, reportAttributesDerived}), + [report, unknownUserDetails, personalDetails, privateIsArchived, policy, conciergeReportID, translate, currentUserAccountID, reportAttributesDerived], ); const shouldShowAttachment = !isTextShared; diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 95858a5d90d8..479caa455e40 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -6627,7 +6627,16 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, personalDetails, undefined, undefined, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const result = getReportDisplayOption({ + report, + unknownUserDetails: undefined, + personalDetails, + privateIsArchived: undefined, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); // Then it should return an option with isSelfDM and alternateText set expect(result.isSelfDM).toBe(true); @@ -6647,7 +6656,16 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, personalDetails, undefined, undefined, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const result = getReportDisplayOption({ + report, + unknownUserDetails: undefined, + personalDetails, + privateIsArchived: undefined, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); // Then it should return an option with invoice room text and alternateText expect(result.isInvoiceRoom).toBe(true); @@ -6669,7 +6687,16 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, unknownUserDetails, personalDetails, undefined, undefined, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const result = getReportDisplayOption({ + report, + unknownUserDetails, + personalDetails, + privateIsArchived: undefined, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); // Then it should return an option with unknownUserDetails data expect(result.text).toBe('Unknown User'); @@ -6690,7 +6717,16 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, personalDetails, undefined, undefined, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const result = getReportDisplayOption({ + report, + unknownUserDetails: undefined, + personalDetails, + privateIsArchived: undefined, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); // Then it should return an option with workspace name expect(result.text).toBe(POLICY.name); @@ -6719,7 +6755,16 @@ describe('OptionsListUtils', () => { }; // When we call getReportDisplayOption with custom personalDetails - const result = getReportDisplayOption(report, undefined, customPersonalDetails, undefined, undefined, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const result = getReportDisplayOption({ + report, + unknownUserDetails: undefined, + personalDetails: customPersonalDetails, + privateIsArchived: undefined, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); // Then it should use the custom personalDetails parameter expect(result).toBeDefined(); @@ -6736,7 +6781,16 @@ describe('OptionsListUtils', () => { const emptyPersonalDetails: PersonalDetailsList = {}; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, emptyPersonalDetails, undefined, undefined, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const result = getReportDisplayOption({ + report, + unknownUserDetails: undefined, + personalDetails: emptyPersonalDetails, + privateIsArchived: undefined, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); // Then it should not throw and return a valid option expect(result).toBeDefined(); @@ -6748,7 +6802,16 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption with undefined report - const result = getReportDisplayOption(undefined, undefined, personalDetails, undefined, undefined, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const result = getReportDisplayOption({ + report: undefined, + unknownUserDetails: undefined, + personalDetails, + privateIsArchived: undefined, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); // Then it should return a valid option (createOption handles undefined) expect(result).toBeDefined(); @@ -7188,16 +7251,16 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption( + const option = getReportDisplayOption({ report, - undefined, - PERSONAL_DETAILS, - !!reportNameValuePair?.private_isArchived, - undefined, - undefined, - translateLocal, - CURRENT_USER_ACCOUNT_ID, - ); + unknownUserDetails: undefined, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: !!reportNameValuePair?.private_isArchived, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -7220,16 +7283,16 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption( + const option = getReportDisplayOption({ report, - undefined, - PERSONAL_DETAILS, - !!reportNameValuePair?.private_isArchived, - undefined, - undefined, - translateLocal, - CURRENT_USER_ACCOUNT_ID, - ); + unknownUserDetails: undefined, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: !!reportNameValuePair?.private_isArchived, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -7249,7 +7312,16 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, undefined, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const option = getReportDisplayOption({ + report, + unknownUserDetails: undefined, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: undefined, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -7273,16 +7345,16 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption( + const option = getReportDisplayOption({ report, - undefined, - PERSONAL_DETAILS, - !!reportNameValuePair?.private_isArchived, - undefined, - undefined, - translateLocal, - CURRENT_USER_ACCOUNT_ID, - ); + unknownUserDetails: undefined, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: !!reportNameValuePair?.private_isArchived, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -7306,16 +7378,16 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption( + const option = getReportDisplayOption({ report, - undefined, - PERSONAL_DETAILS, - !!reportNameValuePair?.private_isArchived, - undefined, - undefined, - translateLocal, - CURRENT_USER_ACCOUNT_ID, - ); + unknownUserDetails: undefined, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: !!reportNameValuePair?.private_isArchived, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -8662,7 +8734,16 @@ describe('OptionsListUtils', () => { await waitForBatchedUpdates(); // When we call getReportDisplayOption with chat report - const option = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, undefined, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const option = getReportDisplayOption({ + report, + unknownUserDetails: undefined, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: undefined, + policy: undefined, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); // Then the option should be created successfully using the reports collection expect(option).toBeDefined(); @@ -9235,7 +9316,16 @@ describe('OptionsListUtils', () => { }, }; - const result = getReportDisplayOption(report, undefined, PERSONAL_DETAILS, undefined, POLICY, undefined, translateLocal, CURRENT_USER_ACCOUNT_ID); + const result = getReportDisplayOption({ + report, + unknownUserDetails: undefined, + personalDetails: PERSONAL_DETAILS, + privateIsArchived: undefined, + policy: POLICY, + conciergeReportID: undefined, + translate: translateLocal, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + }); expect(result).toBeDefined(); expect(result.policyID).toBe(policyID); }); From 8e427f1065c371788b5bf6f32d062c9ca7ff51da Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Thu, 30 Jul 2026 19:45:05 +0700 Subject: [PATCH 9/9] Update OptionsListUtilsTest to set conciergeReportID --- tests/unit/OptionsListUtilsTest.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 3d784453a53b..95f9f0fba8da 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -5517,8 +5517,8 @@ describe('OptionsListUtils', () => { lastActorDetails: null, policy: undefined, isReportArchived: false, - currentUserLogin: CURRENT_USER_EMAIL, + conciergeReportID: undefined, }); expect(lastMessage).toBe(getAddAgentRuleMessage(translateLocal, action)); }); @@ -5541,7 +5541,7 @@ describe('OptionsListUtils', () => { lastActorDetails: null, policy: undefined, isReportArchived: false, - + conciergeReportID: undefined, currentUserLogin: CURRENT_USER_EMAIL, }); expect(lastMessage).toBe(getUpdateAgentRuleMessage(translateLocal, action)); @@ -5565,7 +5565,7 @@ describe('OptionsListUtils', () => { lastActorDetails: null, policy: undefined, isReportArchived: false, - + conciergeReportID: undefined, currentUserLogin: CURRENT_USER_EMAIL, }); expect(lastMessage).toBe(getDeleteAgentRuleMessage(translateLocal, action));