e2e-mobile-onboard

SkillDev tools

Interactive setup wizard for running mobile E2E (Detox) tests locally

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the e2e-mobile-onboard skill

What this skill tells your AI

The instructions your AI receives, as published by ledgerhq/ledger-live in .agents/skills/e2e-mobile-onboard/SKILL.md and read by ahel’s review.


description: Interactive setup wizard for running mobile E2E (Detox) tests locally

Mobile E2E Onboarding

You are an interactive setup wizard that checks the developer's machine and guides them through every prerequisite for running Ledger Live Mobile E2E tests (Detox) locally.

Work through each phase sequentially. For each check, run the shell command, report pass/fail, and if it fails, guide the user through the fix before moving on. Use TodoWrite to track progress across phases.

All commands assume the working directory is the repository root (ledger-live/). Before Phase 1, verify with git rev-parse --show-toplevel and cd there if needed.

CRITICAL SECURITY RULE: NEVER print, log, or display the value of SEED or any secret. Only confirm whether it is set or not.

DO NOT MODIFY TEST CODE: This command is strictly for environment setup and validation. Never create, edit, or delete any test file (specs, page objects, helpers, fixtures, or any file under e2e/).


Phase 0: Platform Selection

Before running any checks, ask the user which platform(s) they want to set up using the AskQuestion tool:

  • iOS only
  • Android only
  • Both

Store the selection and use it throughout all subsequent phases to skip irrelevant checks. For example:

  • iOS only: skip Java, Android SDK, Android emulator, ANDROID_HOME, JAVA_HOME checks
  • Android only: skip Xcode, Ruby, applesimutils, iOS Simulator checks

Phase 1: System Tools

Do NOT hardcode expected versions. Read the source-of-truth config files to determine what's required:

ToolSource of truthHow to read
Nodemise.tomlgrep "^node" mise.toml
pnpmmise.tomlgrep "^pnpm" mise.toml
Javaapps/ledger-live-mobile/android/build.gradleLook for JavaVersion.VERSION_XX in sourceCompatibility
Android SDK APIapps/ledger-live-mobile/android/build.gradleLook for compileSdkVersion
RubyNo pinned version in repoJust check ruby --version returns >= 3.0 (needed for CocoaPods)
XcodeNo pinned version in repoJust check xcodebuild -version returns a recent version
Misemise.tomlmise --version (see min_version at top of mise.toml)

First, read the config files to determine expected versions. Then run these checks (skip those not relevant to the selected platform):

Common checks (always run):

CheckCommandExpected
Dockerdocker --versionInstalled
Misemise --versionInstalled (required)
Nodenode --versionSee version policy below
pnpmpnpm --versionSee version policy below

Node / pnpm version policy:

The [tools] section in mise.toml is the source of truth for the recommended Node and pnpm versions. The check should:

  • PASS if the active version matches the pinned version exactly.
  • WARN if the active major version matches but the minor/patch differs (e.g. pinned 22.13.1, active 22.14.0). This is acceptable and must not block the setup or progression to the next phase.
  • FAIL only if the active major version is different or older than the pinned major version (e.g. pinned Node 22, active Node 20). Only FAIL results are hard blockers that must be resolved before proceeding.

The version is what matters, not how Node or pnpm were installed. If versions drift, suggest running mise install from the repo root (after installing mise).

iOS checks (skip if Android only):

CheckCommandExpected
Xcode CLI toolsxcode-select -vInstalled
Xcode versionxcodebuild -versionRecent stable version
Rubyruby --version>= 3.0 (any installation method is fine -- Homebrew, rbenv, asdf, system, etc.)
Bundlerbundle --versionInstalled (needed for CocoaPods in this repo)
applesimutilswhich applesimutilsInstalled

Ruby validation note: Check the version number returned by ruby --version, not the path. Do not fail because which ruby points to a non-Homebrew location. The real requirement is: Ruby >= 3.0 and bundle is available. Optionally also check bundle exec pod --version from apps/ledger-live-mobile to confirm CocoaPods works.

Android checks (skip if iOS only):

CheckCommandExpected
Javajava -versionMajor version matching build.gradle JavaVersion
Android SDKecho $ANDROID_HOMENon-empty, directory exists
Android emulator$ANDROID_HOME/emulator/emulator -list-avdsAt least one AVD
sdkmanager (optional)command -v sdkmanager or check known pathsAvailable (nice-to-have, not required for running tests)

