Files
iNaturalistReactNative/tests/integration/PhotoImport.test.js
Johannes Klein 6e696cb524 Update to react-native 0.77 (#3026)
* Upgrade helper changes JS side

* Upgrade helper Android

* Upgrade helper iOS side

* Update project.pbxproj

* Create react-native-modal+14.0.0-rc.1.patch

* BackHandler.removeEventListener is deprecated

* Update react-native-modal

* Update .flowconfig

* Update package-lock.json

* Update Podfile.lock

* Update Podfile.lock from main

* Replace toBeVisible with toBeOnTheScreen

This is not recommended by react-navigation, because even though toBeOnTheScreen makes sure the components are in the tree it does not mean they are visible to the user. For example, in terms of navigation a previous screen is still i the tree but not visible to the user in the app.
I spent around a day trying to figure out why the isVisible check stopped working, and still have no clear answer.
Testing in the actual app shows that all of those flows are still working as expected, so it is a test-environment-only problem.
My suggestion would be to re-visit this problem after we have updated RN to latest, and testing related libraries to latest versions.
2025-08-01 12:11:13 +02:00

177 lines
6.1 KiB
JavaScript

import {
screen,
userEvent,
waitFor,
within
} from "@testing-library/react-native";
import initI18next from "i18n/initI18next";
import inatjs from "inaturalistjs";
import * as ImagePicker from "react-native-image-picker";
import { SCREEN_AFTER_PHOTO_EVIDENCE } from "stores/createLayoutSlice.ts";
import factory, { makeResponse } from "tests/factory";
import faker from "tests/helpers/faker";
import { renderApp } from "tests/helpers/render";
import setStoreStateLayout from "tests/helpers/setStoreStateLayout";
import setupUniqueRealm from "tests/helpers/uniqueRealm";
// We're explicitly testing navigation here so we want react-navigation
// working normally
jest.unmock( "@react-navigation/native" );
const directory = faker.string.uuid( );
const mockFileName = `${faker.string.uuid( )}.jpg`;
const mockUri = `file:///var/mobile/Containers/Data/Application/${directory}/tmp/${mockFileName}`;
const mockImageLibraryResponse = {
assets: [
{
uri: mockUri,
fileName: mockFileName
}
]
};
const mockImageLibraryResponseMultiplePhotos = {
assets: [
{
uri: "some_uri",
fileName: "some_file_name"
},
{
uri: mockUri,
fileName: mockFileName
}
]
};
jest.mock( "react-native-image-picker", ( ) => ( {
launchImageLibrary: jest.fn( ( ) => mockImageLibraryResponse )
} ) );
// 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 mockUser = factory( "LocalUser" );
// Mock useCurrentUser hook
jest.mock( "sharedHooks/useCurrentUser", () => ( {
__esModule: true,
default: jest.fn( () => mockUser )
} ) );
// Mock the response from inatjs.computervision.score_image
const topSuggestion = {
taxon: factory.states( "genus" )( "RemoteTaxon", { name: "Primum" } ),
combined_score: 90
};
beforeAll( async () => {
await initI18next();
jest.useFakeTimers( );
} );
beforeEach( ( ) => {
setStoreStateLayout( {
isDefaultMode: false,
screenAfterPhotoEvidence: SCREEN_AFTER_PHOTO_EVIDENCE.SUGGESTIONS,
isAllAddObsOptionsMode: true
} );
inatjs.computervision.score_image.mockResolvedValue( makeResponse( [topSuggestion] ) );
} );
describe( "Photo Import", ( ) => {
const actor = userEvent.setup( );
async function importPhotoForNewObs() {
const tabBar = await screen.findByTestId( "CustomTabBar" );
const addObsButton = await within( tabBar ).findByLabelText( "Add observations" );
await actor.press( addObsButton );
const photoImportButton = await within( tabBar ).findByLabelText( "Photo importer" );
await actor.press( photoImportButton );
}
async function groupPhotosIntoObservation() {
const groupPhotosText = await screen.findByText( /Group Photos/ );
// We used toBeVisible here but the update to RN0.77 broke this expectation
expect( groupPhotosText ).toBeOnTheScreen( );
const path = "file://document/directory/path/galleryPhotos/";
const firstUri = `${path}${mockImageLibraryResponseMultiplePhotos.assets[0].fileName}`;
const secondUri = `${path}${mockImageLibraryResponseMultiplePhotos.assets[1].fileName}`;
const firstPhoto = await screen.findByTestId( `GroupPhotos.${firstUri}` );
await actor.press( firstPhoto );
const secondPhoto = await screen.findByTestId( `GroupPhotos.${secondUri}` );
await actor.press( secondPhoto );
const combineButton = await screen.findByLabelText( /Combine Photos/ );
await actor.press( combineButton );
const importButton = await screen.findByText( /IMPORT 1 OBSERVATION/ );
await actor.press( importButton );
}
async function viewSuggestionsAndAddId() {
const topTaxonResultButton = await screen.findByTestId(
`SuggestionsList.taxa.${topSuggestion.taxon.id}.checkmark`
);
await actor.press( topTaxonResultButton );
}
async function saveObservationWithPhoto() {
// Make sure we're on ObsEdit
const evidenceTitle = await screen.findByText( "EVIDENCE" );
// We used toBeVisible here but the update to RN0.77 broke this expectation
expect( evidenceTitle ).toBeOnTheScreen( );
const localFilePath = `file://document/directory/path/photoUploads/${mockFileName}`;
const photoEvidence = await screen.findByTestId( `EvidenceList.${localFilePath}` );
// We used toBeVisible here but the update to RN0.77 broke this expectation
expect( photoEvidence ).toBeOnTheScreen( );
const saveButton = await screen.findByText( "SAVE" );
await actor.press( saveButton );
const okButton = await screen.findByText( "OK" );
await actor.press( okButton );
await actor.press( saveButton );
// Wait until header shows that there's an obs to upload
await screen.findByText( /Upload \d observation/ );
const obsGridItems = await screen.findAllByTestId( /MyObservations\.obsGridItem\..*/ );
await waitFor( () => {
// We used toBeVisible here but the update to RN0.77 broke this expectation
expect( obsGridItems[0] ).toBeOnTheScreen( );
}, { timeout: 3_000, interval: 500 } );
}
it( "should create and save an observation with an imported photo", async ( ) => {
renderApp( );
await importPhotoForNewObs( );
await viewSuggestionsAndAddId( );
await saveObservationWithPhoto( );
} );
it( "should create and save an observation with multiple imported photos", async ( ) => {
jest.spyOn( ImagePicker, "launchImageLibrary" ).mockImplementation(
( ) => mockImageLibraryResponseMultiplePhotos
);
renderApp( );
await importPhotoForNewObs( );
await groupPhotosIntoObservation( );
await viewSuggestionsAndAddId( );
await saveObservationWithPhoto( );
} );
} );