Skip to content

refactor(test, iOS): resolve Detox simulator target so device name and OS version stay in sync#4195

Open
LKuchno wants to merge 7 commits into
mainfrom
@lkuchno/detox-device-configuration
Open

refactor(test, iOS): resolve Detox simulator target so device name and OS version stay in sync#4195
LKuchno wants to merge 7 commits into
mainfrom
@lkuchno/detox-device-configuration

Conversation

@LKuchno

@LKuchno LKuchno commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Description

Detox iOS e2e runs previously resolved the simulator device name and iOS version independently, each reading its own env var, with nothing guaranteeing the resulting pair belonged to the same — or even an existing — simulator.

This PR unifies resolution into a single SimulatorTarget ({ name, os }) so both values always come from the same simulator. Either env var can now be supplied on its own — a lone model resolves to its latest installed OS, a lone version to the first installed device on that OS — and it adds simulator auto-detection (querying what is installed and booted) so local runs adapt to whatever is already available.

Discussion was updated adding subsection:How the target simulator is chosen
https://github.com/software-mansion/react-native-screens-labs/discussions/572

Changelog

scripts/e2e/ios-devices.js:

  • Unified resolution — introduces resolveSimulatorTarget(), returning a single { name, os } so the device name and iOS version can never come from different simulators. resolveAppleSimulatorName() and getIOSVersion() now derive from it. The resolved target is memoized for the process lifetime.
  • Resolution order:
    1. RNS_APPLE_SIM_NAME / RNS_IOS_VERSION env vars. Either may be given on its own:
      • both provided → the exact name + os instance is validated against the installed simulators. If the combination doesn't exist — e.g. iPhone 15 with iOS 26 (a runtime that model can't run) — the script throws a clear error listing the OS versions that model is actually installed with, instead of letting Detox/applesimutils fail later. (When simctl returns nothing — failed or no simulators installed — validation can't run, so the script warns and returns the pair verbatim, letting Detox surface any error.)
      • model only → its latest installed OS, else the default OS;
      • version only → the first installed device on that OS (preferring a booted one), else the default device.
    2. An already-booted simulator — when several are booted, the newest iOS version wins (preferring iPhone models on a tie), even over the default device.
    3. The default hardcoded device/version, when an instance of it is actually installed.
    4. The newest installed simulator (preferring iPhone models) when the default isn't installed. Falls back to the defaults when nothing is installed, letting Detox surface the error.
  • simctl probinglistInstalledSimulators() queries xcrun simctl list devices available --json (memoized) and maps CoreSimulator runtime ids (...SimRuntime.iOS-26-2) to human versions (iOS 26.2). Only created, available instances are returned, because Detox matches existing simulators through applesimutils and does not create them from a device-type + runtime pairing. Degrades gracefully to defaults (with a warning) if xcrun is unavailable.
  • Config guard — the simctl probe only runs for iOS simulator configurations. The active Detox config is read from the --configuration flag and the DETOX_CONFIGURATION env var, with an argv-scan fallback for indirect invocations.
  • Validation & types — env-var formats are validated (iPhone|iPad …, iOS …) and, when both are given, the device + OS combination is checked for existence before use; JSDoc uses the `iOS ${string}` template-literal type so callers can't drop the prefix.

Test:

Check if script follows the order of simulator and requirements mentioned in comment:

  • Run script with user input
    - provide iPhone model and os version - different than default
    - provide iPhone model and os version not supported on that model
    - iPhone model provided w/o version - take the latest OS version if feasible
    - iOS provided w/o iPhone model - select something with appropriate SDK from the top of the list - first match.
  • Run simulator other than default one and run script without args when default simulator is installed
  • Run simulator other than default one and run script without args when default simulator is booted -> booted device with the newest version is taken
  • Make sure default simulator is installed and no simulators are booted, run script without args -> default device is run
  • Make sure default simulator is NOT installed and no simulator are booted and run script without args -> iPhone with newest version is run

…on stay in sync

Unify Detox iOS simulator resolution into a single SimulatorTarget so the
device name and iOS version always come from the same simulator, removing the
prior risk of pairing an overridden name with a default OS (or vice versa).

- Read RNS_APPLE_SIM_NAME / RNS_IOS_VERSION from env (explicit intent wins).
- Otherwise probe `simctl` for a booted, available simulator: prefer the
  default device/version, then fall back to any booted sim, then to defaults
  so Detox boots one when nothing is running.
- Guard the `simctl` probe on the active Detox config, read from the
  `--configuration` flag and DETOX_CONFIGURATION env var, with an argv-scan
  fallback for indirect invocations.
- Degrade gracefully to defaults if `xcrun simctl` is unavailable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the iOS E2E (Detox) device selection logic so the simulator device name and iOS runtime version are resolved together as a single target, and can optionally auto-detect a currently booted simulator for local runs.

Changes:

  • Introduces resolveSimulatorTarget() (with caching) to return a unified { name, os } target used by both resolveAppleSimulatorName() and getIOSVersion().
  • Adds simctl probing (xcrun simctl list devices booted --json) to detect booted simulators and prefer the default target when already booted.
  • Adds a Detox-configuration guard (via --configuration / DETOX_CONFIGURATION / argv scan) to only run simctl probing for iOS simulator configs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/e2e/ios-devices.js Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@LKuchno LKuchno changed the title feat(test, iOS): resolve Detox simulator target so device name and OS version stay in sync refactor(test, iOS): resolve Detox simulator target so device name and OS version stay in sync Jun 19, 2026
@kkafar

kkafar commented Jun 19, 2026

Copy link
Copy Markdown
Member

The desired order of simulator / emulator resolving (descending priority):

  1. User input,
  2. Already running instance of simulator / emulator,
  3. default hardcoded settings,
  4. first from the list of installed simulators / emulators.

If user specifies only a iPhone model w/o version - take the latest OS version if feasible. In case that is problematic to implement then we can fallback to some default OS version, but let's try first.

If user specifies only a SDK version (iOS / Android version) w/o device model then just select something with appropriate SDK from the top of the list - first match.

@LKuchno LKuchno marked this pull request as draft June 19, 2026 14:19
@LKuchno LKuchno added platform:ios Issue related to iOS part of the library type:e2e Actions related to e2e maintenance and development, especially automation. platform:js Issue related to the JS part of the library labels Jun 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment thread scripts/e2e/ios-devices.js
@LKuchno LKuchno marked this pull request as ready for review June 22, 2026 10:47
@LKuchno LKuchno requested a review from kkafar June 23, 2026 09:42

@kkafar kkafar left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems mostly fine. I have just a few questions.

Comment on lines +36 to +39
// Accept only "iOS <digits>(.<digits>)*" so typos fail here, not downstream.
if (/^iOS\s\d+(\.\d+)*$/.test(passedVersion)) {
return /** @type {`iOS ${string}`} */ (passedVersion);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we actually drop that iOS prefix? It doesn't bring any value and is annoying to type.

What do you think?

Comment on lines +51 to +61
function readDetoxConfigName() {
const fromEnv = process.env.DETOX_CONFIGURATION;
if (fromEnv) {
return fromEnv;
}
const flagIndex = process.argv.indexOf('--configuration');
if (flagIndex !== -1 && flagIndex + 1 < process.argv.length) {
return process.argv[flagIndex + 1];
}
return undefined;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What are the accepted values here? Can we document them or point the reader to the place where they are defined (our config file I assume).

Comment on lines +98 to +106
/**
* Queries `simctl` once (memoized) for created, available simulator instances.
*
* Only existing instances are returned: Detox finds devices via `applesimutils`,
* which matches existing simulators and never creates new ones from a device
* type + runtime pairing.
* @return {InstalledSimulator[]}
*/
function listInstalledSimulators() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do I read this correctly - we'll create a simulator from a device / SDK version pair?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform:ios Issue related to iOS part of the library platform:js Issue related to the JS part of the library type:e2e Actions related to e2e maintenance and development, especially automation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants