Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,5 @@ android/.settings
# Local environment (direnv)
.envrc

# e2e
*Example/artifacts
2 changes: 1 addition & 1 deletion Example/.detoxrc.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
const utils = require('../scripts/detox-utils.cjs');
const utils = require('../scripts/e2e/detox-utils.cjs');
module.exports = utils.commonDetoxConfigFactory('ScreensExample');
8 changes: 4 additions & 4 deletions Example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
"format": "prettier --write --list-different './src/**/*.{js,ts,tsx}'",
"lint": "eslint --ext '.js,.ts,.tsx' --fix src && yarn check-types && yarn format",
"check-types": "tsc --noEmit",
"build-e2e-ios": "detox build --configuration ios.release",
"build-e2e-android": "detox build --configuration android.release",
"test-e2e-ios": "detox test --configuration ios.release --take-screenshots failing",
"test-e2e-android": "detox test --configuration android.release --take-screenshots failing",
"build-e2e-ios": "detox build --configuration ios.sim.release",
"build-e2e-android": "detox build --configuration android.emu.release",
"test-e2e-ios": "detox test --configuration ios.sim.release --take-screenshots failing",
"test-e2e-android": "detox test --configuration android.emu.release --take-screenshots failing",
"postinstall": "patch-package"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion FabricExample/.detoxrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
const utils = require('../scripts/detox-utils.cjs');
const utils = require('../scripts/e2e/detox-utils.cjs');
module.exports = utils.commonDetoxConfigFactory('FabricExample');

8 changes: 4 additions & 4 deletions FabricExample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
"lint": "eslint .",
"start": "npx react-native start",
"test": "jest",
"build-e2e-ios": "detox build --configuration ios.release",
"build-e2e-android": "detox build --configuration android.release",
"test-e2e-ios": "detox test --configuration ios.release --take-screenshots failing",
"test-e2e-android": "detox test --configuration android.release --take-screenshots failing",
"build-e2e-ios": "detox build --configuration ios.sim.release",
"build-e2e-android": "detox build --configuration android.emu.release",
"test-e2e-ios": "detox test --configuration ios.sim.release --take-screenshots failing",
"test-e2e-android": "detox test --configuration android.emu.release --take-screenshots failing",
"postinstall": "patch-package"
},
"dependencies": {
Expand Down
34 changes: 17 additions & 17 deletions scripts/detox-utils.cjs → scripts/e2e/detox-utils.cjs
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
const ChildProcess = require('node:child_process');
const { iosDevice } = require('./ios-devices');

const CI_AVD_NAME = 'e2e_emulator';
// Should be kept in sync with the constant defined in e2e workflow file
const DEFAULT_CI_AVD_NAME = 'e2e_emulator';

const isRunningCI = process.env.CI != null;

// Assumes that local developement is done on arm64-v8a.
// Assumes that local development is done on arm64-v8a.
const apkBulidArchitecture = isRunningCI ? 'x86_64' : 'arm64-v8a';
// test-butler requires AOSP emulator image, which is not available to download for arm64-v8a in Android Studio SDK Manager, therefore
// it is assumed here that arm64-v8a AOSP emulator is not available in local setup.
const testButlerApkPath = isRunningCI ? ['../Example/e2e/apps/test-butler-app-2.2.1.apk'] : undefined;

function detectLocalAndroidEmulator() {
// "DETOX_AVD_NAME" can be set for local developement
const detoxAvdName = process.env.DETOX_AVD_NAME ?? null;
if (detoxAvdName !== null) {
return detoxAvdName
// "RNS_E2E_AVD_NAME" can be set for local development
const avdName = process.env.RNS_E2E_AVD_NAME ?? null;
if (avdName !== null) {
return avdName
}

// Fallback: try to use Android SDK
try {
let stdout = ChildProcess.execSync("emulator -list-avds")
let stdout = ChildProcess.execSync("emulator -list-avds");

// Possibly convert Buffer to string
if (typeof stdout !== 'string') {
Expand All @@ -32,22 +34,22 @@ function detectLocalAndroidEmulator() {
throw new Error('No installed AVDs detected on the device');
}

// Just select first one in the list.
// Just select first one in the list.
// TODO: consider giving user a choice here.
return avdList[0];
} catch (error) {
const errorMessage = `Failed to find Android emulator. Set "DETOX_AVD_NAME" env variable pointing to one. Cause: ${error}`
const errorMessage = `Failed to find Android emulator. Set "RNS_E2E_AVD_NAME" env variable pointing to one. Cause: ${error}`;
console.error(errorMessage);
throw new Error(errorMessage);
}
}

function detectAndroidEmulatorName() {
return isRunningCI ? CI_AVD_NAME : detectLocalAndroidEmulator();
// "RNS_E2E_AVD_NAME" can be set for local development
return isRunningCI ? DEFAULT_CI_AVD_NAME : detectLocalAndroidEmulator();
}

/**
* @type {Detox.DetoxConfig}
* @param {string} applicationName name (FabricExample / ScreensExample)
* @returns {Detox.DetoxConfig}
*/
Expand Down Expand Up @@ -94,14 +96,12 @@ function commonDetoxConfigFactory(applicationName) {
devices: {
simulator: {
type: 'ios.simulator',
device: {
type: 'iPhone 16 Pro',
},
device: iosDevice,
},
attached: {
type: 'android.attached',
device: {
adbName: CI_AVD_NAME,
adbName: process.env.RNS_ADB_NAME,
},
utilBinaryPaths: testButlerApkPath,
},
Expand Down Expand Up @@ -147,10 +147,10 @@ function commonDetoxConfigFactory(applicationName) {
app: 'android.release',
},
},
}
};
}

module.exports = {
commonDetoxConfigFactory,
isRunningCI,
}
};
52 changes: 52 additions & 0 deletions scripts/e2e/ios-devices.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const DEFAULT_APPLE_DEVICE = 'iPhone 17';
const DEFAULT_IOS_VERSION = 'iOS 26.2';