sdkmanager path note: sdkmanager may be installed at $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager, under a Homebrew path like /opt/homebrew/share/android-commandlinetools/cmdline-tools/latest/bin/sdkmanager, or elsewhere on $PATH. Accept any of these. If it is not found at all, this is a warning, not a failure -- sdkmanager is only needed to install new SDK components, not to run tests.

Fix commands for common failures:

  • Xcode CLI: xcode-select --install
  • Ruby: install via your preferred method (Homebrew, rbenv, asdf, etc.) -- just ensure version >= 3.0
  • applesimutils: brew tap wix/brew && brew install applesimutils
  • Java: brew install openjdk@<version> (use the version from build.gradle)
  • Docker: Install Docker Desktop from https://www.docker.com/products/docker-desktop/
  • Mise: curl https://mise.run | sh then mise install from repo root
  • Android SDK / ANDROID_HOME: Add to ~/.zshrc:
    export JAVA_HOME=$(/usr/libexec/java_home)
    export ANDROID_HOME=$HOME/Library/Android/sdk
    export PATH=$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin
    

Do not proceed to Phase 2 until all relevant FAIL-level checks are resolved. WARN-only results (sdkmanager missing, Node/pnpm minor/patch drift versus mise.toml) should be surfaced but must not block progression.


Phase 2: Environment Variables

Check each variable is set and valid. Run [ -n "$VAR" ] && echo "set" || echo "NOT SET" for each.

Skip platform-irrelevant variables based on Phase 0 selection.

Common variables (always check):

VariableValidationDefault / Guidance
SEEDNon-emptyAsk the team for guidance
COINAPPSNon-empty AND directory exists ([ -d "$COINAPPS" ])Path to cloned coin-apps repo. If not cloned: git clone https://github.com/LedgerHQ/coin-apps.git
SPECULOS_IMAGE_TAGNon-emptyghcr.io/ledgerhq/speculos:latest
SPECULOS_DEVICENon-emptynanoX (options: nanoS, nanoSP, nanoX, stax, flex, nanoGen5)
MOCKEquals 00

Android variables (skip if iOS only):

VariableValidationDefault / Guidance
JAVA_HOMENon-empty AND directory exists$(/usr/libexec/java_home)
ANDROID_HOMENon-empty AND directory exists$HOME/Library/Android/sdk

For any missing variable (except SEED), provide the exact export line to add to ~/.zshrc.

Do not proceed to Phase 3 until all relevant checks pass.


Phase 3: Simulators / Emulators

The Detox config at e2e/mobile/detox.config.js requires exact device names. This is a config convention in this repo, not a Detox limitation. Read the config file to confirm the expected names.

Skip the platform not selected in Phase 0.

iOS Simulator (skip if Android only)

Run: xcrun simctl list devices available | grep "iOS Simulator"

  • Required name: read from e2e/mobile/detox.config.js under devices.simulator.device.name (currently iOS Simulator)
  • If missing, offer to auto-create it:
    xcrun simctl create "iOS Simulator" "iPhone 16" "iOS-18-2"
    
    Adjust the device type and runtime to match what's available. List available runtimes with xcrun simctl list runtimes and device types with xcrun simctl list devicetypes.
  • If auto-create fails or the user prefers manual setup:
    1. Open Xcode > Window > Devices and Simulators > Simulators
    2. Click +
    3. Name: iOS Simulator (must match config exactly)
    4. Device Type: iPhone 16 (or recent iPhone)
    5. iOS Version: recent stable
    6. Click Create

Android Emulator (skip if iOS only)

Run: $ANDROID_HOME/emulator/emulator -list-avds | grep Android_Emulator

  • Required AVD name: read from e2e/mobile/detox.config.js under devices.emulator.device.avdName (currently Android_Emulator)
  • If missing, offer to auto-create it (requires avdmanager on PATH or under $ANDROID_HOME/cmdline-tools/):
    avdmanager create avd -n Android_Emulator -k "system-images;android-36;google_apis;arm64-v8a" -d pixel_9_pro
    
    Adjust the system image tag to match the installed image. If avdmanager is not available, guide the manual route.
  • Manual setup:
    1. Open Android Studio > Tools > Device Manager
    2. Click Create Virtual Device
    3. Select a recent Pixel device
    4. System image: API level matching compileSdkVersion from build.gradle, google_apis variant (not google_apis_playstore)
    5. AVD Name: Android_Emulator (must match config exactly)
    6. Click Finish

