mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2025-12-23 22:18:36 -05:00
e2e mock for Geolocation.watchPosition to deflake tests (#1842)
This commit is contained in:
@@ -70,13 +70,16 @@ module.exports = {
|
||||
],
|
||||
"no-alert": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-unused-vars": [
|
||||
// This gets around eslint problems when typing functions in TS
|
||||
"no-unused-vars": 0,
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
vars: "all",
|
||||
args: "after-used",
|
||||
// Overriding airbnb to allow leading underscore to indicate unused var
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
ignoreRestSiblings: true
|
||||
}
|
||||
],
|
||||
@@ -136,7 +139,6 @@ module.exports = {
|
||||
|
||||
// TODO: we should actually type these at some point ~amanda 041824
|
||||
"@typescript-eslint/ban-types": 0,
|
||||
"@typescript-eslint/no-unused-vars": 0,
|
||||
"@typescript-eslint/no-var-requires": 0
|
||||
},
|
||||
// need this so jest doesn't show as undefined in jest.setup.js
|
||||
|
||||
@@ -3,6 +3,8 @@ import {
|
||||
} from "detox";
|
||||
import Config from "react-native-config-node";
|
||||
|
||||
// This needs to be a relative path for the e2e-mock version to be used
|
||||
import { CHUCKS_PAD } from "../src/appConstants/e2e";
|
||||
import { iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
|
||||
|
||||
describe( "Signed in user", () => {
|
||||
@@ -24,6 +26,12 @@ describe( "Signed in user", () => {
|
||||
await waitFor( element( by.id( "new-observation-text" ) ) )
|
||||
.toBeVisible()
|
||||
.withTimeout( 10000 );
|
||||
// Ensure the location from the e2e-mock is being used so we don't end up
|
||||
// with tests flaking out due to time zone issues
|
||||
const pattern = new RegExp( `.*${CHUCKS_PAD.latitude.toFixed( 4 )}.*` );
|
||||
await waitFor( element( by.text( pattern ) ) )
|
||||
.toBeVisible()
|
||||
.withTimeout( 10000 );
|
||||
if ( options.upload ) {
|
||||
// Press Upload now button
|
||||
const uploadNowButton = element( by.id( "ObsEdit.uploadButton" ) );
|
||||
|
||||
20
src/appConstants/e2e.ts
Normal file
20
src/appConstants/e2e.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// e2e test data that needs to be referred to from e2e-mock files *and* the
|
||||
// e2e tests themselves
|
||||
|
||||
// Darwin's house. Note that the e2e tests run in a UTC environment, so the
|
||||
// observed_on_string will be set to a UTC time. If these coordinates fall
|
||||
// within a time zone west of that (i.e. in the past), observation creation
|
||||
// will fail during the period of the day when UTC time has crossed into the
|
||||
// date after the date at these coordinates. The opposite (when local time is
|
||||
// in a time zone behind the time zone of the coordinates) will not fail, but
|
||||
// it might make an observation that's a day behind when you were expecting.
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const CHUCKS_PAD = {
|
||||
latitude: 51.3313127,
|
||||
longitude: 0.0509862,
|
||||
accuracy: 5,
|
||||
altitude: null,
|
||||
altitudeAccuracy: null,
|
||||
heading: null,
|
||||
speed: null
|
||||
};
|
||||
@@ -36,20 +36,24 @@ const ObsEdit = ( ): Node => {
|
||||
const isFocused = useIsFocused( );
|
||||
const currentUser = useCurrentUser( );
|
||||
const [shouldFetchLocation, setShouldFetchLocation] = useState( false );
|
||||
const { hasPermissions, renderPermissionsGate, requestPermissions } = useLocationPermission( );
|
||||
const {
|
||||
hasPermissions: hasLocationPermission,
|
||||
renderPermissionsGate: renderLocationPermissionGate,
|
||||
requestPermissions: requestLocationPermission
|
||||
} = useLocationPermission( );
|
||||
|
||||
const {
|
||||
isFetchingLocation,
|
||||
userLocation
|
||||
} = useWatchPosition( {
|
||||
shouldFetchLocation
|
||||
} );
|
||||
} = useWatchPosition( { shouldFetchLocation } );
|
||||
|
||||
// Note the intended functionality is *not* to request location permission
|
||||
// until the user taps the missing location
|
||||
useEffect( ( ) => {
|
||||
if ( shouldFetchObservationLocation( currentObservation ) ) {
|
||||
if ( hasLocationPermission && shouldFetchObservationLocation( currentObservation ) ) {
|
||||
setShouldFetchLocation( true );
|
||||
}
|
||||
}, [currentObservation] );
|
||||
}, [currentObservation, hasLocationPermission] );
|
||||
|
||||
useEffect( ( ) => {
|
||||
if ( userLocation?.latitude ) {
|
||||
@@ -73,11 +77,11 @@ const ObsEdit = ( ): Node => {
|
||||
|
||||
const onLocationPress = ( ) => {
|
||||
// If we have location permissions, navigate to the location picker
|
||||
if ( hasPermissions ) {
|
||||
if ( hasLocationPermission ) {
|
||||
navToLocationPicker();
|
||||
} else {
|
||||
// If we don't have location permissions, request them
|
||||
requestPermissions( );
|
||||
requestLocationPermission( );
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,13 +149,13 @@ const ObsEdit = ( ): Node => {
|
||||
passesIdentificationTest={passesIdentificationTest}
|
||||
setCurrentObservationIndex={setCurrentObservationIndex}
|
||||
/>
|
||||
{renderPermissionsGate( {
|
||||
{renderLocationPermissionGate( {
|
||||
// If the user does not give location permissions in any form,
|
||||
// navigate to the location picker (if granted we just continue fetching the location)
|
||||
onRequestDenied: navToLocationPicker,
|
||||
onRequestBlocked: navToLocationPicker,
|
||||
onModalHide: ( ) => {
|
||||
if ( !hasPermissions ) navToLocationPicker();
|
||||
if ( !hasLocationPermission ) navToLocationPicker();
|
||||
}
|
||||
} )}
|
||||
</>
|
||||
|
||||
@@ -55,8 +55,7 @@ const ScrollableWithStickyHeader = ( {
|
||||
screenHeight,
|
||||
screenWidth
|
||||
} = useDeviceOrientation( );
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [scrollPosition, setScrollPosition] = useState( 0 );
|
||||
const [_scrollPosition, setScrollPosition] = useState( 0 );
|
||||
|
||||
const [stickyAt, setStickyAt] = useState( 0 );
|
||||
|
||||
|
||||
40
src/sharedHelpers/geolocationWrapper.e2e-mock
Normal file
40
src/sharedHelpers/geolocationWrapper.e2e-mock
Normal file
@@ -0,0 +1,40 @@
|
||||
import Geolocation, {
|
||||
GeolocationError,
|
||||
GeolocationResponse
|
||||
} from "@react-native-community/geolocation";
|
||||
import { CHUCKS_PAD } from "appConstants/e2e.ts";
|
||||
|
||||
function watchPosition(
|
||||
success: ( position: GeolocationResponse ) => void,
|
||||
error?: ( error: GeolocationError ) => void,
|
||||
options?: {
|
||||
interval?: number;
|
||||
fastestInterval?: number;
|
||||
timeout?: number;
|
||||
maximumAge?: number;
|
||||
enableHighAccuracy?: boolean;
|
||||
distanceFilter?: number;
|
||||
useSignificantChanges?: boolean;
|
||||
}
|
||||
) {
|
||||
console.log( "[DEBUG geolocationWrapper.e2e-mock] watchPosition" );
|
||||
const watchID = Date.now();
|
||||
setTimeout( ( ) => {
|
||||
console.log( "[DEBUG geolocationWrapper.e2e-mock] watchPosition success" );
|
||||
success( {
|
||||
coords: CHUCKS_PAD,
|
||||
timestamp: Date.now()
|
||||
} );
|
||||
}, 1000 );
|
||||
return watchID;
|
||||
}
|
||||
|
||||
function clearWatch( watchID: number ) {
|
||||
console.log( "[DEBUG geolocationWrapper.e2e-mock] clearWatch, watchID: ", watchID );
|
||||
}
|
||||
|
||||
export {
|
||||
CHUCKS_PAD,
|
||||
clearWatch,
|
||||
watchPosition
|
||||
};
|
||||
29
src/sharedHelpers/geolocationWrapper.ts
Normal file
29
src/sharedHelpers/geolocationWrapper.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
// This wraps the Geolocation methods we use so we can mock them for e2e tests
|
||||
// that tend to have problems with locations and timezones
|
||||
|
||||
import Geolocation, {
|
||||
GeolocationError,
|
||||
GeolocationResponse
|
||||
} from "@react-native-community/geolocation";
|
||||
|
||||
export function watchPosition(
|
||||
success: ( position: GeolocationResponse ) => void,
|
||||
error?: ( error: GeolocationError ) => void,
|
||||
options?: {
|
||||
interval?: number;
|
||||
fastestInterval?: number;
|
||||
timeout?: number;
|
||||
maximumAge?: number;
|
||||
enableHighAccuracy?: boolean;
|
||||
distanceFilter?: number;
|
||||
useSignificantChanges?: boolean;
|
||||
}
|
||||
) {
|
||||
const watchID = Geolocation.watchPosition( success, error, options );
|
||||
console.log( "[DEBUG geolocationWrapper.ts] watchPosition, watchID: ", watchID );
|
||||
return watchID;
|
||||
}
|
||||
|
||||
export function clearWatch( watchID: number ) {
|
||||
Geolocation.clearWatch( watchID );
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import Geolocation, {
|
||||
import {
|
||||
GeolocationError,
|
||||
GeolocationResponse
|
||||
} from "@react-native-community/geolocation";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
// Please don't change this to an aliased path or the e2e mock will not get
|
||||
// used in our e2e tests on Github Actions
|
||||
import { clearWatch, watchPosition } from "../sharedHelpers/geolocationWrapper";
|
||||
|
||||
export const TARGET_POSITIONAL_ACCURACY = 10;
|
||||
|
||||
interface UserLocation {
|
||||
@@ -32,7 +36,7 @@ const useWatchPosition = ( options: {
|
||||
shouldFetchLocation
|
||||
);
|
||||
|
||||
const watchPosition = ( ) => {
|
||||
const startWatch = ( ) => {
|
||||
setIsFetchingLocation( true );
|
||||
const success = ( position: GeolocationResponse ) => {
|
||||
setCurrentPosition( position );
|
||||
@@ -44,7 +48,7 @@ const useWatchPosition = ( options: {
|
||||
};
|
||||
|
||||
try {
|
||||
const watchID = Geolocation.watchPosition(
|
||||
const watchID = watchPosition(
|
||||
success,
|
||||
failure,
|
||||
geolocationOptions
|
||||
@@ -56,7 +60,7 @@ const useWatchPosition = ( options: {
|
||||
};
|
||||
|
||||
const stopWatch = useCallback( ( id: number ) => {
|
||||
Geolocation.clearWatch( id );
|
||||
clearWatch( id );
|
||||
setSubscriptionId( null );
|
||||
setCurrentPosition( null );
|
||||
setIsFetchingLocation( false );
|
||||
@@ -80,7 +84,7 @@ const useWatchPosition = ( options: {
|
||||
|
||||
useEffect( ( ) => {
|
||||
if ( shouldFetchLocation ) {
|
||||
watchPosition( );
|
||||
startWatch( );
|
||||
}
|
||||
}, [shouldFetchLocation] );
|
||||
|
||||
|
||||
Reference in New Issue
Block a user