Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: Filter options in Request Money and Send Money #40235

Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
977e122
add filtering to money requests
TMisiukiewicz Apr 12, 2024
d66c2e8
create optimistic user when filtering
TMisiukiewicz Apr 12, 2024
ce44bde
exclude already created users and restricted emails
TMisiukiewicz Apr 15, 2024
047f320
simplify generating header message
TMisiukiewicz Apr 15, 2024
ffa8eb5
Merge branch 'main' into perf/filter-money-request-participants
TMisiukiewicz Apr 15, 2024
ccf5350
fix error when loading options
TMisiukiewicz Apr 15, 2024
f28e5e8
fix typechecks and tests
TMisiukiewicz Apr 15, 2024
2b4c5c5
fix tests
TMisiukiewicz Apr 15, 2024
dd42f18
update filtering
TMisiukiewicz Apr 16, 2024
f558989
Merge branch 'main' into perf/filter-money-request-participants
TMisiukiewicz Apr 16, 2024
8822800
Merge branch 'main' into perf/filter-money-request-participants
TMisiukiewicz Apr 17, 2024
477260e
prettier
TMisiukiewicz Apr 17, 2024
f8834dc
Merge remote-tracking branch 'upstream/main' into perf/filter-money-r…
TMisiukiewicz Apr 18, 2024
bef018a
Merge branch 'main' into perf/filter-money-request-participants
TMisiukiewicz Apr 22, 2024
4aaef6c
get participants
TMisiukiewicz Apr 22, 2024
a8b581a
code review updates
TMisiukiewicz Apr 22, 2024
0bf553b
fix test
TMisiukiewicz Apr 22, 2024
e873c96
search recent by workspace name
TMisiukiewicz Apr 22, 2024
17b178e
update displaying recent reports
TMisiukiewicz Apr 22, 2024
e862eb9
Merge remote-tracking branch 'upstream/main' into perf/filter-money-r…
TMisiukiewicz Apr 22, 2024
05f60cb
update getFilteredOptions usage
TMisiukiewicz Apr 22, 2024
38147dc
Merge remote-tracking branch 'upstream/main' into perf/filter-money-r…
TMisiukiewicz Apr 23, 2024
767db86
resolve nab comments
TMisiukiewicz Apr 23, 2024
c17880b
Merge branch 'main' into perf/filter-money-request-participants
TMisiukiewicz Apr 24, 2024
6a669c6
code review updates
TMisiukiewicz Apr 24, 2024
d3fb666
use reduceRight when filtering
TMisiukiewicz Apr 24, 2024
4e3ec39
fix typecheck
TMisiukiewicz Apr 24, 2024
79a7140
add tests for canCreateOptimisticPersonalDetailOption
TMisiukiewicz Apr 24, 2024
80e5b83
add more tests for filterOptions
TMisiukiewicz Apr 24, 2024
3c822e5
Merge branch 'main' into perf/filter-money-request-participants
TMisiukiewicz Apr 25, 2024
e3b1afa
Merge remote-tracking branch 'upstream/main' into perf/filter-money-r…
TMisiukiewicz Apr 26, 2024
49635ed
fix types
TMisiukiewicz Apr 26, 2024
49a421f
fix tests
TMisiukiewicz Apr 26, 2024
712a381
Merge remote-tracking branch 'upstream/main' into perf/filter-money-r…
TMisiukiewicz Apr 29, 2024
146fbc9
Merge branch 'main' into perf/filter-money-request-participants
TMisiukiewicz May 6, 2024
4bb2ece
fix tests
TMisiukiewicz May 6, 2024
42e2aa9
update to max recent reports to show
TMisiukiewicz May 6, 2024
9504d74
fix not displaying workspaces in recents
TMisiukiewicz May 6, 2024
d894a22
Merge remote-tracking branch 'upstream/main' into perf/filter-money-r…
TMisiukiewicz May 10, 2024
07e7d50
Merge remote-tracking branch 'upstream/main' into perf/filter-money-r…
TMisiukiewicz May 13, 2024
ec4c747
Merge remote-tracking branch 'upstream/main' into perf/filter-money-r…
TMisiukiewicz May 13, 2024
d421d17
Merge branch 'main' into perf/filter-money-request-participants
TMisiukiewicz May 14, 2024
60c7149
Merge branch 'main' into perf/filter-money-request-participants
TMisiukiewicz May 17, 2024
d246f11
Merge branch 'main' into perf/filter-money-request-participants
rinej Jun 11, 2024
ad4a170
resolve conficts
rinej Jun 11, 2024
fb19c55
fix: fix typescript
rinej Jun 11, 2024
7716743
fix lint
rinej Jun 11, 2024
1d4cabc
adjust default return, fix tests
rinej Jun 11, 2024
c55c40f
fix: fix recent search logic
rinej Jun 17, 2024
cb671ac
Merge branch 'main' into perf/filter-money-request-participants
rinej Jun 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 78 additions & 34 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ type Options = {

type PreviewConfig = {showChatPreviewLine?: boolean; forcePolicyNamePreview?: boolean; showPersonalDetails?: boolean};

type FilterOptionsConfig = Pick<GetOptionsConfig, 'sortByReportTypeInSearch' | 'canInviteUser' | 'betas' | 'selectedOptions' | 'excludeUnknownUsers' | 'excludeLogins'>;

/**
* OptionsListUtils is used to build a list options passed to the OptionsList component. Several different UI views can
* be configured to display different results based on the options passed to the private getOptions() method. Public
Expand Down Expand Up @@ -1542,6 +1544,42 @@ function orderOptions(options: ReportUtils.OptionData[], searchValue: string | u
);
}

/**
* Builds the option with optimistic personal details
*/
function createOptimisticPersonalDetailOption(searchValue: string, {reportActions = {}, showChatPreviewLine = false}) {
const optimisticAccountID = UserUtils.generateAccountID(searchValue);
const personalDetailsExtended = {
...allPersonalDetails,
[optimisticAccountID]: {
accountID: optimisticAccountID,
login: searchValue,
avatar: UserUtils.getDefaultAvatar(optimisticAccountID),
},
};
const optimisticUser = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, {
showChatPreviewLine,
});

optimisticUser.isOptimisticAccount = true;
optimisticUser.login = searchValue;
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
optimisticUser.text = optimisticUser.text || searchValue;
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
optimisticUser.alternateText = optimisticUser.alternateText || searchValue;

// If user doesn't exist, use a default avatar
optimisticUser.icons = [
{
source: UserUtils.getAvatar('', optimisticAccountID),
name: searchValue,
type: CONST.ICON_TYPE_AVATAR,
},
];

return optimisticUser;
}