Architecture and CI check (Android only)

Detect machine architecture:

Run: uname -m

  • arm64 (Apple Silicon): expected ABI is arm64-v8a
  • x86_64 (Intel): expected ABI is x86_64

Cross-check the emulator ABI (if emulator is running):

Run: adb shell getprop ro.product.cpu.abi

  • Confirm the emulator ABI matches the machine architecture.

Check for ambient CI variable:

Run: echo "CI=${CI:-<unset>}"

This is critical: e2e/mobile/detox.config.js uses CI to choose the Android ABI (x86_64 when CI is set, arm64-v8a otherwise). If CI is inherited from a parent process, IDE, or wrapper shell, local Apple Silicon builds will silently produce x86_64 APKs that fail on an arm64-v8a emulator.

  • If the machine is arm64 and CI is set: warn the user explicitly. Explain that the Android build and test commands in Phase 4 and 5 will be prefixed with CI= (unset) to force the correct ABI. If CI=1 is in their ~/.zshrc, recommend removing it or scoping it only to CI scripts.
  • If the machine is x86_64 and CI is not set: warn that CI=1 is needed for correct ABI. Suggest export CI=1.

Also check these Android SDK components are installed:

  • $ANDROID_HOME/build-tools/ exists
  • $ANDROID_HOME/platform-tools/adb exists

If missing, guide: Android Studio > Tools > SDK Manager > SDK Tools tab > install Build Tools, Platform Tools.

Emulator free space (Android only)

If the Android emulator is already running, check available storage:

Run: adb shell df -h /data and adb shell du -sh /data/local/tmp

Thresholds:

  • < 1 GB free: fail -- Detox install will likely fail with "not enough space"
  • 1 - 1.5 GB free: warn -- tight, may cause issues during test runs
  • > 1.5 GB free: pass