/**
* @return {string}
*/
function getAppleDevice() {
const envVariableKey = 'RNS_E2E_APPLE_SIM_NAME';
const passedDevice = process.env[envVariableKey];
if (passedDevice) {
if (/^(iPhone|iPad)\s.+/.test(passedDevice)) {
Copy link
Contributor

@t0maboro t0maboro Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I haven't made up my mind, but we'll have a rather fixed number of checks; therefore, I'm wondering if startsWith won't improve readability compared to a regex. Treat it as a nitpick; for me, it's okay to leave it in its current form.

return passedDevice;
} else {
throw new Error(`Environment variable ${envVariableKey} should be "iPhone xyz" or "iPad xyz".`);
}
}
return process.env.RNS_E2E_APPLE_SIM_NAME || DEFAULT_APPLE_DEVICE;
}
/**
* @return {`iOS ${string}`} requested version of ios, or default if not specified
*/
function getIOSVersion() {
const envVariableKey = 'RNS_E2E_IOS_VERSION';
const passedVersion = process.env[envVariableKey];
if (passedVersion) {
if (/^(iOS)\s.+/.test(passedVersion)) {
Copy link
Contributor

@t0maboro t0maboro Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I haven't made up my mind, but we'll have a rather fixed number of checks; therefore, I'm wondering if startsWith won't improve readability compared to a regex. Treat it as a nitpick; for me, it's okay to leave it in its current form.

return /** @type {`iOS ${string}`} */ (passedVersion);
} else {
throw new Error(`Environment variable ${envVariableKey} should be "iOS xyz".`);
}
}
return DEFAULT_IOS_VERSION;
}

/**
* @typedef {Object} AppleDevice - represents Detox's config for an Apple device
* @property {string} type - a string which represents a model of an iPhone
* @property {`iOS ${string}`} os - operation system version
*/

/**
* @satisfies {AppleDevice}
* @readonly
* */
const iosDevice = {
type: getAppleDevice(),
os: getIOSVersion(),
};

module.exports = {
iosDevice,
};
Loading