mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2026-01-04 11:58:08 -05:00
327 lines
13 KiB
JavaScript
327 lines
13 KiB
JavaScript
import {
|
|
act,
|
|
screen,
|
|
userEvent,
|
|
waitFor,
|
|
within
|
|
} from "@testing-library/react-native";
|
|
import inatjs from "inaturalistjs";
|
|
import Identification from "realmModels/Identification";
|
|
import useStore from "stores/useStore";
|
|
import factory, { makeResponse } from "tests/factory";
|
|
import faker from "tests/helpers/faker";
|
|
import { renderAppWithObservations } from "tests/helpers/render";
|
|
import setStoreStateLayout from "tests/helpers/setStoreStateLayout";
|
|
import setupUniqueRealm from "tests/helpers/uniqueRealm";
|
|
import { signIn, signOut, TEST_JWT } from "tests/helpers/user";
|
|
|
|
// We're explicitly testing navigation here so we want react-navigation
|
|
// working normally
|
|
jest.unmock( "@react-navigation/native" );
|
|
|
|
// UNIQUE REALM SETUP
|
|
const mockRealmIdentifier = __filename;
|
|
const { mockRealmModelsIndex, uniqueRealmBeforeAll, uniqueRealmAfterAll } = setupUniqueRealm(
|
|
mockRealmIdentifier
|
|
);
|
|
jest.mock( "realmModels/index", ( ) => mockRealmModelsIndex );
|
|
jest.mock( "providers/contexts", ( ) => {
|
|
const originalModule = jest.requireActual( "providers/contexts" );
|
|
return {
|
|
__esModule: true,
|
|
...originalModule,
|
|
RealmContext: {
|
|
...originalModule.RealmContext,
|
|
useRealm: ( ) => global.mockRealms[mockRealmIdentifier],
|
|
useQuery: ( ) => []
|
|
}
|
|
};
|
|
} );
|
|
beforeAll( uniqueRealmBeforeAll );
|
|
afterAll( uniqueRealmAfterAll );
|
|
// /UNIQUE REALM SETUP
|
|
|
|
const initialStoreState = useStore.getState( );
|
|
beforeAll( async ( ) => {
|
|
useStore.setState( initialStoreState, true );
|
|
setStoreStateLayout( {
|
|
isDefaultMode: false
|
|
} );
|
|
// userEvent recommends fake timers
|
|
jest.useFakeTimers( );
|
|
} );
|
|
|
|
const mockUser = factory( "LocalUser" );
|
|
|
|
const makeMockObservations = ( ) => ( [
|
|
factory( "RemoteObservation", {
|
|
_synced_at: faker.date.past( ),
|
|
needsSync: jest.fn( ( ) => false ),
|
|
wasSynced: jest.fn( ( ) => true ),
|
|
// Suggestions won't load without a photo
|
|
observationPhotos: [
|
|
factory( "RemoteObservationPhoto" ),
|
|
factory( "RemoteObservationPhoto" )
|
|
],
|
|
user: mockUser,
|
|
observed_on_string: "2020-01-01"
|
|
} )
|
|
] );
|
|
|
|
// Mock the response from inatjs.computervision.score_image
|
|
const topSuggestion = {
|
|
taxon: factory.states( "genus" )( "RemoteTaxon", { name: "Primum" } ),
|
|
combined_score: 90
|
|
};
|
|
const otherSuggestion = {
|
|
taxon: factory( "RemoteTaxon", { name: "Alia suggestione" } ),
|
|
combined_score: 50
|
|
};
|
|
|
|
beforeEach( async ( ) => {
|
|
const mockScoreImageResponse = makeResponse( [topSuggestion, otherSuggestion] );
|
|
inatjs.computervision.score_image.mockResolvedValue( mockScoreImageResponse );
|
|
inatjs.observations.observers.mockResolvedValue( makeResponse( ) );
|
|
inatjs.taxa.fetch.mockResolvedValue( makeResponse( [topSuggestion.taxon] ) );
|
|
inatjs.observations.viewedUpdates.mockResolvedValue( makeResponse( ) );
|
|
inatjs.identifications.create.mockResolvedValue( {
|
|
results: [factory( "RemoteIdentification", {
|
|
taxon: topSuggestion.taxon,
|
|
user: mockUser
|
|
} )]
|
|
} );
|
|
await signIn( mockUser, { realm: global.mockRealms[__filename] } );
|
|
} );
|
|
|
|
afterEach( ( ) => {
|
|
inatjs.computervision.score_image.mockReset( );
|
|
inatjs.observations.observers.mockReset( );
|
|
inatjs.taxa.fetch.mockReset( );
|
|
inatjs.observations.viewedUpdates.mockReset( );
|
|
inatjs.identifications.create.mockReset( );
|
|
signOut( { realm: global.mockRealms[__filename] } );
|
|
} );
|
|
|
|
// TODO: Fix failing TaxonSearch tests: Unable to find node on an unmounted component error
|
|
|
|
// describe( "TaxonSearch", ( ) => {
|
|
// const mockSearchResultTaxon = factory( "RemoteTaxon" );
|
|
|
|
// beforeEach( ( ) => {
|
|
// inatjs.taxa.search.mockResolvedValue( makeResponse( [
|
|
// mockSearchResultTaxon
|
|
// ] ) );
|
|
// inatjs.observations.observers.mockResolvedValue( makeResponse( [
|
|
// {
|
|
// observation_count: faker.number.int( ),
|
|
// species_count: faker.number.int( ),
|
|
// user: factory( "RemoteUser" )
|
|
// }
|
|
// ] ) );
|
|
// inatjs.taxa.fetch.mockResolvedValue( makeResponse( [] ) );
|
|
// } );
|
|
|
|
// afterEach( ( ) => {
|
|
// inatjs.taxa.search.mockReset( );
|
|
// inatjs.observations.observers.mockReset( );
|
|
// inatjs.taxa.fetch.mockReset( );
|
|
// } );
|
|
|
|
// const actor = userEvent.setup( );
|
|
|
|
// // We need to navigate from MyObs to ObsDetails to Suggestions to TaxonSearch for all of these
|
|
// // tests
|
|
// async function navigateToTaxonSearchForObservation( observation ) {
|
|
// const observationGridItem = await screen.findByTestId(
|
|
// `MyObservations.obsGridItem.${observation.uuid}`
|
|
// );
|
|
// await actor.press( observationGridItem );
|
|
// const suggestIdButton = await screen.findByText( "SUGGEST ID" );
|
|
// await act( async ( ) => actor.press( suggestIdButton ) );
|
|
// await screen.findByTestId(
|
|
// `SuggestionsList.taxa.${topSuggestion.taxon.id}.checkmark`
|
|
// );
|
|
// const searchButton = await screen.findByText( "SEARCH FOR A TAXON" );
|
|
// await actor.press( searchButton );
|
|
// }
|
|
|
|
// async function navigateToTaxonSearchForObservationViaObsEdit( observation ) {
|
|
// const observationGridItem = await screen.findByTestId(
|
|
// `MyObservations.obsGridItem.${observation.uuid}`
|
|
// );
|
|
// await actor.press( observationGridItem );
|
|
// const editButton = await screen.findByLabelText( "Edit" );
|
|
// await act( async ( ) => actor.press( editButton ) );
|
|
// const addIdButton = await screen.findByText( "ADD AN ID" );
|
|
// await actor.press( addIdButton );
|
|
// await screen.findByTestId(
|
|
// `SuggestionsList.taxa.${topSuggestion.taxon.id}.checkmark`
|
|
// );
|
|
// const searchButton = await screen.findByText( "SEARCH FOR A TAXON" );
|
|
// await actor.press( searchButton );
|
|
// }
|
|
|
|
// it(
|
|
// "should create an id with false vision attribute when reached from ObsDetails via"
|
|
// + " Suggestions and search result chosen",
|
|
// async ( ) => {
|
|
// const { observations } = await setupAppWithSignedInUser( );
|
|
// await navigateToTaxonSearchForObservation( observations[0] );
|
|
// const searchInput = await screen.findByLabelText( "Search for a taxon" );
|
|
// We used toBeVisible here but the update to RN0.77 broke this expectation
|
|
// expect( searchInput ).toBeOnTheScreen( );
|
|
// await act(
|
|
// async ( ) => actor.type(
|
|
// searchInput,
|
|
// "doesn't really matter since we're mocking the response"
|
|
// )
|
|
// );
|
|
// const taxonResultButton = await screen.findByTestId(
|
|
// `Search.taxa.${mockSearchResultTaxon.id}.checkmark`
|
|
// );
|
|
// expect( taxonResultButton ).toBeTruthy( );
|
|
// await actor.press( taxonResultButton );
|
|
// expect( await screen.findByText( "ACTIVITY" ) ).toBeTruthy( );
|
|
// expect( inatjs.identifications.create ).toHaveBeenCalledWith( {
|
|
// fields: Identification.ID_FIELDS,
|
|
// identification: {
|
|
// observation_id: observations[0].uuid,
|
|
// taxon_id: mockSearchResultTaxon.id,
|
|
// vision: false
|
|
// }
|
|
// }, {
|
|
// api_token: TEST_JWT
|
|
// } );
|
|
// }
|
|
// );
|
|
|
|
// it(
|
|
// "should update observation with false vision attribute when reached from ObsEdit"
|
|
// + " and search result chosen",
|
|
// async ( ) => {
|
|
// const { observations } = await setupAppWithSignedInUser( );
|
|
// await navigateToTaxonSearchForObservationViaObsEdit( observations[0] );
|
|
// const searchInput = await screen.findByLabelText( "Search for a taxon" );
|
|
// await act(
|
|
// async ( ) => actor.type(
|
|
// searchInput,
|
|
// "doesn't really matter since we're mocking the response"
|
|
// )
|
|
// );
|
|
// const taxonResultButton = await screen.findByTestId(
|
|
// `Search.taxa.${mockSearchResultTaxon.id}.checkmark`
|
|
// );
|
|
// expect( taxonResultButton ).toBeTruthy( );
|
|
// await actor.press( taxonResultButton );
|
|
// const saveChangesButton = await screen.findByText( "SAVE CHANGES" );
|
|
// expect( saveChangesButton ).toBeTruthy( );
|
|
// await actor.press( saveChangesButton );
|
|
// const savedObservation = global.mockRealms[__filename]
|
|
// .objectForPrimaryKey( "Observation", observations[0].uuid );
|
|
// expect( savedObservation ).toHaveProperty( "owners_identification_from_vision", false );
|
|
// }
|
|
// );
|
|
// } );
|
|
|
|
describe( "Suggestions", ( ) => {
|
|
const actor = userEvent.setup( );
|
|
|
|
// We need to navigate from MyObs to ObsDetails to Suggestions for all of these
|
|
// tests
|
|
const navigateToSuggestionsForObservation = async observation => {
|
|
const observationGridItem = await screen.findByTestId(
|
|
`MyObservations.obsGridItem.${observation.uuid}`
|
|
);
|
|
await actor.press( observationGridItem );
|
|
const suggestIdButton = await screen.findByText( "SUGGEST ID" );
|
|
await act( async ( ) => actor.press( suggestIdButton ) );
|
|
};
|
|
|
|
const navigateToSuggestionsForObservationViaObsEdit = async observation => {
|
|
const observationGridItem = await screen.findByTestId(
|
|
`MyObservations.obsGridItem.${observation.uuid}`
|
|
);
|
|
await actor.press( observationGridItem );
|
|
const editButton = await screen.findByLabelText( "Edit" );
|
|
await act( async ( ) => actor.press( editButton ) );
|
|
const addIdButton = await screen.findByText( "IDENTIFY" );
|
|
await actor.press( addIdButton );
|
|
};
|
|
|
|
const setupAppWithSignedInUser = async ( ) => {
|
|
const observations = makeMockObservations( );
|
|
useStore.setState( { observations } );
|
|
await renderAppWithObservations( observations, __filename );
|
|
return { observations };
|
|
};
|
|
|
|
it( "should create ident with vision=true via ObsDetails", async ( ) => {
|
|
const { observations } = await setupAppWithSignedInUser( );
|
|
await navigateToSuggestionsForObservation( observations[0] );
|
|
const taxonId = topSuggestion.taxon.id;
|
|
const topTaxonResultButton = await screen.findByTestId(
|
|
`SuggestionsList.taxa.${taxonId}.checkmark`
|
|
);
|
|
// We used toBeVisible here but the update to RN0.77 broke this expectation
|
|
expect( topTaxonResultButton ).toBeOnTheScreen( );
|
|
await actor.press( topTaxonResultButton );
|
|
const activityTabBtn = await screen.findByText( /ACTIVITY/ );
|
|
// We used toBeVisible here but the update to RN0.77 broke this expectation
|
|
expect( activityTabBtn ).toBeOnTheScreen( );
|
|
await actor.press( activityTabBtn );
|
|
const activityTab = await screen.findByTestId( "ActivityTab" );
|
|
// We used toBeVisible here but the update to RN0.77 broke this expectation
|
|
expect( activityTab ).toBeOnTheScreen( );
|
|
// open bottom sheet
|
|
const bottomSheetText = await screen.findByText(
|
|
/Would you like to suggest the following identification/
|
|
);
|
|
// We used toBeVisible here but the update to RN0.77 broke this expectation
|
|
expect( bottomSheetText ).toBeOnTheScreen( );
|
|
const confirmButton = await screen.findByTestId( "SuggestIDSheet.cvSuggestionsButton" );
|
|
// We used toBeVisible here but the update to RN0.77 broke this expectation
|
|
expect( confirmButton ).toBeOnTheScreen( );
|
|
await actor.press( confirmButton );
|
|
// Wait for the actual identification we created to appear
|
|
const taxonNameInIdent = await within( activityTab ).findByText( topSuggestion.taxon.name );
|
|
// We used toBeVisible here but the update to RN0.77 broke this expectation
|
|
expect( taxonNameInIdent ).toBeOnTheScreen( );
|
|
expect( inatjs.identifications.create ).toHaveBeenCalledWith( {
|
|
fields: Identification.ID_FIELDS,
|
|
identification: {
|
|
observation_id: observations[0].uuid,
|
|
taxon_id: taxonId,
|
|
vision: true
|
|
}
|
|
}, {
|
|
api_token: TEST_JWT
|
|
} );
|
|
} );
|
|
|
|
it( "should update observation with vision=true via ObsEdit", async ( ) => {
|
|
const { observations } = await setupAppWithSignedInUser( );
|
|
await navigateToSuggestionsForObservationViaObsEdit( observations[0] );
|
|
const topTaxonResultButton = await screen.findByTestId(
|
|
`SuggestionsList.taxa.${topSuggestion.taxon.id}.checkmark`
|
|
);
|
|
expect( topTaxonResultButton ).toBeTruthy( );
|
|
await actor.press( topTaxonResultButton );
|
|
const saveChangesButton = await screen.findByText( "SAVE CHANGES" );
|
|
expect( saveChangesButton ).toBeTruthy( );
|
|
await actor.press( saveChangesButton );
|
|
// Ensure we're back on MyObs
|
|
const observationGridItem = await screen.findByTestId(
|
|
`MyObservations.obsGridItem.${observations[0].uuid}`
|
|
);
|
|
await waitFor( ( ) => {
|
|
// We used toBeVisible here but the update to RN0.77 broke this expectation
|
|
expect( observationGridItem ).toBeOnTheScreen( );
|
|
}, { timeout: 3000, interval: 500 } );
|
|
const savedObservation = global.mockRealms[__filename]
|
|
.objectForPrimaryKey( "Observation", observations[0].uuid );
|
|
expect( savedObservation ).toHaveProperty( "owners_identification_from_vision", true );
|
|
} );
|
|
|
|
it.todo( "should create an identification when accessed from Explore" );
|
|
} );
|