mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2025-12-23 22:18:36 -05:00
Use fixed header in ObsDetails advanced mode (#2741)
* Use fixed header on ObsDetails advanced mode * Remove test for removed component - using react navigation header instead * Remove tests related to ObsDetails header icon; using react navigation * Add testID to react navigation level back button and fix e2e * Terminate app after every e2e test and maybe help flakiness * Rework termination of app --------- Co-authored-by: Johannes Klein <johannes.t.klein@gmail.com>
This commit is contained in:
committed by
GitHub
parent
1347ca3488
commit
9b1275a6eb
@@ -2,7 +2,7 @@ import {
|
||||
by, device, element, waitFor
|
||||
} from "detox";
|
||||
|
||||
import { iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
|
||||
import { iNatE2eAfterEach, iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
|
||||
import closeOnboarding from "./sharedFlows/closeOnboarding";
|
||||
import deleteObservation from "./sharedFlows/deleteObservation";
|
||||
import signIn from "./sharedFlows/signIn";
|
||||
@@ -13,6 +13,7 @@ const TIMEOUT = 10_000;
|
||||
describe( "AICamera", () => {
|
||||
beforeAll( async () => iNatE2eBeforeAll( device ) );
|
||||
beforeEach( async () => iNatE2eBeforeEach( device ) );
|
||||
afterEach( async () => iNatE2eAfterEach( device ) );
|
||||
|
||||
it(
|
||||
"should open the ai camera, take photo, select a suggestion, upload and delete observation",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { execSync } from "child_process";
|
||||
import { exec, execSync } from "child_process";
|
||||
|
||||
export async function iNatE2eBeforeAll( device ) {
|
||||
if ( device.getPlatform() === "android" ) {
|
||||
@@ -53,3 +53,100 @@ export async function iNatE2eBeforeEach( device ) {
|
||||
`plutil -replace restrictedBool.allowPasswordAutoFill.value -bool NO ~/Library/Developer/CoreSimulator/Devices/${device.id}/data/Library/UserConfigurationProfiles/PublicInfo/PublicEffectiveUserSettings.plist`
|
||||
);
|
||||
}
|
||||
|
||||
function execPromise( command ) {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
exec( command, ( error, stdout, stderr ) => {
|
||||
if ( error ) {
|
||||
console.log( `Error executing command: ${command}` );
|
||||
console.log( `stderr: ${stderr}` );
|
||||
reject( error );
|
||||
return;
|
||||
}
|
||||
resolve( stdout );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
async function getSimulatorId() {
|
||||
try {
|
||||
// List all available simulators
|
||||
const output = await execPromise( "xcrun simctl list devices --json" );
|
||||
const { devices } = JSON.parse( output );
|
||||
|
||||
// Use Object.values and Array.find instead of loops
|
||||
const bootedDevice = Object.entries( devices )
|
||||
.flatMap( ( [_runtime, deviceList] ) => deviceList ) // Use _ prefix for unused variables
|
||||
.find( device => device.state === "Booted" );
|
||||
|
||||
if ( bootedDevice ) {
|
||||
console.log( `Found booted simulator: ${bootedDevice.name} (${bootedDevice.udid})` );
|
||||
return bootedDevice.udid;
|
||||
}
|
||||
|
||||
console.log( "No booted simulator found" );
|
||||
return null;
|
||||
} catch ( error ) {
|
||||
console.log( "Error getting simulator ID:", error.message );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function terminateApp( deviceId, bundleId ) {
|
||||
try {
|
||||
console.log( `Attempting to terminate ${bundleId} on device ${deviceId}...` );
|
||||
const result = execSync( `/usr/bin/xcrun simctl terminate ${deviceId} ${bundleId}` );
|
||||
console.log( "App terminated successfully", result.toString() );
|
||||
return true;
|
||||
} catch ( error ) {
|
||||
if ( error.stderr && error.stderr.toString().includes( "found nothing to terminate" ) ) {
|
||||
console.log( "App is not running, nothing to terminate." );
|
||||
return true;
|
||||
}
|
||||
console.error( "Error during app termination:", error.message );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function iNatE2eAfterEach( device ) {
|
||||
if ( device && device.getPlatform() === "android" ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Try to use device.terminateApp first (the built-in Detox method)
|
||||
if ( device ) {
|
||||
try {
|
||||
await device.terminateApp();
|
||||
console.log( "App terminated through Detox" );
|
||||
// Add a small delay to let Detox processes settle
|
||||
await new Promise( resolve => { setTimeout( resolve, 300 ); } );
|
||||
return;
|
||||
} catch ( detoxError ) {
|
||||
console.log(
|
||||
"Detox terminateApp failed, falling back to manual termination:",
|
||||
detoxError.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to manual termination if Detox method fails or device is unavailable
|
||||
const deviceId = await getSimulatorId();
|
||||
const bundleId = "org.inaturalist.iNaturalistMobile";
|
||||
|
||||
if ( deviceId && bundleId ) {
|
||||
console.log( "Using manual termination via simctl" );
|
||||
// Use existing terminateApp, but don't throw errors
|
||||
try {
|
||||
await terminateApp( deviceId, bundleId );
|
||||
} catch ( error ) {
|
||||
console.log( "Manual termination error (non-fatal):", error.message );
|
||||
}
|
||||
|
||||
// Add a delay to let processes settle
|
||||
await new Promise( resolve => { setTimeout( resolve, 500 ); } );
|
||||
}
|
||||
} catch ( error ) {
|
||||
console.log( "Error during cleanup (non-fatal):", error.message );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
// we don't need this or switchPowerMode.e2e.js.js since they're repetititve
|
||||
// with what we're already doing in the signedIn and aiCamera tests
|
||||
// and we would see any failures there
|
||||
|
||||
import { device } from "detox";
|
||||
|
||||
import { iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
|
||||
import { iNatE2eAfterEach, iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
|
||||
import closeOnboarding from "./sharedFlows/closeOnboarding";
|
||||
import signIn from "./sharedFlows/signIn";
|
||||
|
||||
describe( "Shared flow", () => {
|
||||
beforeAll( async () => iNatE2eBeforeAll( device ) );
|
||||
beforeEach( async () => iNatE2eBeforeEach( device ) );
|
||||
afterEach( async ( ) => iNatE2eAfterEach( device ) );
|
||||
|
||||
it( "should sign in the test user", async () => {
|
||||
await closeOnboarding( );
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
by, device, element, expect, waitFor
|
||||
} from "detox";
|
||||
|
||||
import { iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
|
||||
import { iNatE2eAfterEach, iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
|
||||
import closeOnboarding from "./sharedFlows/closeOnboarding";
|
||||
import deleteObservation from "./sharedFlows/deleteObservation";
|
||||
import signIn from "./sharedFlows/signIn";
|
||||
@@ -11,6 +11,7 @@ import uploadObservation from "./sharedFlows/uploadObservation";
|
||||
describe( "Signed in user", () => {
|
||||
beforeAll( async ( ) => iNatE2eBeforeAll( device ) );
|
||||
beforeEach( async ( ) => iNatE2eBeforeEach( device ) );
|
||||
afterEach( async ( ) => iNatE2eAfterEach( device ) );
|
||||
|
||||
async function createAndUploadObservation( options = { upload: false } ) {
|
||||
const addObsButton = element( by.id( "add-obs-button" ) );
|
||||
@@ -101,7 +102,7 @@ describe( "Signed in user", () => {
|
||||
await element( by.id( `ObsDetails.${uuid}` ) ).scrollTo( "bottom" );
|
||||
const comment = element( by.text( "This is a comment" ) );
|
||||
await waitFor( comment ).toBeVisible().withTimeout( 10000 );
|
||||
await element( by.id( "ObsDetails.BackButton" ) ).tap( );
|
||||
await element( by.id( "header-back-button" ) ).tap( );
|
||||
await waitFor( username ).toBeVisible( ).withTimeout( 10000 );
|
||||
|
||||
/*
|
||||
|
||||
@@ -6,12 +6,13 @@ import {
|
||||
waitFor
|
||||
} from "detox";
|
||||
|
||||
import { iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
|
||||
import { iNatE2eAfterEach, iNatE2eBeforeAll, iNatE2eBeforeEach } from "./helpers";
|
||||
import closeOnboarding from "./sharedFlows/closeOnboarding";
|
||||
|
||||
describe( "Signed out user", () => {
|
||||
beforeAll( async ( ) => iNatE2eBeforeAll( device ) );
|
||||
beforeEach( async ( ) => iNatE2eBeforeEach( device ) );
|
||||
afterEach( async ( ) => iNatE2eAfterEach( device ) );
|
||||
|
||||
it( "should start at My Observations with log in text", async () => {
|
||||
await closeOnboarding( );
|
||||
|
||||
Reference in New Issue
Block a user