/**
* filter options based on specific conditions
*/
Expand Down Expand Up @@ -1840,6 +1878,7 @@ function getOptions(
currentUserOption = undefined;
}

// TODO: creating user to invite can be removed once we implement filtering in all search pages. This logic will be handled in filtering instead.
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
let userToInvite: ReportUtils.OptionData | null = null;
const noOptions = recentReportOptions.length + personalDetailsOptions.length === 0 && !currentUserOption;
const noOptionsMatchExactly = !personalDetailsOptions
Expand All @@ -1858,33 +1897,7 @@ function getOptions(
!excludeUnknownUsers
) {
// Generates an optimistic account ID for new users not yet saved in Onyx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB, this comment can be moved inside the createOptimisticPersonalDetailOption where we are generating optimistic account ID or we can remove this comment.

const optimisticAccountID = UserUtils.generateAccountID(searchValue);
const personalDetailsExtended = {
...allPersonalDetails,
[optimisticAccountID]: {
accountID: optimisticAccountID,
login: searchValue,
avatar: UserUtils.getDefaultAvatar(optimisticAccountID),
},
};
userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, {
showChatPreviewLine,
});
userToInvite.isOptimisticAccount = true;
userToInvite.login = searchValue;
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
userToInvite.text = userToInvite.text || searchValue;
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
userToInvite.alternateText = userToInvite.alternateText || searchValue;

// If user doesn't exist, use a default avatar
userToInvite.icons = [
{
source: UserUtils.getAvatar('', optimisticAccountID),
name: searchValue,
type: CONST.ICON_TYPE_AVATAR,
},
];
userToInvite = createOptimisticPersonalDetailOption(searchValue, {reportActions, showChatPreviewLine});
}

// If we are prioritizing 1:1 chats in search, do it only once we started searching
Expand Down Expand Up @@ -2248,8 +2261,10 @@ function getFirstKeyForList(data?: Option[] | null) {
/**
* Filters options based on the search input value
*/
function filterOptions(options: Options, searchInputValue: string): Options {
const searchValue = getSearchValueForPhoneOrEmail(searchInputValue);
function filterOptions(options: Options, searchInputValue: string, config?: FilterOptionsConfig): Options {
const {sortByReportTypeInSearch = false, canInviteUser = true, betas = [], selectedOptions = [], excludeUnknownUsers = false, excludeLogins = []} = config ?? {};
const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchInputValue)));
const searchValue = parsedPhoneNumber.possible ? parsedPhoneNumber.number?.e164 ?? '' : searchInputValue.toLowerCase();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const searchValue = parsedPhoneNumber.possible ? parsedPhoneNumber.number?.e164 ?? '' : searchInputValue.toLowerCase();
const searchValue = (parsedPhoneNumber.possible && parsedPhoneNumber.number?.e164) ? parsedPhoneNumber.number.e164 : searchInputValue.toLowerCase();

