mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2026-06-20 05:28:29 -04:00
The problem seemed to be reverse geocoding the coordinates for each observation before moving on to ObsEdit, i.e. when that threw an exception it kind of silently cause Promise.all not to resolve... which is not supposed to happen for a few reasons, foremost among them that we were catching the error and returning null instead. So I'm still confused about why exactly this was happening. Regardless, geocoding is potentially slow and buggy, so IMO it's better to do it on ObsEdit than in the provider, and only do it when we need it, i.e. when the user is actually looking at the obs. Some other minor changes * Show loading indicator on GroupPhotos button while creating obs * fetchPlaceName performs a null check on coords before making a network request to test connectivity * More precise error handling * Removed some redundant await statements * Mocked react-native-geocoder-reborn in tests Closes #857
310 lines
9.0 KiB
JavaScript
310 lines
9.0 KiB
JavaScript
import "react-native-gesture-handler/jestSetup";
|
|
import "@shopify/flash-list/jestSetup";
|
|
|
|
import mockBottomSheet from "@gorhom/bottom-sheet/mock";
|
|
import mockRNCNetInfo from "@react-native-community/netinfo/jest/netinfo-mock";
|
|
import inatjs from "inaturalistjs";
|
|
import React from "react";
|
|
import mockRNDeviceInfo from "react-native-device-info/jest/react-native-device-info-mock";
|
|
import mockRNLocalize from "react-native-localize/mock";
|
|
// eslint-disable-next-line import/no-unresolved
|
|
import mockSafeAreaContext from "react-native-safe-area-context/jest/mock";
|
|
|
|
import { makeResponse } from "./factory";
|
|
import {
|
|
mockCamera,
|
|
mockSortDevices,
|
|
mockUseCameraDevice
|
|
} from "./vision-camera/vision-camera";
|
|
|
|
jest.mock( "vision-camera-plugin-inatvision" );
|
|
jest.mock( "react-native-worklets-core", () => ( {
|
|
Worklets: {
|
|
createRunInJsFn: jest.fn()
|
|
}
|
|
} ) );
|
|
|
|
jest.mock( "@sayem314/react-native-keep-awake" );
|
|
jest.mock( "react-native/Libraries/EventEmitter/NativeEventEmitter" );
|
|
|
|
jest.mock(
|
|
"@react-native-async-storage/async-storage",
|
|
() => require( "@react-native-async-storage/async-storage/jest/async-storage-mock" )
|
|
);
|
|
|
|
require( "react-native-reanimated/lib/reanimated2/jestUtils" ).setUpTests();
|
|
|
|
jest.mock( "react-native-vision-camera", ( ) => ( {
|
|
Camera: mockCamera,
|
|
sortDevices: mockSortDevices,
|
|
useCameraDevice: mockUseCameraDevice,
|
|
VisionCameraProxy: {
|
|
getFrameProcessorPlugin: jest.fn( )
|
|
}
|
|
} ) );
|
|
|
|
jest.mock( "react-native-localize", () => mockRNLocalize );
|
|
jest.mock( "react-native-safe-area-context", () => mockSafeAreaContext );
|
|
// Trivial mock b/c I assume we can't really test the native parts of this
|
|
// library ~~~kueda 20230516
|
|
jest.mock( "react-native-share-menu", ( ) => ( {
|
|
addNewShareListener: jest.fn( ),
|
|
getInitialShare: jest.fn( )
|
|
} ) );
|
|
|
|
// mock Portal with a Modal component inside of it (MediaViewer)
|
|
jest.mock( "react-native-paper", () => {
|
|
const actual = jest.requireActual( "react-native-paper" );
|
|
const MockedModule = {
|
|
...actual,
|
|
// eslint-disable-next-line react/jsx-no-useless-fragment
|
|
Portal: ( { children } ) => <>{children}</>
|
|
};
|
|
return MockedModule;
|
|
} );
|
|
|
|
jest.mock( "@react-navigation/native", ( ) => {
|
|
const actualNav = jest.requireActual( "@react-navigation/native" );
|
|
return {
|
|
...actualNav,
|
|
useIsFocused: jest.fn( ( ) => true ),
|
|
useRoute: jest.fn( ( ) => ( { } ) ),
|
|
useNavigation: ( ) => ( {
|
|
addListener: jest.fn(),
|
|
setOptions: jest.fn( ),
|
|
goBack: jest.fn( )
|
|
} )
|
|
};
|
|
} );
|
|
|
|
jest.mock( "@react-navigation/drawer", ( ) => {
|
|
const actualNav = jest.requireActual( "@react-navigation/drawer" );
|
|
return {
|
|
...actualNav,
|
|
useDrawerStatus: jest.fn( ( ) => false )
|
|
};
|
|
} );
|
|
|
|
// this resolves error with importing file after Jest environment is torn down
|
|
// https://github.com/react-navigation/react-navigation/issues/9568#issuecomment-881943770
|
|
jest.mock( "@react-navigation/native/lib/commonjs/useLinking.native", ( ) => ( {
|
|
default: ( ) => ( { getInitialState: { then: jest.fn( ) } } ),
|
|
__esModule: true
|
|
} ) );
|
|
|
|
// https://github.com/callstack/react-native-testing-library/issues/658#issuecomment-766886514
|
|
jest.mock( "react-native/Libraries/LogBox/LogBox" );
|
|
|
|
jest.mock( "react-native-config", () => ( {
|
|
OAUTH_CLIENT_ID: process.env.OAUTH_CLIENT_ID,
|
|
OAUTH_CLIENT_SECRET: process.env.OAUTH_CLIENT_SECRET,
|
|
JWT_ANONYMOUS_API_SECRET: process.env.JWT_ANONYMOUS_API_SECRET,
|
|
API_URL: process.env.API_URL
|
|
} ) );
|
|
|
|
jest.mock( "react-native-device-info", () => mockRNDeviceInfo );
|
|
|
|
jest.mock( "react-native-sensitive-info", () => {
|
|
class RNSInfo {
|
|
static stores = new Map();
|
|
|
|
static getServiceName( o = {} ) {
|
|
return o.sharedPreferencesName
|
|
|| o.keychainService
|
|
|| "default";
|
|
}
|
|
|
|
static validateString( s ) {
|
|
if ( typeof s !== "string" ) { throw new Error( "Invalid string:", s ); }
|
|
}
|
|
|
|
static getItem = jest.fn( async ( k, o ) => {
|
|
RNSInfo.validateString( k );
|
|
|
|
const serviceName = RNSInfo.getServiceName( o );
|
|
const service = RNSInfo.stores.get( serviceName );
|
|
|
|
if ( service ) { return service.get( k ) || null; }
|
|
return null;
|
|
} );
|
|
|
|
static getAllItems = jest.fn( async o => {
|
|
const serviceName = RNSInfo.getServiceName( o );
|
|
const service = RNSInfo.stores.get( serviceName );
|
|
let mappedValues = [];
|
|
|
|
if ( service?.size ) {
|
|
// for ( const [k, v] of service.entries() ) {
|
|
// mappedValues.push( { key: k, value: v, service: serviceName } );
|
|
// }
|
|
mappedValues = service.entries( ).map(
|
|
( key, value ) => ( { key, value, service: serviceName } )
|
|
);
|
|
}
|
|
|
|
return mappedValues;
|
|
} );
|
|
|
|
static setItem = jest.fn( async ( k, v, o ) => {
|
|
RNSInfo.validateString( k );
|
|
RNSInfo.validateString( v );
|
|
|
|
const serviceName = RNSInfo.getServiceName( o );
|
|
let service = RNSInfo.stores.get( serviceName );
|
|
|
|
if ( !service ) {
|
|
RNSInfo.stores.set( serviceName, new Map() );
|
|
service = RNSInfo.stores.get( serviceName );
|
|
}
|
|
|
|
service.set( k, v );
|
|
|
|
return null;
|
|
} );
|
|
|
|
static deleteItem = jest.fn( async ( k, o ) => {
|
|
RNSInfo.validateString( k );
|
|
|
|
const serviceName = RNSInfo.getServiceName( o );
|
|
const service = RNSInfo.stores.get( serviceName );
|
|
|
|
if ( service ) { service.delete( k ); }
|
|
|
|
return null;
|
|
} );
|
|
|
|
static hasEnrolledFingerprints = jest.fn( async () => true );
|
|
|
|
static setInvalidatedByBiometricEnrollment = jest.fn();
|
|
|
|
// "Touch ID" | "Face ID" | false
|
|
static isSensorAvailable = jest.fn( async () => "Face ID" );
|
|
}
|
|
|
|
return RNSInfo;
|
|
} );
|
|
|
|
// Some test environments may need a little more time
|
|
jest.setTimeout( 50000 );
|
|
|
|
// https://github.com/zoontek/react-native-permissions
|
|
// eslint-disable-next-line global-require
|
|
jest.mock( "react-native-permissions", () => require( "react-native-permissions/mock" ) );
|
|
|
|
// mocking globally since this currently affects a handful of unit and integration tests
|
|
jest.mock( "@react-native-community/geolocation", ( ) => ( {
|
|
getCurrentPosition: ( ) => jest.fn( )
|
|
} ) );
|
|
require( "react-native" ).NativeModules.RNCGeolocation = { };
|
|
|
|
jest.mock( "@react-native-community/netinfo", () => mockRNCNetInfo );
|
|
|
|
global.ReanimatedDataMock = {
|
|
now: () => 0
|
|
};
|
|
|
|
jest.mock( "react-native-fs", ( ) => {
|
|
const RNFS = {
|
|
appendFile: jest.fn( ),
|
|
CachesDirectoryPath: "caches/directory/path",
|
|
DocumentDirectoryPath: "document/directory/path",
|
|
exists: jest.fn( async ( ) => true ),
|
|
moveFile: async ( ) => "testdata",
|
|
copyFile: async ( ) => "testdata",
|
|
stat: jest.fn( ( ) => ( {
|
|
mtime: 123
|
|
} ) ),
|
|
readFile: jest.fn( ( ) => "testdata" ),
|
|
readDir: jest.fn( async ( ) => ( [
|
|
{
|
|
ctime: 123,
|
|
mtime: 123,
|
|
name: "testdata"
|
|
}
|
|
] ) )
|
|
};
|
|
|
|
return RNFS;
|
|
} );
|
|
|
|
require( "react-native" ).NativeModules.FileReaderModule = { };
|
|
|
|
// Mock native animation for all tests
|
|
jest.mock( "react-native/Libraries/Animated/NativeAnimatedHelper" );
|
|
|
|
jest.mock( "@gorhom/bottom-sheet", ( ) => ( {
|
|
...mockBottomSheet,
|
|
__esModule: true,
|
|
// eslint-disable-next-line react/jsx-no-useless-fragment
|
|
BottomSheetTextInput: ( ) => <></>
|
|
} ) );
|
|
|
|
jest.mock( "@react-native-camera-roll/camera-roll", ( ) => ( {
|
|
nativeInterface: jest.fn( ),
|
|
CameraRoll: {
|
|
getPhotos: jest.fn( ( ) => ( {
|
|
page_info: {
|
|
end_cursor: jest.fn( ),
|
|
has_next_page: false
|
|
},
|
|
edges: [
|
|
// This expexcts something like
|
|
// { node: photo }
|
|
]
|
|
} ) ),
|
|
getAlbums: jest.fn( ( ) => ( {
|
|
// Expecting album titles as keys and photo counts as values
|
|
// "My Amazing album": 12
|
|
} ) )
|
|
}
|
|
} ) );
|
|
|
|
jest.mock( "react-native-exif-reader", ( ) => ( {
|
|
readExif: jest.fn( )
|
|
} ) );
|
|
|
|
// https://github.com/APSL/react-native-keyboard-aware-scroll-view/issues/493#issuecomment-861711442
|
|
jest.mock( "react-native-keyboard-aware-scroll-view", ( ) => ( {
|
|
KeyboardAwareScrollView: jest
|
|
.fn( )
|
|
.mockImplementation( ( { children } ) => children )
|
|
} ) );
|
|
|
|
// Mock inaturalistjs so we can make some fake responses
|
|
jest.mock( "inaturalistjs" );
|
|
inatjs.observations.search.mockResolvedValue( makeResponse( ) );
|
|
inatjs.observations.updates.mockResolvedValue( makeResponse( ) );
|
|
|
|
jest.mock( "react-native-orientation-locker", () => ( {
|
|
addDeviceOrientationListener: jest.fn( ),
|
|
addEventListener: jest.fn( ),
|
|
getDeviceOrientation: jest.fn( ),
|
|
getInitialOrientation: jest.fn( ),
|
|
getOrientation: jest.fn( ),
|
|
lockToPortrait: jest.fn( ),
|
|
removeEventListener: jest.fn( ),
|
|
removeOrientationListener: jest.fn( ),
|
|
unlockAllOrientations: jest.fn( )
|
|
} ) );
|
|
|
|
const mockErrorHandler = error => {
|
|
console.log( error );
|
|
};
|
|
jest.mock( "react-native-exception-handler", () => ( {
|
|
setJSExceptionHandler: jest
|
|
.fn()
|
|
.mockImplementation( () => mockErrorHandler() ),
|
|
setNativeExceptionHandler: jest
|
|
.fn()
|
|
.mockImplementation( () => mockErrorHandler() )
|
|
} ) );
|
|
|
|
jest.mock( "react-native-geocoder-reborn", ( ) => ( {
|
|
geocodePosition: jest.fn( coord => [
|
|
`Somewhere near ${coord.lat}, ${coord.lng}`,
|
|
"Somewhere",
|
|
"Somewheria",
|
|
"SW"
|
|
] )
|
|
} ) );
|