mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2026-10-07 02:22:38 -04:00
MOB-1339: respect scientific name preference
This commit is contained in:
1 parent
17509ba8f5
commit
cf54517a0b
4 files changed
+139
-13
No files matched your search
@@ -46,7 +46,7 @@ export const resultToSubject = ( result: UniversalSearchResultItem ): ExploreV2S
|
||||
|
||||
export const subjectToText = (
|
||||
subject: ExploreV2Subject,
|
||||
prefersCommonNames: boolean,
|
||||
commonNameIsPrimary: boolean,
|
||||
): string => {
|
||||
switch ( subject.type ) {
|
||||
case "user":
|
||||
@@ -55,7 +55,7 @@ export const subjectToText = (
|
||||
return subject.project.title;
|
||||
case "taxon":
|
||||
default:
|
||||
return ( prefersCommonNames && subject.taxon.preferred_common_name )
|
||||
return ( commonNameIsPrimary && subject.taxon.preferred_common_name )
|
||||
? generateTaxonPieces( subject.taxon ).commonName ?? subject.taxon.name
|
||||
: subject.taxon.name;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,8 @@ const UniversalSearch = ( ) => {
|
||||
const { t } = useTranslation( );
|
||||
const { dispatch } = useExploreV2( );
|
||||
const currentUser = useCurrentUser( );
|
||||
const prefersCommonNames = currentUser?.prefers_common_names !== false;
|
||||
const commonNameIsPrimary = currentUser?.prefers_common_names !== false
|
||||
&& currentUser?.prefers_scientific_name_first !== true;
|
||||
|
||||
const [taxonText, setTaxonText] = useState( "" );
|
||||
const [locationText, setLocationText] = useState( "" );
|
||||
@@ -80,11 +81,11 @@ const UniversalSearch = ( ) => {
|
||||
}, [debounceQuery] );
|
||||
|
||||
const handleSelect = useCallback( ( subject: ExploreV2Subject ) => {
|
||||
setTaxonText( subjectToText( subject, prefersCommonNames ) );
|
||||
setTaxonText( subjectToText( subject, commonNameIsPrimary ) );
|
||||
setQueryImmediately( "" );
|
||||
dispatch( { type: EXPLORE_V2_ACTION.SET_SUBJECT, subject } );
|
||||
locationInputRef.current?.focus( );
|
||||
}, [dispatch, prefersCommonNames, setQueryImmediately] );
|
||||
}, [commonNameIsPrimary, dispatch, setQueryImmediately] );
|
||||
|
||||
const handleReset = useCallback( ( ) => {
|
||||
setTaxonText( "" );
|
||||
|
||||
@@ -17,7 +17,6 @@ export type UniversalSearchResultItem =
|
||||
|
||||
const useUniversalSearch = ( query: string ) => {
|
||||
const trimmedQuery = query.trim( );
|
||||
// TODO look into some sort of throttle here
|
||||
const shouldFetch = trimmedQuery.length > 0;
|
||||
|
||||
const {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fireEvent, screen } from "@testing-library/react-native";
|
||||
import { act, fireEvent, screen } from "@testing-library/react-native";
|
||||
import UniversalSearch from "components/Explore/ExploreV2/screens/UniversalSearch";
|
||||
import initI18next from "i18n/initI18next";
|
||||
import i18next from "i18next";
|
||||
@@ -18,10 +18,92 @@ jest.mock( "@react-navigation/native", ( ) => {
|
||||
};
|
||||
} );
|
||||
|
||||
const mockDispatch = jest.fn( );
|
||||
jest.mock( "providers/ExploreV2Context", ( ) => {
|
||||
const actual = jest.requireActual( "providers/ExploreV2Context" );
|
||||
return {
|
||||
...actual,
|
||||
useExploreV2: jest.fn( ),
|
||||
};
|
||||
} );
|
||||
const { useExploreV2 } = require( "providers/ExploreV2Context" );
|
||||
|
||||
jest.mock( "sharedHooks/useUniversalSearch" );
|
||||
const useUniversalSearch = require( "sharedHooks/useUniversalSearch" ).default;
|
||||
|
||||
jest.mock( "sharedHooks/useCurrentUser", ( ) => ( {
|
||||
__esModule: true,
|
||||
default: jest.fn( ),
|
||||
} ) );
|
||||
const useCurrentUser = require( "sharedHooks/useCurrentUser" ).default;
|
||||
|
||||
const CURRENT_USER = {
|
||||
id: 99,
|
||||
login: "tester",
|
||||
icon_url: null,
|
||||
observations_count: 42,
|
||||
prefers_common_names: true,
|
||||
prefers_scientific_name_first: false,
|
||||
};
|
||||
|
||||
const MIXED_RESULTS = [
|
||||
{
|
||||
type: "user",
|
||||
user: {
|
||||
id: 7,
|
||||
login: "carrieseltzer",
|
||||
icon_url: "https://example.com/u.jpg",
|
||||
observations_count: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "taxon",
|
||||
taxon: {
|
||||
id: 12,
|
||||
name: "Eumyias thalassinus",
|
||||
preferred_common_name: "Verditer Flycatcher",
|
||||
iconic_taxon_name: "Aves",
|
||||
default_photo: { url: "https://example.com/t.jpg" },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "project",
|
||||
project: {
|
||||
id: 9,
|
||||
title: "InverteFest",
|
||||
project_type: "collection",
|
||||
rule_preferences: [],
|
||||
icon: "https://example.com/p.jpg",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const typeQuery = text => {
|
||||
fireEvent.changeText( screen.getByTestId( "UniversalSearch.taxonInput" ), text );
|
||||
// Only the timer advancement needs an act wrapper; fireEvent is already
|
||||
// wrapped internally by Testing Library.
|
||||
act( ( ) => {
|
||||
jest.advanceTimersByTime( 400 );
|
||||
} );
|
||||
};
|
||||
|
||||
beforeAll( async ( ) => {
|
||||
await initI18next( );
|
||||
} );
|
||||
|
||||
beforeEach( ( ) => {
|
||||
jest.useFakeTimers( );
|
||||
mockNavigate.mockClear( );
|
||||
mockDispatch.mockClear( );
|
||||
useExploreV2.mockReturnValue( { dispatch: mockDispatch, state: {} } );
|
||||
useCurrentUser.mockReturnValue( CURRENT_USER );
|
||||
useUniversalSearch.mockReturnValue( { results: [], isLoading: false, refetch: jest.fn( ) } );
|
||||
} );
|
||||
|
||||
afterEach( ( ) => {
|
||||
jest.useRealTimers( );
|
||||
} );
|
||||
|
||||
describe( "UniversalSearch screen", ( ) => {
|
||||
it( "renders the core layout", ( ) => {
|
||||
renderComponent( <UniversalSearch /> );
|
||||
@@ -53,11 +135,55 @@ describe( "UniversalSearch screen", ( ) => {
|
||||
|
||||
expect( screen.queryByDisplayValue( "cup plant" ) ).toBeNull( );
|
||||
expect( screen.queryByDisplayValue( "California" ) ).toBeNull( );
|
||||
expect(
|
||||
screen.getByPlaceholderText( i18next.t( "Search-for-species-user-or-project" ) ),
|
||||
).toBeTruthy( );
|
||||
expect(
|
||||
screen.getByPlaceholderText( i18next.t( "Search-for-a-location" ) ),
|
||||
).toBeTruthy( );
|
||||
} );
|
||||
|
||||
it( "shows mixed autocomplete results while typing", ( ) => {
|
||||
useUniversalSearch.mockReturnValue( {
|
||||
results: MIXED_RESULTS,
|
||||
isLoading: false,
|
||||
refetch: jest.fn( ),
|
||||
} );
|
||||
renderComponent( <UniversalSearch /> );
|
||||
|
||||
typeQuery( "ver" );
|
||||
|
||||
expect( screen.getByText( "carrieseltzer" ) ).toBeTruthy( );
|
||||
expect( screen.getByText( "InverteFest" ) ).toBeTruthy( );
|
||||
expect( screen.getByTestId( "UniversalSearchResult.taxon.12" ) ).toBeTruthy( );
|
||||
// each row has a (no-op) info icon
|
||||
expect( screen.getByTestId( "UniversalSearchResult.info.7" ) ).toBeTruthy( );
|
||||
} );
|
||||
|
||||
it( "fills the field and sets the subject when a result is tapped", ( ) => {
|
||||
useUniversalSearch.mockReturnValue( {
|
||||
results: MIXED_RESULTS,
|
||||
isLoading: false,
|
||||
refetch: jest.fn( ),
|
||||
} );
|
||||
renderComponent( <UniversalSearch /> );
|
||||
|
||||
typeQuery( "ver" );
|
||||
|
||||
fireEvent.press( screen.getByTestId( "UniversalSearchResult.user.7" ) );
|
||||
|
||||
expect( mockDispatch ).toHaveBeenCalledWith(
|
||||
expect.objectContaining( {
|
||||
type: "SET_SUBJECT",
|
||||
subject: expect.objectContaining( {
|
||||
type: "user",
|
||||
user: expect.objectContaining( { id: 7, login: "carrieseltzer" } ),
|
||||
} ),
|
||||
} ),
|
||||
);
|
||||
// the search field is filled with the selected suggestion
|
||||
expect( screen.getByDisplayValue( "carrieseltzer" ) ).toBeTruthy( );
|
||||
} );
|
||||
|
||||
it( "navigates to Advanced Search", ( ) => {
|
||||
renderComponent( <UniversalSearch /> );
|
||||
|
||||
fireEvent.press( screen.getByText( i18next.t( "Advanced-Search" ) ) );
|
||||
|
||||
expect( mockNavigate ).toHaveBeenCalledWith( "AdvancedSearch" );
|
||||
} );
|
||||
} );
|
||||
Reference in new issue
Block a user