Don't we need to use searchInputValue when e164 is not available?

const searchTerms = searchValue ? searchValue.split(' ') : [];

// The regex below is used to remove dots only from the local part of the user email (local-part@domain)
Expand Down Expand Up @@ -2292,12 +2307,14 @@ function filterOptions(options: Options, searchInputValue: string): Options {
if (item.alternateText) {
values.push(item.alternateText);
}
values = values.concat(getParticipantsLoginsArray(item));
} else if (!!item.isChatRoom || !!item.isPolicyExpenseChat) {
if (item.subtitle) {
values.push(item.subtitle);
}
} else {
values = values.concat(getParticipantsLoginsArray(item));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we move this to the last else block, with that for the policy expense chat it does not consider the participants but only subtitle for the filter values. Due to that policy expense chats are missing in the Recent section while filtering.

Screen.Recording.2024-04-17.at.23.16.23.mov

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recently this PR caused a big performance regression: #38887 and it was reverted in #40019. Since the filtering was merged somewhere close before reverting, I adjusted function to match the correct behavior. Seems like it works the same way on production

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, Ok. On prod I am not seeing those policy expense chat for the global search but it is shown for the request/send money participants filtration. I think that reverted PR tried manipulating the getSearchText but here for request money participants search I don't think we are making a call to getSearchText.

Screen.Recording.2024-04-18.at.23.40.09.mov

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the reverted PR was adding all the participants of the chat to the searchText, so it became super inefficient, as some of the chats had thousands of participants.
getSearchText is called when options are created (first opening of any search page - then they are cached), then, when searching, we are not calling it anymore because we are not recreating the options. Once getSearchText is removed, the initial load of the list will speed up as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That reverted PR is in production, doubt why it still shows the policy expense chats in the search filter for request/send money flow.

Screenshot 2024-04-22 at 01 00 19

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I re-reviewed the isSearchStringMatch function, responsible for searching a match in a string generated by getSearchText and found that for non-chatrooms, it also looks for matches in a participants list. Updated the filterOptions to match this behavior so now it should also display e.g. workspaces when you search by user name. Thanks for spotting this! 👍

}
values = values.concat(getParticipantsLoginsArray(item));

return uniqFast(values);
});
Expand All @@ -2316,12 +2333,39 @@ function filterOptions(options: Options, searchInputValue: string): Options {
};
}, options);

const recentReports = matchResults.recentReports.concat(matchResults.personalDetails);
let {recentReports, personalDetails} = matchResults;

if (sortByReportTypeInSearch) {
recentReports = recentReports.concat(matchResults.personalDetails);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
recentReports = recentReports.concat(matchResults.personalDetails);
recentReports = recentReports.concat(personalDetails);

Let's use the destructured prop

personalDetails = [];
recentReports = orderOptions(recentReports, searchValue);
}

let userToInvite = null;
if (canInviteUser) {
const noOptions = recentReports.length + personalDetails.length === 0;
const noOptionsMatchExactly = !personalDetails
.concat(recentReports)
.find((option) => option.login === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue ?? '').toLowerCase() || option.login === searchValue?.toLowerCase());
if (
searchValue &&
(noOptions || noOptionsMatchExactly) &&
!isCurrentUser({login: searchValue} as PersonalDetails) &&
selectedOptions.every((option) => 'login' in option && option.login !== searchValue) &&
((Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue) && !Str.endsWith(searchValue, CONST.SMS.DOMAIN)) ||
(parsedPhoneNumber.possible && Str.isValidE164Phone(LoginUtils.getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')))) &&
!excludeLogins.find((optionToExclude) => optionToExclude === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue).toLowerCase()) &&
(searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas)) &&
!excludeUnknownUsers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about creating a function for these if conditions part as the same set of conditions is used twice in the file

) {
userToInvite = createOptimisticPersonalDetailOption(searchValue, {});
}
}

