Files
iNaturalistReactNative/tests/integration/broken/SuggestionsWithSyncedObs.test.js
Abbey Campbell 2ee99d6c7b fix tests
2025-12-02 13:29:00 -08:00

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" );
} );