Skip to content
Open
4 changes: 2 additions & 2 deletions src/components/Search/SearchAutocompleteList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -374,11 +374,11 @@ function SearchAutocompleteList({

const reportOptions: OptionData[] = [...orderedOptions.recentReports, ...orderedOptions.personalDetails];
if (searchOptions.userToInvite) {
reportOptions.push(searchOptions.userToInvite);
reportOptions.push({...searchOptions.userToInvite, alternateText: translate('common.invite')});
}

return reportOptions.slice(0, 20);
}, [autocompleteQueryValue, searchOptions]);
}, [autocompleteQueryValue, searchOptions, translate]);

const debounceHandleSearch = useDebounce(() => {
if (!handleSearch || !autocompleteQueryWithoutFilters) {
Expand Down
11 changes: 9 additions & 2 deletions src/libs/OptionsListUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1911,13 +1911,20 @@ function canCreateOptimisticPersonalDetailOption({
currentUserOption?: SearchOptionData | null;
searchValue: string;
}) {
if (recentReportOptions.length + personalDetailsOptions.length > 0) {
const normalizedSearchValue = addSMSDomainIfPhoneNumber(searchValue ?? '').toLowerCase();
const rawSearchValue = (searchValue ?? '').toLowerCase();
const matchesLogin = (login: string | undefined) => {
const normalizedLogin = login?.toLowerCase();
return normalizedLogin === normalizedSearchValue || normalizedLogin === rawSearchValue;
};
const hasExactLoginMatch = recentReportOptions.some((o) => matchesLogin(o.login)) || personalDetailsOptions.some((o) => matchesLogin(o.login));
if (hasExactLoginMatch) {
return false;
}
if (!currentUserOption) {
return true;
}
return currentUserOption.login !== addSMSDomainIfPhoneNumber(searchValue ?? '').toLowerCase() && currentUserOption.login !== searchValue?.toLowerCase();
return currentUserOption.login !== normalizedSearchValue && currentUserOption.login !== rawSearchValue;
}

/**
Expand Down
60 changes: 59 additions & 1 deletion tests/ui/components/SearchAutocompleteListTest.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {act, render} from '@testing-library/react-native';
import {act, render, screen} from '@testing-library/react-native';
import React from 'react';
import Onyx from 'react-native-onyx';
import {LocaleContextProvider} from '@components/LocaleContextProvider';
Expand Down Expand Up @@ -128,6 +128,64 @@ describe('SearchAutocompleteList', () => {
expect(mockHtmlToText).not.toHaveBeenCalled();
});

it('should set alternateText to "Invite" on the userToInvite row when autocompleteQueryValue is non-empty', async () => {
// Regression test for #88730: when userToInvite is present and the query is non-empty,
// recentReportsOptions spreads it with alternateText: translate('common.invite').
type OptionsListUtilsMock = {getSearchOptions: jest.Mock; combineOrderingOfReportsAndPersonalDetails: jest.Mock};
const OptionsListUtils: OptionsListUtilsMock = jest.requireMock('@libs/OptionsListUtils');

const inviteOption = {
reportID: undefined,
keyForList: 'unknown@example.com',
login: 'unknown@example.com',
text: 'unknown@example.com',
alternateText: '',
};

OptionsListUtils.getSearchOptions.mockImplementation(() => ({
recentReports: [],
personalDetails: [],
currentUserOption: null,
userToInvite: inviteOption,
categoryOptions: [],
}));
OptionsListUtils.combineOrderingOfReportsAndPersonalDetails.mockImplementation(() => ({recentReports: [], personalDetails: []}));

render(
<OnyxListItemProvider>
<LocaleContextProvider>
<SearchAutocompleteList
autocompleteQueryValue="unknown@example.com"
handleSearch={jest.fn()}
onListItemPress={jest.fn()}
/>
</LocaleContextProvider>
</OnyxListItemProvider>,
);

await waitForBatchedUpdatesWithAct();

expect(screen.getByText('Invite')).toBeTruthy();

// Restore default mock so other tests are not affected
OptionsListUtils.getSearchOptions.mockImplementation(() => ({
recentReports: [
{
reportID: '10',
keyForList: '10',
text: 'Test Report',
alternateText: 'alternate text',
lastMessageText: 'last message',
},
],
personalDetails: [],
currentUserOption: null,
userToInvite: null,
categoryOptions: [],
}));
OptionsListUtils.combineOrderingOfReportsAndPersonalDetails.mockImplementation(() => ({recentReports: [], personalDetails: []}));
});

it('should call Parser.htmlToText when parentReportAction is not ADD_COMMENT', async () => {
const reportID = '10';
const parentReportID = '20';
Expand Down
Loading
Loading