refactor(test, iOS): resolve Detox simulator target so device name and OS version stay in sync#4195
refactor(test, iOS): resolve Detox simulator target so device name and OS version stay in sync#4195LKuchno wants to merge 7 commits into
Conversation
…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>
There was a problem hiding this comment.
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 bothresolveAppleSimulatorName()andgetIOSVersion(). - Adds
simctlprobing (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 runsimctlprobing for iOS simulator configs.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
The desired order of simulator / emulator resolving (descending priority):
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. |
…ill be used for script run without arguments
kkafar
left a comment
There was a problem hiding this comment.
Seems mostly fine. I have just a few questions.
| // Accept only "iOS <digits>(.<digits>)*" so typos fail here, not downstream. | ||
| if (/^iOS\s\d+(\.\d+)*$/.test(passedVersion)) { | ||
| return /** @type {`iOS ${string}`} */ (passedVersion); | ||
| } |
There was a problem hiding this comment.
Can we actually drop that iOS prefix? It doesn't bring any value and is annoying to type.
What do you think?
| 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; | ||
| } |
There was a problem hiding this comment.
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).
| /** | ||
| * 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() { |
There was a problem hiding this comment.
Do I read this correctly - we'll create a simulator from a device / SDK version pair?
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 chosenhttps://github.com/software-mansion/react-native-screens-labs/discussions/572
Changelog
scripts/e2e/ios-devices.js:resolveSimulatorTarget(), returning a single{ name, os }so the device name and iOS version can never come from different simulators.resolveAppleSimulatorName()andgetIOSVersion()now derive from it. The resolved target is memoized for the process lifetime.RNS_APPLE_SIM_NAME/RNS_IOS_VERSIONenv vars. Either may be given on its own:name+osinstance is validated against the installed simulators. If the combination doesn't exist — e.g.iPhone 15withiOS 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/applesimutilsfail later. (Whensimctlreturns nothing — failed or no simulators installed — validation can't run, so the script warns and returns the pair verbatim, letting Detox surface any error.)simctlprobing —listInstalledSimulators()queriesxcrun 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 throughapplesimutilsand does not create them from a device-type + runtime pairing. Degrades gracefully to defaults (with a warning) ifxcrunis unavailable.simctlprobe only runs for iOS simulator configurations. The active Detox config is read from the--configurationflag and theDETOX_CONFIGURATIONenv var, with an argv-scan fallback for indirect invocations.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:
- 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.