If space is low, offer these cleanup actions:

  1. adb shell pm uninstall com.ledger.live.detox and adb shell pm uninstall com.ledger.live.detox.test
  2. adb shell rm -rf /data/local/tmp/*
  3. adb shell pm trim-caches 4G
  4. If still tight, suggest wiping AVD data: Android Studio > Device Manager > Wipe Data, or recreate the emulator with a larger data partition.

If the emulator is not yet running, skip this check and run it before the smoke test in Phase 5 instead.

Do not proceed to Phase 4 until all relevant checks pass.


Phase 4: Install Dependencies and Build

Run sequentially (check exit codes between each step):

4a. Install dependencies

pnpm i --filter="live-mobile..." --filter="ledger-live" --filter="live-cli..." --filter="ledger-live-mobile-e2e-tests"

4b. Build lib dependencies

pnpm build:llm:deps

4c. Build CLI

pnpm build:cli

4d. Platform-specific build

iOS (if selected in Phase 0) -- uses debug build:

pnpm mobile pod
pnpm mobile e2e:build -c ios.sim.debug

Android (if selected in Phase 0) -- uses release build:

On Apple Silicon (arm64), explicitly clear CI to ensure the correct ABI:

CI= pnpm mobile e2e:build -c android.emu.release

On Intel (x86_64), set CI=1:

CI=1 pnpm mobile e2e:build -c android.emu.release

Why release for Android? Android debug builds are broken locally due to a known Detox/Espresso reflection bug (NoSuchFieldException: eventInjector). This is a project-level debug dependency issue, not a machine setup problem. Only release builds work. Release bundles JS into the APK, so no Metro bundler is needed for Android.

If any step fails, report the error and help the user fix it before continuing.

Common build failures:

  • iOS build fail: rm -rf apps/ledger-live-mobile/ios/build then pnpm mobile pod then retry
  • iOS pod install flaky error (ArgumentError - pathname contains null byte): this is a known intermittent issue with CocoaPods in pnpm monorepos. If the first pnpm mobile pod succeeded, avoid forcing another pod regeneration. If it keeps failing, try cd apps/ledger-live-mobile/ios && rm -rf Pods Podfile.lock && bundle exec pod install && cd -.
  • iOS build appears hung: if the .app artifact already exists under apps/ledger-live-mobile/ios/build/Build/Products/, the build likely succeeded even if the terminal did not show a clean exit. Check for the artifact before retrying.
  • iOS build fails on native compilation errors (e.g. Xcode / React Native compatibility): this is a project-level issue, not a machine setup problem. Report it as such.
  • Android build fail: cd apps/ledger-live-mobile/android && ./gradlew clean && cd - then retry
  • OOM: prefix command with NODE_OPTIONS="--max-old-space-size=10240"

Phase 5: Smoke Test

Run a single quick test to validate the full setup. Only test the platform(s) selected in Phase 0.

iOS smoke test (skip if Android only)

iOS uses a debug build, which requires the Metro bundler running.

Terminal 1 -- Start the bundler:

pnpm mobile start

Terminal 2 -- Run the smoke test:

cd e2e/mobile && pnpm test:ios:debug userOpensApplication.spec.ts

Android smoke test (skip if iOS only)

Android uses a release build. JS is bundled in the APK -- no Metro bundler or port bridging needed.

Make sure the emulator is running (adb devices should list a device), then run:

cd e2e/mobile && pnpm test:android userOpensApplication.spec.ts

Both platforms

If both were selected, start the bundler first (for iOS), run the iOS test, then run the Android test. The Android test does not depend on the bundler.

Report whether each test passed or failed. If it failed, check the error output and help debug.

Common smoke test failures:

  • "Element not found": the app may not have loaded yet -- could be a build issue, or an optional onboarding prompt (e.g. analytics opt-in) that the test does not expect. See "Setup vs flow mismatch" below.
  • "Cannot find bundled JS" (iOS only): bundler not running
  • Simulator/emulator name mismatch: re-check Phase 3
  • NoSuchFieldException: eventInjector (Android debug only): this is a project-level Detox/Espresso dependency mismatch, not a missing SDK or emulator issue. Do not keep troubleshooting the machine. Switch to release configuration (pnpm test:android instead of pnpm test:android:debug).
  • Requested internal only, but not enough space (Android): the emulator's /data partition is full. See "Emulator free space" in Phase 3.

Setup vs flow mismatch:

If the app launches and connects to Detox successfully but the test fails on a specific UI element (e.g. a button not found after an unexpected screen appears), the problem is likely a test-flow mismatch, not a broken setup. The smoke test may assume a specific onboarding path that has changed (e.g. a new analytics prompt was added). In this case:

  • Confirm the setup is valid (app launched, Detox connected).
  • Report that the failure appears to be a test scenario issue, not an environment issue.
  • Suggest the user check the test expectations against the current app flow.

Non-blocking warnings to ignore:

  • Watchman recrawl warnings (e.g. "Recrawled this watch...") are noisy but non-blocking. They do not cause test failures. Optionally suggest watchman watch-del-all && watchman shutdown-server to clean up, but do not present Watchman warnings as the reason a test failed.

Phase 6: Summary

Print a final checklist summarizing everything. Only include rows for checks that were actually run (based on platform selection). Mark skipped platforms as "skipped".

## E2E Mobile Setup -- Complete

| Check                  | Status            |
| ---------------------- | ----------------- |
| Xcode & CLI tools      | ✅ / ❌ / skipped |
| Ruby                   | ✅ / ❌ / skipped |
| applesimutils          | ✅ / ❌ / skipped |
| Java                   | ✅ / ❌ / skipped |
| Android SDK            | ✅ / ❌ / skipped |
| Docker                 | ✅ / ❌           |
| Mise / Node / pnpm    | ✅ / ❌           |
| Environment variables  | ✅ / ❌           |
| iOS Simulator          | ✅ / ❌ / skipped |
| Android Emulator       | ✅ / ❌ / skipped |
| Dependencies installed | ✅ / ❌           |
| App built              | ✅ / ❌           |
| Smoke test             | ✅ / ❌           |

If everything passed, confirm the user is ready to run tests with:

You're all set! Run tests from e2e/mobile/:

  # iOS (debug -- requires bundler running via `pnpm mobile start`)
  pnpm test:ios:debug <testFile.spec.ts>

  # Android (release -- no bundler needed)
  pnpm test:android <testFile.spec.ts>

  # All tests (iOS debug / Android release)
  pnpm test:ios:debug
  pnpm test:android

Note: Android debug builds (pnpm test:android:debug) do not work locally due to a known Detox/Espresso bug. Always use the release configuration for Android.

If anything failed, list the remaining issues and offer to help fix them.

Signals

GitHub stars
618
Forks
490
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
e2e-mobile-onboard
Source
github.com/ledgerhq/ledger-live