return {
personalDetails: [],
recentReports: orderOptions(recentReports, searchValue),
userToInvite: null,
personalDetails,
recentReports,
userToInvite,
currentUserOption: null,
categoryOptions: [],
tagOptions: [],
Expand Down
6 changes: 3 additions & 3 deletions src/pages/ChatFinderPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@ function ChatFinderPage({betas, isSearchingForReports, navigation}: ChatFinderPa
};
}

const newOptions = OptionsListUtils.filterOptions(searchOptions, debouncedSearchValue);
const header = OptionsListUtils.getHeaderMessage(newOptions.recentReports.length > 0, false, debouncedSearchValue);
const newOptions = OptionsListUtils.filterOptions(searchOptions, debouncedSearchValue, {sortByReportTypeInSearch: true});
const header = OptionsListUtils.getHeaderMessage(newOptions.recentReports.length > 0, Boolean(newOptions.userToInvite), debouncedSearchValue);
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
return {
recentReports: newOptions.recentReports,
personalDetails: newOptions.personalDetails,
userToInvite: null,
userToInvite: newOptions.userToInvite,
headerMessage: header,
};
}, [debouncedSearchValue, searchOptions]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,16 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
Report.searchInServer(debouncedSearchTerm.trim());
}, [debouncedSearchTerm]);

