Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
32 changes: 16 additions & 16 deletions scripts/detox-utils.cjs → scripts/e2e/detox-utils.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
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;

Expand All @@ -11,15 +13,15 @@ const apkBulidArchitecture = isRunningCI ? 'x86_64' : 'arm64-v8a';
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 developement
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 developement
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,
}
};
41 changes: 41 additions & 0 deletions scripts/e2e/ios-devices.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const DEFAULT_APPLE_DEVICE = 'iPhone 17';
const DEFEAULT_IOS_VERSION = 'iOS 26.2';

/**
* @return {string}
*/
function getAppleDevice() {
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 passedVersion = process.env.RNS_E2E_IOS_VERSION;
if (passedVersion) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume that the passed version should have some specific format, so I'd add some documentation and additional validation that it matches the proper regex

Copy link
Collaborator Author

@KrzysztofWojnar KrzysztofWojnar Nov 24, 2025

Choose a reason for hiding this comment

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

configuration will be passed to Detox and I planned to rely on validation in Detox itself. However, now I think you're right, because for as long as I can remember, iPhones have had consistent naming conventions and I can expect them to maintain it.

if (passedVersion.startsWith('iOS ')) {
return /** @type {`iOS ${string}`} */ (passedVersion);
}
return `iOS ${passedVersion}`;
}
return DEFEAULT_IOS_VERSION;
}

/**
* @typedef {Object} AppleDevice - creates a new type named 'SpecialType'
* @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