From e487350f4b0e01ab5fb32f73de9b43a887d2744b Mon Sep 17 00:00:00 2001 From: Rohit Verma Date: Sat, 23 May 2026 15:10:42 +0530 Subject: [PATCH 1/7] fix: add 'hasMore' flag to options sorting utilities Enhances `optionsOrderBy` and `optionsOrderAndGroupBy` to return a `hasMore` boolean, indicating if there were more items than the requested limit. --- src/libs/OptionsListUtils/index.ts | 24 ++++++++++++------- .../PersonalDetailOptionsListUtils/index.ts | 6 ++--- src/pages/Share/ShareTab.tsx | 2 +- tests/unit/OptionsListUtilsTest.tsx | 14 +++++------ 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 222e0633ce27..6ee865d2f065 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -1608,7 +1608,7 @@ function createFilteredOptionList( // Step 2: Sort by lastVisibleActionCreated (most recent first) and limit to top N // In search mode, skip sorting because we return all reports anyway - sorting is unnecessary - const sortedReports = isSearching ? reportsArray : optionsOrderBy(reportsArray, (report) => reportSortComparator(report, privateIsArchivedMap), maxRecentReports); + const sortedReports = isSearching ? reportsArray : optionsOrderBy(reportsArray, (report) => reportSortComparator(report, privateIsArchivedMap), maxRecentReports).options; // Step 3: If search term is present, build report map with ONLY 1:1 DM reports // This allows personal details to have valid 1:1 DM reportIDs for proper avatar display @@ -1752,12 +1752,13 @@ function optionsOrderBy( limit?: number, filter?: (option: T) => boolean | undefined, reversed = false, -): T[] { +): {options: T[]; hasMore: boolean} { const heap = reversed ? new MaxHeap(comparator) : new MinHeap(comparator); + let hasMore = false; // If a limit is 0 or negative, return an empty array if (limit !== undefined && limit <= 0) { - return []; + return {options: [], hasMore}; } for (const option of options) { @@ -1765,6 +1766,7 @@ function optionsOrderBy( continue; } if (limit !== undefined && heap.size() >= limit) { + hasMore = true; const peekedValue = heap.peek(); if (!peekedValue) { throw new Error('Heap is empty, cannot peek value'); @@ -1777,7 +1779,7 @@ function optionsOrderBy( heap.push(option); } } - return [...heap].reverse(); + return {options: [...heap].reverse(), hasMore}; } /** @@ -1794,9 +1796,10 @@ function optionsOrderAndGroupBy( limit?: number, filter?: (option: T) => boolean | undefined, reversed = false, -): T[][] { +): {options: T[][]; hasMore: boolean} { // Create a heap for each separator + one default heap (N+1 total) const heaps: Array | MaxHeap> = []; + let hasMore = false; for (let i = 0; i < separators.length; i++) { heaps.push(reversed ? new MaxHeap(comparator) : new MinHeap(comparator)); } @@ -1804,7 +1807,7 @@ function optionsOrderAndGroupBy( // If limit is 0 or negative, return N+1 empty arrays if (limit !== undefined && limit <= 0) { - return Array(separators.length + 1).map(() => []); + return {options: Array(separators.length + 1).map(() => []), hasMore}; } // Process each option @@ -1832,6 +1835,7 @@ function optionsOrderAndGroupBy( // Add to heap with limit logic (each heap has its own limit) if (limit !== undefined && targetHeap.size() >= limit) { + hasMore = true; const peekedValue = targetHeap.peek(); if (!peekedValue) { throw new Error('Heap is empty, cannot peek value'); @@ -1853,7 +1857,7 @@ function optionsOrderAndGroupBy( } results.push([...defaultHeap].reverse()); - return results; + return {options: results, hasMore}; } /** @@ -2500,7 +2504,8 @@ function getValidOptions( }; let selfDMChats: Array>; - [selfDMChats, workspaceChats, recentReportOptions] = optionsOrderAndGroupBy([isSelfDMChat, isWorkspaceChat], options.reports, recentReportComparator, maxElements, filteringFunction); + const groupedOptions = optionsOrderAndGroupBy([isSelfDMChat, isWorkspaceChat], options.reports, recentReportComparator, maxElements, filteringFunction); + [selfDMChats, workspaceChats, recentReportOptions] = groupedOptions.options; if (selfDMChats.length > 0) { selfDMChat = prepareReportOptionsForDisplay( @@ -2631,7 +2636,8 @@ function getValidOptions( const maxPersonalDetailsElements = maxElements ? Math.max(maxElements - recentReportOptions.length - workspaceChats.length - (!selfDMChat ? 1 : 0), MIN_PERSONAL_DETAILS_SLOTS) : undefined; - personalDetailsOptions = optionsOrderBy(options.personalDetails, personalDetailsComparator, maxPersonalDetailsElements, filteringFunction, true); + const groupedPersonalDetails = optionsOrderBy(options.personalDetails, personalDetailsComparator, maxPersonalDetailsElements, filteringFunction, true); + personalDetailsOptions = groupedPersonalDetails.options; for (let i = 0; i < personalDetailsOptions.length; i++) { const personalDetail = personalDetailsOptions.at(i); diff --git a/src/libs/PersonalDetailOptionsListUtils/index.ts b/src/libs/PersonalDetailOptionsListUtils/index.ts index 2b6c1a2eca72..5138cc36b053 100644 --- a/src/libs/PersonalDetailOptionsListUtils/index.ts +++ b/src/libs/PersonalDetailOptionsListUtils/index.ts @@ -305,7 +305,7 @@ function getValidOptions( return matchesSearchTerms(personalDetail, searchTerms); }; - const selectedOptions = optionsOrderBy(extendedOptions, personalDetailsComparator, maxElements, selectedFilteringFunction, true); + const selectedOptions = optionsOrderBy(extendedOptions, personalDetailsComparator, maxElements, selectedFilteringFunction, true).options; // If we're including selected options from the search results, we only want to exclude them if the search input is empty // This is because on certain pages, we show the selected options at the top when the search input is empty // This prevents the issue of seeing the selected option twice if you have them as a recent chat and select them @@ -369,7 +369,7 @@ function getValidOptions( return true; }; - recentOptions = optionsOrderBy(options, recentReportComparator, recentMaxElements, filteringFunction); + recentOptions = optionsOrderBy(options, recentReportComparator, recentMaxElements, filteringFunction).options; } // Get valid personal details and check if we can find the current user: @@ -397,7 +397,7 @@ function getValidOptions( return matchesSearchTerms(personalDetail, searchTerms); }; - personalDetailsOptions = optionsOrderBy(options, personalDetailsComparator, maxElements, filteringFunction, true); + personalDetailsOptions = optionsOrderBy(options, personalDetailsComparator, maxElements, filteringFunction, true).options; let userToInvite: OptionData | null = null; if (includeUserToInvite) { diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index 3a17ea12eb5e..1d1e9920f7da 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -101,7 +101,7 @@ function ShareTab({ref}: ShareTabProps) { let recentReportsOptions: OptionData[]; if (textInputValue.trim() === '') { - recentReportsOptions = optionsOrderBy(searchOptions.recentReports, recentReportComparator, 20); + recentReportsOptions = optionsOrderBy(searchOptions.recentReports, recentReportComparator, 20).options; } else { const orderedOptions = combineOrderingOfReportsAndPersonalDetails(searchOptions, textInputValue, { sortByReportTypeInSearch: true, diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 67747b1e23c9..858fa203a03a 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -3752,7 +3752,7 @@ describe('OptionsListUtils', () => { {reportID: '4', lastVisibleActionCreated: '2022-01-01T13:00:00Z'} as OptionData, ]; const comparator = (option: OptionData) => option.lastVisibleActionCreated ?? ''; - const result = optionsOrderBy(options, comparator, 2); + const result = optionsOrderBy(options, comparator, 2).options; expect(result.length).toBe(2); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion expect(result.at(0)!.reportID).toBe('4'); @@ -3766,7 +3766,7 @@ describe('OptionsListUtils', () => { {reportID: '2', lastVisibleActionCreated: '2022-01-01T12:00:00Z'} as OptionData, ]; const comparator = (option: OptionData) => option.lastVisibleActionCreated ?? ''; - const result = optionsOrderBy(options, comparator, 5); + const result = optionsOrderBy(options, comparator, 5).options; expect(result.length).toBe(2); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion expect(result.at(0)!.reportID).toBe('2'); @@ -3786,7 +3786,7 @@ describe('OptionsListUtils', () => { {reportID: '3', lastVisibleActionCreated: '2022-01-01T09:00:00Z', isPinned: true} as OptionData, ]; const comparator = (option: OptionData) => option.lastVisibleActionCreated ?? ''; - const result = optionsOrderBy(options, comparator, 2, (option) => option.isPinned); + const result = optionsOrderBy(options, comparator, 2, (option) => option.isPinned).options; expect(result.length).toBe(2); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion expect(result.at(0)!.reportID).toBe('1'); @@ -3801,7 +3801,7 @@ describe('OptionsListUtils', () => { {reportID: '3', lastVisibleActionCreated: '2022-01-01T09:00:00Z'} as OptionData, ]; const comparator = (option: OptionData) => option.lastVisibleActionCreated ?? ''; - const result = optionsOrderBy(options, comparator, -1); + const result = optionsOrderBy(options, comparator, -1).options; expect(result).toEqual([]); }); @@ -3811,7 +3811,7 @@ describe('OptionsListUtils', () => { {reportID: '2', lastVisibleActionCreated: '2022-01-01T12:00:00Z'} as OptionData, ]; const comparator = (option: OptionData) => option.lastVisibleActionCreated ?? ''; - const result = optionsOrderBy(options, comparator, -100); + const result = optionsOrderBy(options, comparator, -100).options; expect(result).toEqual([]); }); @@ -3821,7 +3821,7 @@ describe('OptionsListUtils', () => { {reportID: '2', lastVisibleActionCreated: '2022-01-01T12:00:00Z'} as OptionData, ]; const comparator = (option: OptionData) => option.lastVisibleActionCreated ?? ''; - const result = optionsOrderBy(options, comparator, 0); + const result = optionsOrderBy(options, comparator, 0).options; expect(result).toEqual([]); }); @@ -3834,7 +3834,7 @@ describe('OptionsListUtils', () => { ]; const comparator = (option: OptionData) => option.lastVisibleActionCreated ?? ''; // We will pass reversed === true to sort the list in ascending order - const result = optionsOrderBy(options, comparator, 2, undefined, true); + const result = optionsOrderBy(options, comparator, 2, undefined, true).options; expect(result.length).toBe(2); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion expect(result.at(0)!.reportID).toBe('3'); From 8d334c0329b21456db26346ad05f8753dce36e1c Mon Sep 17 00:00:00 2001 From: Rohit Verma Date: Sat, 23 May 2026 15:21:08 +0530 Subject: [PATCH 2/7] fix: prevent increasing maxResults when no more results to load --- src/hooks/useSearchSelector/base.ts | 12 ++++++++---- src/libs/OptionsListUtils/index.ts | 6 ++++++ src/libs/OptionsListUtils/types.ts | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/hooks/useSearchSelector/base.ts b/src/hooks/useSearchSelector/base.ts index 607bfd2517e3..ffa43e2bbbd0 100644 --- a/src/hooks/useSearchSelector/base.ts +++ b/src/hooks/useSearchSelector/base.ts @@ -214,10 +214,6 @@ function useSearchSelectorBase({ const [allPolicyTags] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS, {selector: passthroughPolicyTagListSelector}); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const onListEndReached = useDebounce(() => { - setMaxResults((previous) => previous + maxResultsPerPage); - }, CONST.TIMING.SEARCH_OPTION_LIST_DEBOUNCE_TIME); - const computedSearchTerm = getSearchValueForPhoneOrEmail(debouncedSearchTerm, countryCode); const trimmedSearchInput = debouncedSearchTerm.trim(); @@ -343,6 +339,14 @@ function useSearchSelectorBase({ } })(); + const onListEndReached = useDebounce(() => { + if (!areOptionsInitialized || !baseOptions.hasMore) { + return; + } + + setMaxResults((previous) => previous + maxResultsPerPage); + }, CONST.TIMING.SEARCH_OPTION_LIST_DEBOUNCE_TIME); + const isOptionSelected = (option: OptionData) => selectedOptions.some((selected) => doOptionsMatch(selected, option)); const searchOptions = { diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 6ee865d2f065..8875ec151354 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -2435,6 +2435,7 @@ function getValidOptions( let recentReportOptions: Array> = []; let workspaceChats: Array> = []; let selfDMChat: SearchOptionData | undefined; + let hasMore = false; const searchTerms = processSearchString(searchString); if (includeRecentReports) { @@ -2507,6 +2508,8 @@ function getValidOptions( const groupedOptions = optionsOrderAndGroupBy([isSelfDMChat, isWorkspaceChat], options.reports, recentReportComparator, maxElements, filteringFunction); [selfDMChats, workspaceChats, recentReportOptions] = groupedOptions.options; + hasMore = hasMore || groupedOptions.hasMore; + if (selfDMChats.length > 0) { selfDMChat = prepareReportOptionsForDisplay( selfDMChats, @@ -2639,6 +2642,8 @@ function getValidOptions( const groupedPersonalDetails = optionsOrderBy(options.personalDetails, personalDetailsComparator, maxPersonalDetailsElements, filteringFunction, true); personalDetailsOptions = groupedPersonalDetails.options; + hasMore = hasMore || groupedPersonalDetails.hasMore; + for (let i = 0; i < personalDetailsOptions.length; i++) { const personalDetail = personalDetailsOptions.at(i); if (!personalDetail) { @@ -2678,6 +2683,7 @@ function getValidOptions( userToInvite, workspaceChats, selfDMChat, + hasMore, }; } diff --git a/src/libs/OptionsListUtils/types.ts b/src/libs/OptionsListUtils/types.ts index 0c251c9d2e55..747cffd32b53 100644 --- a/src/libs/OptionsListUtils/types.ts +++ b/src/libs/OptionsListUtils/types.ts @@ -260,6 +260,7 @@ type Options = { currentUserOption: SearchOptionData | null | undefined; workspaceChats?: SearchOptionData[]; selfDMChat?: SearchOptionData | undefined; + hasMore?: boolean; }; type PreviewConfig = { From f2c787e526f16d8c938a9f72dba87725cb0269ac Mon Sep 17 00:00:00 2001 From: Rohit Verma Date: Sat, 23 May 2026 15:56:50 +0530 Subject: [PATCH 3/7] test: fix failing test --- tests/unit/OptionsListUtilsTest.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 858fa203a03a..b716db57b3fe 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -3775,7 +3775,7 @@ describe('OptionsListUtils', () => { }); it('returns empty array if options is empty', () => { - const result = optionsOrderBy([], recentReportComparator, 3); + const result = optionsOrderBy([], recentReportComparator, 3).options; expect(result).toEqual([]); }); From 73fb47c23df453ad92adeabcdfd37d48e1efb339 Mon Sep 17 00:00:00 2001 From: Rohit Verma Date: Wed, 27 May 2026 20:23:12 +0530 Subject: [PATCH 4/7] refactor: move hasMore outside of the Options type and update references --- .../Search/FilterComponents/InSelector.tsx | 2 +- .../Search/SearchAutocompleteList.tsx | 2 +- .../Search/SearchFiltersChatsSelector.tsx | 2 +- src/hooks/useAutocompleteSuggestions.ts | 4 +-- src/hooks/useSearchSelector/base.ts | 4 +-- src/libs/OptionsListUtils/index.ts | 32 +++++++++++-------- src/libs/OptionsListUtils/types.ts | 7 +++- src/pages/NewChatPage/index.tsx | 2 +- src/pages/Share/ShareTab.tsx | 2 +- 9 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/components/Search/FilterComponents/InSelector.tsx b/src/components/Search/FilterComponents/InSelector.tsx index f75003429607..83828fd8c075 100644 --- a/src/components/Search/FilterComponents/InSelector.tsx +++ b/src/components/Search/FilterComponents/InSelector.tsx @@ -90,7 +90,7 @@ function InSelector({value = [], onChange}: InSelectorProps) { policyCollection: allPolicies, sortedActions, conciergeReportID, - }); + }).options; const chatOptions = filterAndOrderOptions(defaultOptions, cleanSearchTerm, countryCode, loginList, currentUserEmail, currentUserAccountID, personalDetails, { selectedOptions, diff --git a/src/components/Search/SearchAutocompleteList.tsx b/src/components/Search/SearchAutocompleteList.tsx index 77d2df9ed10a..83424da95300 100644 --- a/src/components/Search/SearchAutocompleteList.tsx +++ b/src/components/Search/SearchAutocompleteList.tsx @@ -221,7 +221,7 @@ function SearchAutocompleteList({ personalDetails, sortedActions, conciergeReportID, - }); + }).options; }, [ listOptions, draftComments, diff --git a/src/components/Search/SearchFiltersChatsSelector.tsx b/src/components/Search/SearchFiltersChatsSelector.tsx index f3dd7035b2b1..edaf911ee772 100644 --- a/src/components/Search/SearchFiltersChatsSelector.tsx +++ b/src/components/Search/SearchFiltersChatsSelector.tsx @@ -98,7 +98,7 @@ function SearchFiltersChatsSelector({initialReportIDs, onFiltersUpdate, isScreen policyCollection: allPolicies, sortedActions, conciergeReportID, - }); + }).options; const chatOptions = filterAndOrderOptions(defaultOptions, cleanSearchTerm, countryCode, loginList, currentUserEmail, currentUserAccountID, personalDetails, { selectedOptions, diff --git a/src/hooks/useAutocompleteSuggestions.ts b/src/hooks/useAutocompleteSuggestions.ts index fea016822280..6dc8e2f958b5 100644 --- a/src/hooks/useAutocompleteSuggestions.ts +++ b/src/hooks/useAutocompleteSuggestions.ts @@ -240,7 +240,7 @@ function useAutocompleteSuggestions({ personalDetails, sortedActions, conciergeReportID, - }).personalDetails.filter((participant) => participant.text && !alreadyAutocompletedKeys.has(participant.text.toLowerCase())); + }).options.personalDetails.filter((participant) => participant.text && !alreadyAutocompletedKeys.has(participant.text.toLowerCase())); return participants.map((participant) => ({ filterKey: autocompleteKey, @@ -277,7 +277,7 @@ function useAutocompleteSuggestions({ personalDetails, sortedActions, conciergeReportID, - }).recentReports.filter((chat) => { + }).options.recentReports.filter((chat) => { if (!chat.text) { return false; } diff --git a/src/hooks/useSearchSelector/base.ts b/src/hooks/useSearchSelector/base.ts index ffa43e2bbbd0..ac983d77a3c0 100644 --- a/src/hooks/useSearchSelector/base.ts +++ b/src/hooks/useSearchSelector/base.ts @@ -217,7 +217,7 @@ function useSearchSelectorBase({ const computedSearchTerm = getSearchValueForPhoneOrEmail(debouncedSearchTerm, countryCode); const trimmedSearchInput = debouncedSearchTerm.trim(); - const baseOptions = (() => { + const {options: baseOptions, hasMore} = (() => { if (!areOptionsInitialized) { return getEmptyOptions(); } @@ -340,7 +340,7 @@ function useSearchSelectorBase({ })(); const onListEndReached = useDebounce(() => { - if (!areOptionsInitialized || !baseOptions.hasMore) { + if (!areOptionsInitialized || !hasMore) { return; } diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 8875ec151354..f6c0f1cff253 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -202,6 +202,7 @@ import type { Option, OptionList, Options, + OptionsResult, OrderOptionsConfig, OrderReportOptionsConfig, PayeePersonalDetails, @@ -2394,7 +2395,7 @@ function getValidOptions( sortedActions, ...config }: GetOptionsConfig = {}, -): Options { +): OptionsResult { const restrictedLogins = getRestrictedLogins(config, options, canShowManagerMcTest); // Gather shared configs: @@ -2677,12 +2678,14 @@ function getValidOptions( } return { - personalDetails: personalDetailsOptions, - recentReports: recentReportOptions, - currentUserOption: currentUserRef.current, - userToInvite, - workspaceChats, - selfDMChat, + options: { + personalDetails: personalDetailsOptions, + recentReports: recentReportOptions, + currentUserOption: currentUserRef.current, + userToInvite, + workspaceChats, + selfDMChat, + }, hasMore, }; } @@ -2740,7 +2743,7 @@ function getSearchOptions({ allPolicyTags, sortedActions, conciergeReportID, -}: SearchOptionsConfig): Options { +}: SearchOptionsConfig): OptionsResult { const optionList = getValidOptions(options, policyCollection, draftComments, loginList, currentUserAccountID, currentUserEmail, conciergeReportID, { betas, includeRecentReports, @@ -3292,12 +3295,15 @@ function sortAlphabetically>, return items.sort((a, b) => localeCompare(a[key]?.toLowerCase() ?? '', b[key]?.toLowerCase() ?? '')); } -function getEmptyOptions(): Options { +function getEmptyOptions(): OptionsResult { return { - recentReports: [], - personalDetails: [], - userToInvite: null, - currentUserOption: null, + options: { + recentReports: [], + personalDetails: [], + userToInvite: null, + currentUserOption: null, + }, + hasMore: false, }; } diff --git a/src/libs/OptionsListUtils/types.ts b/src/libs/OptionsListUtils/types.ts index 747cffd32b53..3aa97fa067c6 100644 --- a/src/libs/OptionsListUtils/types.ts +++ b/src/libs/OptionsListUtils/types.ts @@ -260,7 +260,6 @@ type Options = { currentUserOption: SearchOptionData | null | undefined; workspaceChats?: SearchOptionData[]; selfDMChat?: SearchOptionData | undefined; - hasMore?: boolean; }; type PreviewConfig = { @@ -297,6 +296,11 @@ type OrderReportOptionsConfig = { type ReportAndPersonalDetailOptions = Pick; +type OptionsResult = { + options: Options; + hasMore?: boolean; +}; + export type { FilterUserToInviteConfig, GetOptionsConfig, @@ -318,4 +322,5 @@ export type { SelectionListSections, SectionForSearchTerm, IsValidReportsConfig, + OptionsResult, }; diff --git a/src/pages/NewChatPage/index.tsx b/src/pages/NewChatPage/index.tsx index 981fec1cb92f..b0098715e588 100755 --- a/src/pages/NewChatPage/index.tsx +++ b/src/pages/NewChatPage/index.tsx @@ -88,7 +88,7 @@ function useOptions(reportAttributesDerived: ReportAttributesDerivedValue['repor const personalDetails = listOptions?.personalDetails ?? []; useGroupChatDraftParticipantSync(personalDetails, !isLoading, allPersonalDetails, loginList, currentUserEmail, currentUserAccountID, selectedOptions, setSelectedOptions); - const defaultOptions = getValidOptions( + const {options: defaultOptions} = getValidOptions( { reports, personalDetails: personalDetails.concat(contacts), diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index 1d1e9920f7da..40401cf88b65 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -96,7 +96,7 @@ function ShareTab({ref}: ShareTabProps) { personalDetails, sortedActions, conciergeReportID, - }) + }).options : defaultListOptions; let recentReportsOptions: OptionData[]; From c5b4d9db0056f752fff3f3ab19df723ccb9511dc Mon Sep 17 00:00:00 2001 From: Rohit Verma Date: Thu, 28 May 2026 09:51:41 +0530 Subject: [PATCH 5/7] test: fix failing tests --- tests/perf-test/OptionsListUtils.perf-test.ts | 4 +- .../components/SearchAutocompleteListTest.tsx | 29 +- tests/unit/OptionsListUtilsTest.tsx | 251 ++++++++++-------- .../hooks/useAutocompleteSuggestions.test.ts | 19 +- tests/unit/useSearchSelectorTest.tsx | 84 +++--- 5 files changed, 219 insertions(+), 168 deletions(-) diff --git a/tests/perf-test/OptionsListUtils.perf-test.ts b/tests/perf-test/OptionsListUtils.perf-test.ts index 84c4056b35ab..041736cbbc1e 100644 --- a/tests/perf-test/OptionsListUtils.perf-test.ts +++ b/tests/perf-test/OptionsListUtils.perf-test.ts @@ -147,7 +147,7 @@ describe('OptionsListUtils', () => { /* Testing getFilteredOptions */ test('[OptionsListUtils] getFilteredOptions with search value', async () => { await waitForBatchedUpdates(); - const formattedOptions = getValidOptions( + const {options: formattedOptions} = getValidOptions( {reports: options.reports, personalDetails: options.personalDetails}, allPolicies, {}, @@ -163,7 +163,7 @@ describe('OptionsListUtils', () => { }); test('[OptionsListUtils] getFilteredOptions with empty search value', async () => { await waitForBatchedUpdates(); - const formattedOptions = getValidOptions( + const {options: formattedOptions} = getValidOptions( {reports: options.reports, personalDetails: options.personalDetails}, allPolicies, {}, diff --git a/tests/ui/components/SearchAutocompleteListTest.tsx b/tests/ui/components/SearchAutocompleteListTest.tsx index 95a8d4cf7042..cf727b0865c4 100644 --- a/tests/ui/components/SearchAutocompleteListTest.tsx +++ b/tests/ui/components/SearchAutocompleteListTest.tsx @@ -49,19 +49,22 @@ jest.mock('@hooks/useFilteredOptions', () => ({ jest.mock('@libs/OptionsListUtils', () => ({ getSearchOptions: jest.fn(() => ({ - recentReports: [ - { - reportID: '10', - keyForList: '10', - text: 'Test Report', - alternateText: 'alternate text', - lastMessageText: 'last message', - }, - ], - personalDetails: [], - currentUserOption: null, - userToInvite: null, - categoryOptions: [], + options: { + recentReports: [ + { + reportID: '10', + keyForList: '10', + text: 'Test Report', + alternateText: 'alternate text', + lastMessageText: 'last message', + }, + ], + personalDetails: [], + currentUserOption: null, + userToInvite: null, + categoryOptions: [], + }, + hasMore: false, })), combineOrderingOfReportsAndPersonalDetails: jest.fn(() => ({recentReports: [], personalDetails: []})), getAlternateText: jest.fn(), diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 1906b9606591..af2362be98c9 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -767,7 +767,7 @@ describe('OptionsListUtils', () => { it('should return all options when no search value is provided', () => { // Given a set of options // When we call getSearchOptions with all betas - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -791,7 +791,7 @@ describe('OptionsListUtils', () => { it('should include current user when includeCurrentUser is true for type:chat from suggestions', () => { // Given a set of options where the current user is Iron Man (accountID: 2) // When we call getSearchOptions with includeCurrentUser set to true - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS, draftComments: {}, betas: [CONST.BETAS.ALL], @@ -824,7 +824,7 @@ describe('OptionsListUtils', () => { it('should exclude current user when includeCurrentUser is false', () => { // Given a set of options where the current user is Iron Man (accountID: 2) // When we call getSearchOptions with includeCurrentUser set to false (default behavior) - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS, draftComments: {}, betas: [CONST.BETAS.ALL], @@ -854,7 +854,7 @@ describe('OptionsListUtils', () => { it('should use policyCollection to filter workspace chats correctly', () => { // Given a set of options with workspace rooms // When we call getSearchOptions with policyCollection - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS_WITH_WORKSPACE_ROOM, draftComments: {}, betas: [CONST.BETAS.ALL], @@ -884,7 +884,7 @@ describe('OptionsListUtils', () => { it('should handle empty policyCollection', () => { // Given a set of options // When we call getSearchOptions with empty policyCollection - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS, draftComments: {}, betas: [CONST.BETAS.ALL], @@ -913,7 +913,7 @@ describe('OptionsListUtils', () => { it('should handle undefined policyCollection', () => { // Given a set of options // When we call getSearchOptions with undefined policyCollection - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS, draftComments: {}, betas: [CONST.BETAS.ALL], @@ -943,7 +943,7 @@ describe('OptionsListUtils', () => { // Given a set of options that includes Concierge and a valid conciergeReportID const conciergeReportID = '11'; // When we call getSearchOptions with conciergeReportID - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS_WITH_CONCIERGE, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CONCIERGE, draftComments: {}, @@ -967,7 +967,7 @@ describe('OptionsListUtils', () => { it('should handle undefined conciergeReportID without errors', () => { // Given a set of options with Concierge // When we call getSearchOptions with conciergeReportID set to undefined - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS_WITH_CONCIERGE, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CONCIERGE, draftComments: {}, @@ -993,7 +993,7 @@ describe('OptionsListUtils', () => { // Given a search query that matches Concierge const conciergeReportID = '11'; // When we call getSearchOptions with a search query matching Concierge - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS_WITH_CONCIERGE, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CONCIERGE, draftComments: {}, @@ -1019,7 +1019,7 @@ describe('OptionsListUtils', () => { it('should sort options alphabetically and preserves reportID for personal details with existing reports', () => { // Given a set of reports and personalDetails // When we call getValidOptions() - let results: Pick = getValidOptions( + const {options: validOptions} = getValidOptions( { reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails, @@ -1031,6 +1031,7 @@ describe('OptionsListUtils', () => { CURRENT_USER_EMAIL, undefined, ); + let results: Pick = validOptions; // When we call orderOptions() results = orderOptions(results); @@ -1061,7 +1062,7 @@ describe('OptionsListUtils', () => { it('should sort personal details options alphabetically when only personal details are provided', () => { // Given a set of personalDetails and an empty reports array - let results: Pick = getValidOptions( + const {options: validOptions} = getValidOptions( {personalDetails: OPTIONS.personalDetails, reports: []}, allPolicies, {}, @@ -1070,6 +1071,7 @@ describe('OptionsListUtils', () => { CURRENT_USER_EMAIL, undefined, ); + let results: Pick = validOptions; // When we call orderOptions() results = orderOptions(results); @@ -1096,7 +1098,7 @@ describe('OptionsListUtils', () => { it('should return empty options when no reports or personal details are provided', () => { // Given empty arrays of reports and personalDetails // When we call getValidOptions() - const results = getValidOptions({reports: [], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined); + const {options: results} = getValidOptions({reports: [], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined); // Then the result should be empty expect(results.personalDetails).toEqual([]); @@ -1110,7 +1112,7 @@ describe('OptionsListUtils', () => { it('should include Concierge by default in results', () => { // Given a set of reports and personalDetails that includes Concierge // When we call getValidOptions() - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS_WITH_CONCIERGE.reports, personalDetails: OPTIONS_WITH_CONCIERGE.personalDetails}, allPolicies, {}, @@ -1129,7 +1131,7 @@ describe('OptionsListUtils', () => { it('should exclude Concierge when excludedLogins is specified', () => { // Given a set of reports and personalDetails that includes Concierge and a config object that excludes Concierge // When we call getValidOptions() - const results = getValidOptions( + const {options: results} = getValidOptions( { reports: OPTIONS_WITH_CONCIERGE.reports, personalDetails: OPTIONS_WITH_CONCIERGE.personalDetails, @@ -1156,7 +1158,7 @@ describe('OptionsListUtils', () => { // Given a set of reports that includes Concierge and a valid conciergeReportID const conciergeReportID = '11'; // When we call getValidOptions() with a conciergeReportID - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS_WITH_CONCIERGE.reports, personalDetails: OPTIONS_WITH_CONCIERGE.personalDetails}, allPolicies, {}, @@ -1176,7 +1178,7 @@ describe('OptionsListUtils', () => { it('should exclude Chronos when excludedLogins is specified', () => { // Given a set of reports and personalDetails that includes Chronos and a config object that excludes Chronos // When we call getValidOptions() - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS_WITH_CHRONOS.reports, personalDetails: OPTIONS_WITH_CHRONOS.personalDetails}, allPolicies, {}, @@ -1199,7 +1201,7 @@ describe('OptionsListUtils', () => { it('should exclude Receipts option from results when excludedLogins is specified', () => { // Given a set of reports and personalDetails that includes receipts and a config object that excludes receipts // When we call getValidOptions() - const results = getValidOptions( + const {options: results} = getValidOptions( { reports: OPTIONS_WITH_RECEIPTS.reports, personalDetails: OPTIONS_WITH_RECEIPTS.personalDetails, @@ -1257,7 +1259,7 @@ describe('OptionsListUtils', () => { notificationPreference: 'hidden', }; // When we call getValidOptions with includeMultipleParticipantReports set to true - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: [adminRoom], personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1312,7 +1314,7 @@ describe('OptionsListUtils', () => { notificationPreference: 'hidden', brickRoadIndicator: CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR, }; - const results = getValidOptions({reports: [workspaceChat], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [workspaceChat], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeMultipleParticipantReports: true, showRBR: true, sortedActions: undefined, @@ -1356,7 +1358,7 @@ describe('OptionsListUtils', () => { notificationPreference: 'hidden', brickRoadIndicator: CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR, }; - const results = getValidOptions({reports: [workspaceChat], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [workspaceChat], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeMultipleParticipantReports: true, showRBR: false, sortedActions: undefined, @@ -1404,7 +1406,7 @@ describe('OptionsListUtils', () => { isBold: false, }; - const results = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, shouldUnreadBeBold: true, includeMultipleParticipantReports: true, @@ -1431,10 +1433,19 @@ describe('OptionsListUtils', () => { }; // When we call getValidOptions with personalDetails parameter - const results = getValidOptions({reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, {}, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { - personalDetails: customPersonalDetails, - sortedActions: undefined, - }); + const {options: results} = getValidOptions( + {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, + {}, + {}, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + undefined, + { + personalDetails: customPersonalDetails, + sortedActions: undefined, + }, + ); // Then the function should complete without errors and return valid results // The personalDetails param is used internally by prepareReportOptionsForDisplay for workspace chats @@ -1447,7 +1458,7 @@ describe('OptionsListUtils', () => { it('should include all reports by default', () => { // Given a set of reports and personalDetails that includes workspace rooms // When we call getValidOptions() - const results = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, includeMultipleParticipantReports: true, includeP2P: true, @@ -1480,7 +1491,7 @@ describe('OptionsListUtils', () => { }; // When we call getValidOptions with shouldSeparateWorkspaceChat and personalDetails config - const results = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, includeMultipleParticipantReports: true, includeP2P: true, @@ -1499,7 +1510,7 @@ describe('OptionsListUtils', () => { it('should handle undefined personalDetails config in workspace chat lookups', () => { // Given a set of reports with workspace rooms // When we call getValidOptions with shouldSeparateWorkspaceChat but no personalDetails config - const results = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, includeMultipleParticipantReports: true, includeP2P: true, @@ -1517,7 +1528,7 @@ describe('OptionsListUtils', () => { it('should handle empty personalDetails config in workspace chat lookups', () => { // Given a set of reports with workspace rooms // When we call getValidOptions with shouldSeparateWorkspaceChat and empty personalDetails - const results = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, includeMultipleParticipantReports: true, includeP2P: true, @@ -1535,7 +1546,7 @@ describe('OptionsListUtils', () => { it('should handle null personalDetails config in workspace chat lookups', () => { // Given a set of reports with workspace rooms // When we call getValidOptions with shouldSeparateWorkspaceChat and null personalDetails - const results = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions(OPTIONS_WITH_WORKSPACE_ROOM, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, includeMultipleParticipantReports: true, includeP2P: true, @@ -1555,7 +1566,7 @@ describe('OptionsListUtils', () => { it('should exclude users with recent reports from personalDetails', () => { // Given a set of reports and personalDetails // When we call getValidOptions with no search value - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1576,7 +1587,7 @@ describe('OptionsListUtils', () => { it('should exclude selected options', () => { // Given a set of reports and personalDetails // When we call getValidOptions with excludeLogins param - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1598,7 +1609,7 @@ describe('OptionsListUtils', () => { it('should include Concierge in the results by default', () => { // Given a set of report and personalDetails that include Concierge // When we call getValidOptions() - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS_WITH_CONCIERGE.reports, personalDetails: OPTIONS_WITH_CONCIERGE.personalDetails}, allPolicies, {}, @@ -1617,7 +1628,7 @@ describe('OptionsListUtils', () => { it('should exclude Concierge from the results when it is specified in excludedLogins', () => { // Given a set of reports and personalDetails that includes Concierge // When we call getValidOptions with excludeLogins param - const results = getValidOptions( + const {options: results} = getValidOptions( { reports: OPTIONS_WITH_CONCIERGE.reports, personalDetails: OPTIONS_WITH_CONCIERGE.personalDetails, @@ -1644,7 +1655,7 @@ describe('OptionsListUtils', () => { it('should exclude Chronos from the results when it is specified in excludedLogins', () => { // given a set of reports and personalDetails that includes Chronos // When we call getValidOptions() with excludeLogins param - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS_WITH_CHRONOS.reports, personalDetails: OPTIONS_WITH_CHRONOS.personalDetails}, allPolicies, {}, @@ -1668,7 +1679,7 @@ describe('OptionsListUtils', () => { it('should exclude Receipts from the results when it is specified in excludedLogins', () => { // Given a set of reports and personalDetails that includes receipts // When we call getValidOptions() with excludeLogins param - const results = getValidOptions( + const {options: results} = getValidOptions( { reports: OPTIONS_WITH_RECEIPTS.reports, personalDetails: OPTIONS_WITH_RECEIPTS.personalDetails, @@ -1696,7 +1707,7 @@ describe('OptionsListUtils', () => { // Given a set of reports and personalDetails with multiple reports // When we call getValidOptions with maxRecentReportElements set to 2 const maxRecentReports = 2; - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1717,7 +1728,7 @@ describe('OptionsListUtils', () => { it('should show all reports when maxRecentReportElements is not specified', () => { // Given a set of reports and personalDetails // When we call getValidOptions without maxRecentReportElements - const resultsWithoutLimit = getValidOptions( + const {options: resultsWithoutLimit} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1726,7 +1737,7 @@ describe('OptionsListUtils', () => { CURRENT_USER_EMAIL, undefined, ); - const resultsWithLimit = getValidOptions( + const {options: resultsWithLimit} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1747,7 +1758,7 @@ describe('OptionsListUtils', () => { it('should not affect personalDetails count when maxRecentReportElements is specified', () => { // Given a set of reports and personalDetails // When we call getValidOptions with and without maxRecentReportElements - const resultsWithoutLimit = getValidOptions( + const {options: resultsWithoutLimit} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1756,7 +1767,7 @@ describe('OptionsListUtils', () => { CURRENT_USER_EMAIL, undefined, ); - const resultsWithLimit = getValidOptions( + const {options: resultsWithLimit} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1779,7 +1790,7 @@ describe('OptionsListUtils', () => { // When we call getValidOptions with both maxElements and maxRecentReportElements const maxRecentReports = 3; const maxTotalElements = 10; - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1814,7 +1825,7 @@ describe('OptionsListUtils', () => { }, []); // When we call getValidOptions for share destination with an empty search value - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: filteredReports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -1856,7 +1867,7 @@ describe('OptionsListUtils', () => { }, []); // When we call getValidOptions for share destination with an empty search value - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: filteredReportsWithWorkspaceRooms, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2045,7 +2056,7 @@ describe('OptionsListUtils', () => { it('should return all options when search is empty', () => { // Given a set of options // When we call getSearchOptions with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2069,7 +2080,7 @@ describe('OptionsListUtils', () => { const searchText = 'man'; // Given a set of options // When we call getSearchOptions with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2102,7 +2113,7 @@ describe('OptionsListUtils', () => { const searchText = 'mistersinister@marauders.com'; // Given a set of options // When we call getSearchOptions with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2132,7 +2143,7 @@ describe('OptionsListUtils', () => { }; const OPTIONS_WITH_ARCHIVED = createOptionList(PERSONAL_DETAILS, archivedMap, REPORTS, undefined, MOCK_REPORT_ATTRIBUTES_DERIVED); // When we call getSearchOptions with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_ARCHIVED, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2160,7 +2171,7 @@ describe('OptionsListUtils', () => { // Given a set of options created from PERSONAL_DETAILS_WITH_PERIODS const OPTIONS_WITH_PERIODS = createOptionList(PERSONAL_DETAILS_WITH_PERIODS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS, undefined); // When we call getSearchOptions with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_PERIODS, draftComments: {}, loginList, @@ -2187,7 +2198,7 @@ describe('OptionsListUtils', () => { const searchText = 'avengers'; // Given a set of options with workspace rooms // When we call getSearchOptions with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_WORKSPACE_ROOM, draftComments: {}, loginList, @@ -2211,7 +2222,7 @@ describe('OptionsListUtils', () => { it('should put exact match by login on the top of the list', () => { const searchText = 'reedrichards@expensify.com'; // Given a set of options with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2245,7 +2256,7 @@ describe('OptionsListUtils', () => { MOCK_REPORT_ATTRIBUTES_DERIVED_WITH_CHAT_ROOM, ); // When we call getSearchOptions with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_CHAT_ROOMS, draftComments: {}, loginList, @@ -2270,7 +2281,7 @@ describe('OptionsListUtils', () => { renderLocaleContextProvider(); const searchText = 'fantastic'; // Given a set of options - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2296,7 +2307,7 @@ describe('OptionsListUtils', () => { it('should return the user to invite when the search value is a valid, non-existent email', () => { const searchText = 'test@email.com'; // Given a set of options - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2319,7 +2330,7 @@ describe('OptionsListUtils', () => { it('should not return any results if the search value is on an excluded logins list', () => { const searchText = 'admin@expensify.com'; // Given a set of options with excluded logins list - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2344,7 +2355,7 @@ describe('OptionsListUtils', () => { it('should return the user to invite when the search value is a valid, non-existent email and the user is not excluded', () => { const searchText = 'test@email.com'; // Given a set of options - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2369,7 +2380,7 @@ describe('OptionsListUtils', () => { it('should return limited amount of recent reports if the limit is set', () => { const searchText = ''; // Given a set of options - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2403,7 +2414,7 @@ describe('OptionsListUtils', () => { it('should not return any user to invite if email exists on the personal details list', () => { const searchText = 'natasharomanoff@expensify.com'; // Given a set of options with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -2435,7 +2446,7 @@ describe('OptionsListUtils', () => { return filtered; }, []); // When we call getValidOptions for share destination with the filteredReports - const options = getValidOptions( + const {options} = getValidOptions( {reports: filteredReports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2478,7 +2489,7 @@ describe('OptionsListUtils', () => { }, []); // When we call getValidOptions for share destination with the filteredReports - const options = getValidOptions( + const {options} = getValidOptions( {reports: filteredReportsWithWorkspaceRooms, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2521,7 +2532,7 @@ describe('OptionsListUtils', () => { }, []); // When we call getValidOptions for share destination with the filteredReports - const options = getValidOptions( + const {options} = getValidOptions( {reports: filteredReportsWithWorkspaceRooms, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2554,7 +2565,7 @@ describe('OptionsListUtils', () => { it('should show the option from personal details when searching for personal detail with no existing report', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2596,7 +2607,7 @@ describe('OptionsListUtils', () => { const OPTIONS_WITH_GROUP_CHAT = createOptionList(PERSONAL_DETAILS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS_WITH_GROUP_CHAT, undefined); // When we call getSearchOptions with a search query that matches a participant display name - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_GROUP_CHAT, draftComments: {}, loginList, @@ -2638,7 +2649,7 @@ describe('OptionsListUtils', () => { const OPTIONS_WITH_GROUP_CHAT = createOptionList(PERSONAL_DETAILS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS_WITH_GROUP_CHAT, undefined); // When we call getSearchOptions with a search query that matches a participant login - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_GROUP_CHAT, draftComments: {}, loginList, @@ -2680,7 +2691,7 @@ describe('OptionsListUtils', () => { const OPTIONS_WITH_GROUP_CHAT = createOptionList(PERSONAL_DETAILS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS_WITH_GROUP_CHAT, undefined); // When we call getSearchOptions with a search query that matches a participant name - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_GROUP_CHAT, draftComments: {}, loginList, @@ -2722,7 +2733,7 @@ describe('OptionsListUtils', () => { const OPTIONS_WITH_GROUP_CHAT = createOptionList(PERSONAL_DETAILS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS_WITH_GROUP_CHAT, undefined); // When we call getSearchOptions with a search query that does not match any participant - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_GROUP_CHAT, draftComments: {}, loginList, @@ -2761,7 +2772,7 @@ describe('OptionsListUtils', () => { const OPTIONS_WITH_GROUP_CHAT_NO_PARTICIPANTS = createOptionList(PERSONAL_DETAILS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS_WITH_GROUP_CHAT_NO_PARTICIPANTS, undefined); // When we call getSearchOptions with all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_GROUP_CHAT_NO_PARTICIPANTS, draftComments: {}, loginList, @@ -2784,7 +2795,7 @@ describe('OptionsListUtils', () => { it('should not return any options or user to invite if there are no search results and the string does not match a potential email or phone', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2805,7 +2816,7 @@ describe('OptionsListUtils', () => { it('should not return any options but should return an user to invite if no matching options exist and the search value is a potential email', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2826,7 +2837,7 @@ describe('OptionsListUtils', () => { it('should return user to invite when search term has a period with options for it that do not contain the period', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2846,7 +2857,7 @@ describe('OptionsListUtils', () => { it('should return user which has displayName with accent mark when search value without accent mark', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2864,7 +2875,7 @@ describe('OptionsListUtils', () => { it('should not return options but should return an user to invite if no matching options exist and the search value is a potential phone number', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2887,7 +2898,7 @@ describe('OptionsListUtils', () => { it('should not return options but should return an user to invite if no matching options exist and the search value is a potential phone number with country code added', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2910,7 +2921,7 @@ describe('OptionsListUtils', () => { it('should not return options but should return an user to invite if no matching options exist and the search value is a potential phone number with special characters added', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2933,7 +2944,7 @@ describe('OptionsListUtils', () => { it('should not return any options or user to invite if contact number contains alphabet characters', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -2954,10 +2965,19 @@ describe('OptionsListUtils', () => { it('should not return userToInvite for plain text name when shouldAcceptName is false', () => { // Given a set of options - const options = getValidOptions({reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, {}, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { - includeUserToInvite: true, - sortedActions: undefined, - }); + const {options} = getValidOptions( + {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, + {}, + {}, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + undefined, + { + includeUserToInvite: true, + sortedActions: undefined, + }, + ); // When we call filterAndOrderOptions with a plain text name (not email or phone) without shouldAcceptName const filteredOptions = filterAndOrderOptions(options, 'Jeff Amazon', COUNTRY_CODE, loginList, CURRENT_USER_EMAIL, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, { @@ -2970,10 +2990,19 @@ describe('OptionsListUtils', () => { it('should return userToInvite for plain text name when shouldAcceptName is true', () => { // Given a set of options - const options = getValidOptions({reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, {}, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { - includeUserToInvite: true, - sortedActions: undefined, - }); + const {options} = getValidOptions( + {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, + {}, + {}, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + undefined, + { + includeUserToInvite: true, + sortedActions: undefined, + }, + ); // When we call filterAndOrderOptions with a plain text name (not email or phone) with shouldAcceptName const filteredOptions = filterAndOrderOptions(options, 'Jeff', COUNTRY_CODE, loginList, CURRENT_USER_EMAIL, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, { @@ -2986,7 +3015,7 @@ describe('OptionsListUtils', () => { it('should not return any options if search value does not match any personal details', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -3004,7 +3033,7 @@ describe('OptionsListUtils', () => { it('should return one recent report and no personal details if a search value provides an email', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -3028,7 +3057,7 @@ describe('OptionsListUtils', () => { it('should return all matching reports and personal details', () => { // Given a set of options - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -3055,7 +3084,7 @@ describe('OptionsListUtils', () => { it('should return matching option when searching (getSearchOptions)', () => { // Given a set of options - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -3078,7 +3107,7 @@ describe('OptionsListUtils', () => { it('should return latest lastVisibleActionCreated item on top when search value matches multiple items (getSearchOptions)', () => { // Given a set of options - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -3106,7 +3135,7 @@ describe('OptionsListUtils', () => { // Given a set of options with periods const OPTIONS_WITH_PERIODS = createOptionList(PERSONAL_DETAILS_WITH_PERIODS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS, undefined); // When we call getSearchOptions - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: OPTIONS_WITH_PERIODS, draftComments: {}, loginList, @@ -3145,7 +3174,7 @@ describe('OptionsListUtils', () => { OPTIONS.personalDetails = OPTIONS.personalDetails.flatMap((obj) => [obj, {...obj}]); // Given a set of options - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, reportAttributesDerived: MOCK_REPORT_ATTRIBUTES_DERIVED, draftComments: {}, @@ -3173,7 +3202,7 @@ describe('OptionsListUtils', () => { const OPTIONS_WITH_SELF_DM = createOptionList(PERSONAL_DETAILS, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS_WITH_SELF_DM, undefined); // Given a set of options with self dm and all betas - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS_WITH_SELF_DM, draftComments: {}, loginList, @@ -5641,7 +5670,7 @@ describe('OptionsListUtils', () => { const policies = {[`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`]: policy}; // Test that getValidOptions accepts policies collection as second parameter - const results = getValidOptions({reports: [], personalDetails: []}, policies, undefined, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined); + const {options: results} = getValidOptions({reports: [], personalDetails: []}, policies, undefined, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined); expect(results).toBeDefined(); expect(results.recentReports).toBeDefined(); @@ -5650,7 +5679,7 @@ describe('OptionsListUtils', () => { it('should work with undefined policies', () => { const options = {reports: [], personalDetails: []}; - const results = getValidOptions(options, undefined, undefined, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined); + const {options: results} = getValidOptions(options, undefined, undefined, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined); expect(results).toBeDefined(); expect(results.recentReports).toBeDefined(); @@ -5659,7 +5688,7 @@ describe('OptionsListUtils', () => { it('should work with empty policies collection', () => { const options = {reports: [], personalDetails: []}; - const results = getValidOptions(options, {}, undefined, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined); + const {options: results} = getValidOptions(options, {}, undefined, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined); expect(results).toBeDefined(); expect(results.recentReports).toBeDefined(); @@ -5683,7 +5712,7 @@ describe('OptionsListUtils', () => { const policies = {[`${ONYXKEYS.COLLECTION.POLICY}${testPolicyID}`]: policy}; // Verify function works with policies parameter - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, policies, undefined, @@ -7060,7 +7089,7 @@ describe('OptionsListUtils', () => { describe('reports parameter functionality', () => { it('getValidOptions should use reports parameter to look up chat reports', () => { // When we call getValidOptions with the reports collection - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -7078,7 +7107,7 @@ describe('OptionsListUtils', () => { it('filterAndOrderOptions should use reports parameter correctly', () => { // Given a set of options and reports collection - const options = getValidOptions( + const {options} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -7099,7 +7128,7 @@ describe('OptionsListUtils', () => { it('getSearchOptions should use reports parameter from config', () => { // When we call getSearchOptions with reports in the config - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, draftComments: {}, loginList, @@ -7119,7 +7148,7 @@ describe('OptionsListUtils', () => { it('getSearchOptions should forward sortedActions to getValidOptions', () => { const sortedActions = {}; - const options = getSearchOptions({ + const {options} = getSearchOptions({ options: OPTIONS, draftComments: {}, loginList, @@ -7152,7 +7181,7 @@ describe('OptionsListUtils', () => { it('should work correctly when reports is an empty object', () => { // When we call getValidOptions with empty reports - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -7170,7 +7199,7 @@ describe('OptionsListUtils', () => { it('should work correctly when reports is undefined', () => { // When we call getValidOptions without reports parameter - const results = getValidOptions( + const {options: results} = getValidOptions( {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, allPolicies, {}, @@ -7813,7 +7842,7 @@ describe('OptionsListUtils', () => { [iouReportID]: [iouAction], }; - const results = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, includeMultipleParticipantReports: true, action: CONST.IOU.ACTION.CREATE, @@ -7867,7 +7896,7 @@ describe('OptionsListUtils', () => { const sortedActions = {}; - const results = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, action: CONST.IOU.ACTION.CREATE, sortedActions, @@ -7917,7 +7946,7 @@ describe('OptionsListUtils', () => { isBold: false, }; - const results = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, action: CONST.IOU.ACTION.CREATE, sortedActions: undefined, @@ -7993,7 +8022,7 @@ describe('OptionsListUtils', () => { [iouReportID]: [newerIOUAction, olderIOUAction], }; - const results = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, includeMultipleParticipantReports: true, action: CONST.IOU.ACTION.CREATE, @@ -8063,7 +8092,7 @@ describe('OptionsListUtils', () => { [iouReportID]: [iouAction], }; - const results = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, sortedActions, }); @@ -8121,7 +8150,7 @@ describe('OptionsListUtils', () => { [reportID]: [nonPreviewAction], }; - const results = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, action: CONST.IOU.ACTION.CREATE, sortedActions, @@ -8192,7 +8221,7 @@ describe('OptionsListUtils', () => { [iouReportID]: [iouAction], }; - const results = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, includeMultipleParticipantReports: true, action: CONST.IOU.ACTION.CREATE, @@ -8248,7 +8277,7 @@ describe('OptionsListUtils', () => { isBold: false, }; - const results = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { + const {options: results} = getValidOptions({reports: [inputOption], personalDetails: []}, allPolicies, {}, loginList, CURRENT_USER_ACCOUNT_ID, CURRENT_USER_EMAIL, undefined, { includeRecentReports: true, action: CONST.IOU.ACTION.CREATE, sortedActions: {[reportID]: [commentAction]}, @@ -8299,7 +8328,7 @@ describe('OptionsListUtils', () => { isBold: false, }; - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: {reports: [inputOption], personalDetails: []}, draftComments: {}, betas: [CONST.BETAS.ALL], @@ -8365,7 +8394,7 @@ describe('OptionsListUtils', () => { [reportID]: [iouAction], }; - const results = getSearchOptions({ + const {options: results} = getSearchOptions({ options: {reports: [inputOption], personalDetails: []}, draftComments: {}, betas: [CONST.BETAS.ALL], diff --git a/tests/unit/hooks/useAutocompleteSuggestions.test.ts b/tests/unit/hooks/useAutocompleteSuggestions.test.ts index c9721ba6d428..71b884f3a63a 100644 --- a/tests/unit/hooks/useAutocompleteSuggestions.test.ts +++ b/tests/unit/hooks/useAutocompleteSuggestions.test.ts @@ -38,14 +38,17 @@ jest.mock('@libs/SearchAutocompleteUtils', () => ({ jest.mock('@libs/OptionsListUtils', () => ({ getSearchOptions: jest.fn(() => ({ - personalDetails: [ - {text: 'John Doe', login: 'john@example.com', accountID: 1}, - {text: 'Jane Smith', login: 'jane@example.com', accountID: 2}, - ], - recentReports: [ - {text: 'General Chat', reportID: 'report1'}, - {text: 'Team Updates', reportID: 'report2'}, - ], + options: { + personalDetails: [ + {text: 'John Doe', login: 'john@example.com', accountID: 1}, + {text: 'Jane Smith', login: 'jane@example.com', accountID: 2}, + ], + recentReports: [ + {text: 'General Chat', reportID: 'report1'}, + {text: 'Team Updates', reportID: 'report2'}, + ], + }, + hasMore: false, })), })); diff --git a/tests/unit/useSearchSelectorTest.tsx b/tests/unit/useSearchSelectorTest.tsx index b8e096202807..741e6a6b0faa 100644 --- a/tests/unit/useSearchSelectorTest.tsx +++ b/tests/unit/useSearchSelectorTest.tsx @@ -18,8 +18,8 @@ const EMPTY_OPTIONS = {recentReports: [], personalDetails: [], userToInvite: nul jest.mock('@libs/OptionsListUtils', () => ({ __esModule: true, ...jest.requireActual('@libs/OptionsListUtils'), - getValidOptions: jest.fn(() => EMPTY_OPTIONS), - getSearchOptions: jest.fn(() => EMPTY_OPTIONS), + getValidOptions: jest.fn(() => ({options: EMPTY_OPTIONS, hasMore: false})), + getSearchOptions: jest.fn(() => ({options: EMPTY_OPTIONS, hasMore: false})), })); const MOCK_ACCOUNT_ID = 12345; @@ -305,10 +305,12 @@ describe('useSearchSelector selection and non-existing options', () => { it('keeps selected contacts in availableOptions.personalDetails when shouldKeepSelectedInAvailableOptions is true', async () => { const optionsWithSelected = { - recentReports: [], - personalDetails: [{...EXISTING_CONTACT, isSelected: true}, SECOND_CONTACT], - userToInvite: null, - currentUserOption: null, + options: { + recentReports: [], + personalDetails: [{...EXISTING_CONTACT, isSelected: true}, SECOND_CONTACT], + userToInvite: null, + currentUserOption: null, + }, }; mockGetValidOptions.mockReturnValue(optionsWithSelected); @@ -330,10 +332,12 @@ describe('useSearchSelector selection and non-existing options', () => { it('filters out selected contacts from availableOptions.personalDetails when shouldKeepSelectedInAvailableOptions is false', async () => { const optionsWithSelected = { - recentReports: [], - personalDetails: [{...EXISTING_CONTACT, isSelected: true}, SECOND_CONTACT], - userToInvite: null, - currentUserOption: null, + options: { + recentReports: [], + personalDetails: [{...EXISTING_CONTACT, isSelected: true}, SECOND_CONTACT], + userToInvite: null, + currentUserOption: null, + }, }; mockGetValidOptions.mockReturnValue(optionsWithSelected); @@ -356,10 +360,12 @@ describe('useSearchSelector selection and non-existing options', () => { it('populates selectedNonExistingOptions with selected users not in personalDetails when shouldSeparateNonExistingSelectedOptions is true', async () => { // Return contacts that do NOT include the non-existing user const optionsWithContacts = { - recentReports: [], - personalDetails: [EXISTING_CONTACT], - userToInvite: null, - currentUserOption: null, + options: { + recentReports: [], + personalDetails: [EXISTING_CONTACT], + userToInvite: null, + currentUserOption: null, + }, }; mockGetValidOptions.mockReturnValue(optionsWithContacts); @@ -385,10 +391,12 @@ describe('useSearchSelector selection and non-existing options', () => { it('returns empty selectedNonExistingOptions when shouldSeparateNonExistingSelectedOptions is false', async () => { const optionsWithContacts = { - recentReports: [], - personalDetails: [EXISTING_CONTACT], - userToInvite: null, - currentUserOption: null, + options: { + recentReports: [], + personalDetails: [EXISTING_CONTACT], + userToInvite: null, + currentUserOption: null, + }, }; mockGetValidOptions.mockReturnValue(optionsWithContacts); @@ -408,10 +416,12 @@ describe('useSearchSelector selection and non-existing options', () => { it('does not include existing contacts in selectedNonExistingOptions', async () => { const optionsWithContacts = { - recentReports: [], - personalDetails: [{...EXISTING_CONTACT, isSelected: true}, SECOND_CONTACT], - userToInvite: null, - currentUserOption: null, + options: { + recentReports: [], + personalDetails: [{...EXISTING_CONTACT, isSelected: true}, SECOND_CONTACT], + userToInvite: null, + currentUserOption: null, + }, }; mockGetValidOptions.mockReturnValue(optionsWithContacts); @@ -436,10 +446,12 @@ describe('useSearchSelector selection and non-existing options', () => { it('adds non-existing user to selectedNonExistingOptions after toggleSelection', async () => { const optionsWithUserToInvite = { - recentReports: [], - personalDetails: [EXISTING_CONTACT], - userToInvite: NON_EXISTING_USER_TO_INVITE, - currentUserOption: null, + options: { + recentReports: [], + personalDetails: [EXISTING_CONTACT], + userToInvite: NON_EXISTING_USER_TO_INVITE, + currentUserOption: null, + }, }; mockGetValidOptions.mockReturnValue(optionsWithUserToInvite); @@ -472,10 +484,12 @@ describe('useSearchSelector selection and non-existing options', () => { it('removes non-existing user from selectedNonExistingOptions after deselection', async () => { const optionsWithContacts = { - recentReports: [], - personalDetails: [EXISTING_CONTACT], - userToInvite: null, - currentUserOption: null, + options: { + recentReports: [], + personalDetails: [EXISTING_CONTACT], + userToInvite: null, + currentUserOption: null, + }, }; mockGetValidOptions.mockReturnValue(optionsWithContacts); @@ -504,10 +518,12 @@ describe('useSearchSelector selection and non-existing options', () => { it('handles mix of existing and non-existing selected users correctly', async () => { const optionsWithContacts = { - recentReports: [], - personalDetails: [{...EXISTING_CONTACT, isSelected: true}], - userToInvite: null, - currentUserOption: null, + options: { + recentReports: [], + personalDetails: [{...EXISTING_CONTACT, isSelected: true}], + userToInvite: null, + currentUserOption: null, + }, }; mockGetValidOptions.mockReturnValue(optionsWithContacts); From ba52595915fbc8cfdab321f9cddfef5773fe3b46 Mon Sep 17 00:00:00 2001 From: Rohit Verma Date: Thu, 28 May 2026 10:00:50 +0530 Subject: [PATCH 6/7] test: fix AssignCardFeed ui test --- tests/ui/AssignCardFeed.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/ui/AssignCardFeed.tsx b/tests/ui/AssignCardFeed.tsx index f4e30392cbec..e1521be9ff9e 100644 --- a/tests/ui/AssignCardFeed.tsx +++ b/tests/ui/AssignCardFeed.tsx @@ -206,8 +206,8 @@ describe('AssignCardFeed', () => { searchTerm: '', debouncedSearchTerm: '', setSearchTerm: jest.fn(), - searchOptions: getEmptyOptions(), - availableOptions: getEmptyOptions(), + searchOptions: getEmptyOptions().options, + availableOptions: getEmptyOptions().options, selectedOptions: [], selectedOptionsForDisplay: [], setSelectedOptions: jest.fn(), @@ -261,8 +261,8 @@ describe('AssignCardFeed', () => { searchTerm: '', debouncedSearchTerm: '', setSearchTerm: jest.fn(), - searchOptions: getEmptyOptions(), - availableOptions: getEmptyOptions(), + searchOptions: getEmptyOptions().options, + availableOptions: getEmptyOptions().options, selectedOptions: [], selectedOptionsForDisplay: [], setSelectedOptions: jest.fn(), @@ -314,8 +314,8 @@ describe('AssignCardFeed', () => { searchTerm: '', debouncedSearchTerm: '', setSearchTerm: jest.fn(), - searchOptions: getEmptyOptions(), - availableOptions: getEmptyOptions(), + searchOptions: getEmptyOptions().options, + availableOptions: getEmptyOptions().options, selectedOptions: [], selectedOptionsForDisplay: [], setSelectedOptions: jest.fn(), @@ -589,8 +589,8 @@ describe('AssignCardFeed', () => { searchTerm: '', debouncedSearchTerm: '', setSearchTerm: jest.fn(), - searchOptions: getEmptyOptions(), - availableOptions: getEmptyOptions(), + searchOptions: getEmptyOptions().options, + availableOptions: getEmptyOptions().options, selectedOptions: [], selectedOptionsForDisplay: [], setSelectedOptions: jest.fn(), @@ -644,8 +644,8 @@ describe('AssignCardFeed', () => { searchTerm: '', debouncedSearchTerm: '', setSearchTerm: jest.fn(), - searchOptions: getEmptyOptions(), - availableOptions: getEmptyOptions(), + searchOptions: getEmptyOptions().options, + availableOptions: getEmptyOptions().options, selectedOptions: [], selectedOptionsForDisplay: [], setSelectedOptions: jest.fn(), From bad212f0b4d3338e40ef67bc1b46c1edb257d9c4 Mon Sep 17 00:00:00 2001 From: Rohit Verma Date: Thu, 28 May 2026 11:27:44 +0530 Subject: [PATCH 7/7] test: add unit tests for hasMorec case of getValidOptions --- tests/unit/OptionsListUtilsTest.tsx | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index af2362be98c9..8a2f8d55cc13 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -1452,6 +1452,36 @@ describe('OptionsListUtils', () => { expect(results.recentReports.length).toBeGreaterThan(0); expect(results.personalDetails.length).toBeGreaterThan(0); }); + + it('should return hasMore true when there are more options than maxElements', () => { + const {hasMore} = getValidOptions( + {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, + allPolicies, + {}, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + undefined, + {maxElements: 1}, + ); + + expect(hasMore).toBe(true); + }); + + it('should return hasMore false when maxElements is larger than the total number of options', () => { + const {hasMore} = getValidOptions( + {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, + allPolicies, + {}, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + undefined, + {maxElements: 100}, + ); + + expect(hasMore).toBe(false); + }); }); describe('getValidOptions() for chat room', () => {