/**
* Returns the sections needed for the OptionsSelector
*
* @returns {Array}
*/
const [sections, newChatOptions] = useMemo(() => {
const newSections = [];
const chatOptions = useMemo(() => {
if (!areOptionsInitialized || !didScreenTransitionEnd) {
return [newSections, {}];
return {};
}
const chatOptions = OptionsListUtils.getFilteredOptions(

const optionList = OptionsListUtils.getFilteredOptions(
options.reports,
options.personalDetails,
betas,
debouncedSearchTerm,
'',
participants,
CONST.EXPENSIFY_EMAILS,

Expand All @@ -122,11 +117,38 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
false,
);

return optionList;
}, [action, areOptionsInitialized, betas, canUseP2PDistanceRequests, didScreenTransitionEnd, iouRequestType, iouType, options.personalDetails, options.reports, participants]);

const filteredOptions = useMemo(() => {
if (!areOptionsInitialized || debouncedSearchTerm.trim() === '') {
return {};
}

const newOptions = OptionsListUtils.filterOptions(chatOptions, debouncedSearchTerm, {
betas,
selectedOptions: participants,
excludeLogins: CONST.EXPENSIFY_EMAILS,
});
return newOptions;
}, [areOptionsInitialized, betas, chatOptions, debouncedSearchTerm, participants]);

/**
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
* Returns the sections needed for the OptionsSelector
* @returns {Array}
*/
const [sections, header] = useMemo(() => {
const requestMoneyOptions = debouncedSearchTerm.trim() !== '' ? filteredOptions : chatOptions;
const newSections = [];
if (!areOptionsInitialized || !didScreenTransitionEnd) {
return [newSections, ''];
}

const formatResults = OptionsListUtils.formatSectionsFromSearchTerm(
debouncedSearchTerm,
participants,
chatOptions.recentReports,
chatOptions.personalDetails,
requestMoneyOptions.recentReports,
requestMoneyOptions.personalDetails,
maxParticipantsReached,
personalDetails,
true,
Expand All @@ -140,45 +162,50 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF

newSections.push({
title: translate('common.recents'),
data: chatOptions.recentReports,
shouldShow: !_.isEmpty(chatOptions.recentReports),
data: requestMoneyOptions.recentReports,
shouldShow: !_.isEmpty(options.recentReports),
});

if (![CONST.IOU.ACTION.CATEGORIZE, CONST.IOU.ACTION.SHARE].includes(action)) {
newSections.push({
title: translate('common.contacts'),
data: chatOptions.personalDetails,
data: requestMoneyOptions.personalDetails,
shouldShow: !_.isEmpty(chatOptions.personalDetails),
});
}

if (chatOptions.userToInvite && !OptionsListUtils.isCurrentUser(chatOptions.userToInvite)) {
if (requestMoneyOptions.userToInvite && !OptionsListUtils.isCurrentUser(requestMoneyOptions.userToInvite)) {
newSections.push({
title: undefined,
data: _.map([chatOptions.userToInvite], (participant) => {
data: _.map([requestMoneyOptions.userToInvite], (participant) => {
const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false);
return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, personalDetails);
}),
shouldShow: true,
});
}

return [newSections, chatOptions];
const headerMessage = OptionsListUtils.getHeaderMessage(
_.get(requestMoneyOptions, 'personalDetails', []).length + _.get(requestMoneyOptions, 'recentReports', []).length !== 0,
Boolean(requestMoneyOptions.userToInvite),
debouncedSearchTerm.trim(),
maxParticipantsReached,
_.some(participants, (participant) => participant.searchText.toLowerCase().includes(debouncedSearchTerm.trim().toLowerCase())),
);

return [newSections, headerMessage];
}, [
areOptionsInitialized,
options.reports,
options.personalDetails,
betas,
debouncedSearchTerm,
filteredOptions,
chatOptions,
areOptionsInitialized,
didScreenTransitionEnd,
participants,
iouType,
action,
canUseP2PDistanceRequests,
iouRequestType,
maxParticipantsReached,
personalDetails,
translate,
didScreenTransitionEnd,
options.recentReports,
]);

/**
Expand Down Expand Up @@ -244,19 +271,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
[participants, onParticipantsAdded],
);

const headerMessage = useMemo(
() =>
OptionsListUtils.getHeaderMessage(
_.get(newChatOptions, 'personalDetails', []).length + _.get(newChatOptions, 'recentReports', []).length !== 0,
Boolean(newChatOptions.userToInvite),
debouncedSearchTerm.trim(),
maxParticipantsReached,
_.some(participants, (participant) => participant.searchText.toLowerCase().includes(debouncedSearchTerm.trim().toLowerCase())),
),
[maxParticipantsReached, newChatOptions, participants, debouncedSearchTerm],
);

// Right now you can't split an expense with a workspace and other additional participants
// Right now you can't split a request with a workspace and other additional participants
// This is getting properly fixed in https://github.com/Expensify/App/issues/27508, but as a stop-gap to prevent
// the app from crashing on native when you try to do this, we'll going to hide the button if you have a workspace and other participants
const hasPolicyExpenseChatParticipant = _.some(participants, (participant) => participant.isPolicyExpenseChat);
Expand Down Expand Up @@ -394,7 +409,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
onSelectRow={addSingleParticipant}
footerContent={footerContent}
headerMessage={headerMessage}
headerMessage={header}
showLoadingPlaceholder={!areOptionsInitialized || !didScreenTransitionEnd}
rightHandSideComponent={itemRightSideComponent}
/>
Expand Down
9 changes: 4 additions & 5 deletions tests/unit/OptionsListUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2610,20 +2610,19 @@ describe('OptionsListUtils', () => {
const options = OptionsListUtils.getSearchOptions(OPTIONS, '', [CONST.BETAS.ALL]);
const filteredOptions = OptionsListUtils.filterOptions(options, '');

expect(options.recentReports.length + options.personalDetails.length).toBe(filteredOptions.recentReports.length);
expect(options.recentReports.length + options.personalDetails.length).toBe(filteredOptions.recentReports.length + filteredOptions.personalDetails.length);
});

it('should return filtered options in correct order', () => {
const searchText = 'man';
const options = OptionsListUtils.getSearchOptions(OPTIONS, '', [CONST.BETAS.ALL]);

const filteredOptions = OptionsListUtils.filterOptions(options, searchText);
expect(filteredOptions.recentReports.length).toBe(5);
const filteredOptions = OptionsListUtils.filterOptions(options, searchText, {sortByReportTypeInSearch: true});
expect(filteredOptions.recentReports.length).toBe(4);
expect(filteredOptions.recentReports[0].text).toBe('Invisible Woman');
expect(filteredOptions.recentReports[1].text).toBe('Spider-Man');
expect(filteredOptions.recentReports[2].text).toBe('Black Widow');
expect(filteredOptions.recentReports[3].text).toBe('Mister Fantastic');
expect(filteredOptions.recentReports[4].text).toBe("SHIELD's workspace (archived)");
});

it('should filter users by email', () => {
Expand All @@ -2650,7 +2649,7 @@ describe('OptionsListUtils', () => {
const OPTIONS_WITH_PERIODS = OptionsListUtils.createOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS);
const options = OptionsListUtils.getSearchOptions(OPTIONS_WITH_PERIODS, '', [CONST.BETAS.ALL]);

const filteredOptions = OptionsListUtils.filterOptions(options, searchText);
const filteredOptions = OptionsListUtils.filterOptions(options, searchText, {sortByReportTypeInSearch: true});

expect(filteredOptions.recentReports.length).toBe(1);
expect(filteredOptions.recentReports[0].login).toBe('[email protected]');
Expand Down
Loading