Skip to content

LLM:End to end testing

Bruno Cunha edited this page Dec 11, 2025 · 14 revisions

Ledger Live Mobile end-to-end testing

This project uses Detox and Jest for end-to-end testing the LLM application. Detox is a mobile end-to-end testing tool developed by Wix, and is specifically built for React Native applications. Please refer to the documentation of those projects for the specifics. In this readme you will find the key setup and workflow steps for testing a flow in Ledger Live Mobile.

Overview

Welcome! This guide covers the Ledger Live Mobile End-to-End (E2E) testing framework. We'll cover everything from setting up your environment to writing, running, and debugging tests for both iOS and Android.

The framework is built on a modern tech stack designed for our monorepo environment, ensuring our mobile application is robust and reliable.

Key Technologies 🛠️

  • Detox: The core E2E testing framework
  • Jest: The test runner that executes our test files
  • TypeScript: For writing clean, type-safe tests
  • Allure: For creating beautiful, interactive test reports
  • Speculos: A simulator for Ledger hardware wallets, allowing us to test device interactions
  • pnpm + Turborepo: For managing our monorepo and orchestrating complex build processes efficiently

Prerequisites & Setup

Let's get your machine ready for testing.

System Requirements

  • macOS (required for iOS development and testing)
  • Homebrew: The missing package manager for macOS. Install here.
  • Xcode: Version 16.2 or higher. Install from the Mac App Store and run xcode-select --install in your terminal.
  • Android Studio: Latest stable release. Install here. Follow the setup wizard to install the SDK and create an Android Virtual Device (AVD). A Pixel 6 with API 33 is recommended.
  • Docker Desktop: For running containers, especially useful for Speculos. Install here.

Environment Setup

1. Clone the Monorepo

First, get the code from our repository.

git clone https://github.com/LedgerHQ/ledger-live.git
cd ledger-live

2. Install Proto & Project Tools

We use Proto to manage our toolchain (Node.js, pnpm, etc.). This ensures everyone uses the exact same versions, preventing "it works on my machine" issues. (❗ Make sure you are using Node from proto tool, if you have Node also from other tools like nvm or asdf, it is recommended to remove it or adjust your environment to use Node from proto)

proto use

3. Install Ruby (for iOS)

CocoaPods, an iOS dependency manager, requires Ruby.

# Install a specific Ruby version
brew install ruby@3.3

# Add Ruby to your shell's PATH. Add this to your ~/.zshrc or ~/.bash_profile
if [ -d "/opt/homebrew/opt/ruby@3.3/bin" ]; then
  export PATH=/opt/homebrew/opt/ruby@3.3/bin:$PATH
  export PATH=`gem environment gemdir`/bin:$PATH
fi

# Reload your shell config
source ~/.zshrc

# Install required gems
gem install bundler:2.5.7
gem install cocoapods

Make sure to also clone Coin apps repo:

git clone https://github.com/LedgerHQ/coin-apps.git

4. Install Project Dependencies

This command uses pnpm filters to install only the packages necessary for mobile E2E testing.

# Optional but need when a full clean needs to be done
rm -rf ios/build
rm -rf node_modules
pnpm clean
pnpm store prune

pnpm i --filter="live-mobile..." --filter="ledger-live" --filter="@ledgerhq/dummy-*-app..." --filter="live-cli..." --filter="ledger-live-mobile-e2e-tests"
pnpm build:llm:deps
pnpm build:cli

5. Set Environment Variables

These variables are crucial for running tests, especially for hardware wallet simulation.

# Set these in your terminal for the current session
export COINAPPS="/path/to/your/coin-apps"
export MOCK="0"
export SEED="your 24 word ledger recovery phrase here"
export SPECULOS_IMAGE_TAG="ghcr.io/ledgerhq/speculos:master"
export SPECULOS_DEVICE="nanoX" #Device under test
export SPECULOS_FIRMWARE_VERSION="2.5.1" #Device's firmware version

⚠️ Important:

  • Replace the placeholder COINAPPS path and SEED phrase.
  • Your SEED phrase is sensitive. Keep it secure and never commit it to version control.
  • To make these variables permanent, add them to your shell profile (~/.zshrc or ~/.bash_profile).

Device Setup

To get started ensure you have your device setup.

Android Emulator Setup

You will need to manually create an Android Emulator called Android_Emulator.

  1. Open Android Studio.
  2. Go to Tools → AVD Manager.
  3. Click Create Virtual Device.
  4. Choose device (e.g., Pixel 7).
  5. Select system image (API 34 recommended).
  6. Name it Android_Emulator.
  7. Click Finish.

iOS Simulator Setup

You will need to manually create an iOS Simulator called iOS Simulator.

  1. Open Simulator:

    • Go to Xcode → Open Developer Tool → Simulator.
    • Alternatively, use: open -a Simulator.
  2. Create New Simulator:

    • In Simulator, go to Device → Manage Devices and Simulators.
    • Click the + button.
    • Enter "iOS Simulator" for the Name (exact name required).
    • Select "iPhone 15" (or any recent iPhone) for Device Type.
    • Choose "iOS 17.0 or later" for iOS Version.
    • Click Create.

Quick start script to install iOS and Android debug versions from scratch

Ensure you have Android Studio and Xcode installed with the relevant development/emulator tools installed (see 'Local Environment Setup' above).

The following script will ensure a clean local environment for running detox tests in debug mode.

pnpm clean
pnpm i --filter="live-mobile..." --filter="ledger-live" --no-frozen-lockfile --unsafe-perm
pnpm mobile pod
pnpm build:llm:deps
pnpm mobile e2e:build -c android.emu.debug
pnpm mobile e2e:build -c ios.sim.debug

It's also a good idea to build the Dummy Live Apps as they are used in the Live SDK and Wallet API E2E tests for both LLM and LLD: pnpm test-utils dummy-apps:install && pnpm test-utils dummy-apps:build


Local Environment Setup

Writing and running Detox tests requires Xcode for iOS and Android Studio (along with the SDK and emulator tools) for Android. The best place to setup both Android and iOS is to follow the React Native's own documentation.

Next, follow the steps in the Detox Environment Setup section.

Prerequisites for all Detox tests:

  • Node is installed (currently we use v16)

Tips for iOS setup

Most of the setup is taken care of in the React Native docs, but you will have to do some additional installations, such as the Detox CLI and applesimutils (MacOS only). After following the above React Native and Detox steps, you should have the following setup:

  • XCode and XCode command line tools - run xcode-select -v and xcrun --version to make sure these are working
  • rbenv is installed and which ruby points to an rbenv shim, not usr/bin/ruby. Be sure to follow the steps in the RN guide to add rbenv to your shell profile.
  • An iPhone simulator for iPhone 13 - open Xcode > Window > Devices and Simulators > Simulators > Add a new device from the '+' sign in the bottom right corner.
  • applesimutils is installed. ( https://github.com/wix/AppleSimulatorUtils )

Tips for Android setup

The Android toolkit can be more complex than the iOS one. Once you've done the React Native and Detox setup steps, follow the Detox Android Environment Setup guide for further steps. The main things to make sure of are:

  • Java version 11 installed. Check with java -version
  • Android 12.0 (API Level 11) is installed.
  • Android SDK Build Tools, SDK Platform Tools, SDK Command Line Tools, Android Emulator, CMake 3.10.2 and NDK 21.4.7075529 are installed. You can do this through Android Studio > Tools > SDK Tools, or via the command line.
  • Your shell profile (for example ~/.zshrc) should have environmental variables setup something like this:
export JAVA_HOME=`/usr/libexec/java_home`
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/tools/bin/sdkmanager:$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin

Note: There is a bit of inconsistency in the documentation between React Native, Detox and Android themselves about whether to use ANDROID_ROOT or ANDROID_SDK_ROOT. The second one is now deprecated but both should work in either case.


Project Structure

The mobile E2E tests live in the apps/ledger-live-mobile/e2e directory. Here's a look at the key folders:

e2e/
├── specs/              # Test files (the tests themselves)
├── bridge/              # Proxy and server for test-speculos & test-simulator/emulator communication
├── page/               # Page Object Models (reusable UI components)
├── helpers/            # Reusable helper functions (e.g., wallet interactions)
├── artifacts/          # Output folder for screenshots, videos, and reports
├── detox.config.js     # Detox configuration for different environments
└── jest.config.ts      # Jest test runner configuration

Most files for the tests are in the /e2e/mobile directory.

  • /bridge: This contains the code to setup a websocket which allows communication between the test process and the LLM app. This allows us to:

    • create different conditions for testing the app by setting the user data.
    • perform mock device actions.
    • do quick navigations around the app (useful for setting up tests).
  • /models: The models contain logic for interacting with elements on specific pages in the application. They roughly follow the Page Object Model that is standard in UI testing.

  • /userdata: This is the application data that will be used to start the session, it contains all the information regarding user settings, existing accounts, operations, balances, etc. It allows us to test different scenarios with independent configurations.

  • /specs: The test suites themselves. We make use of the helpers and combine the snippets from the flows to build the different test scenarios. Ideally we should be able to reuse parts from flows in the specs.

  • /jest.config.ts: Configuration for Detox. Contains settings like what the setup and teardown files are, how long the timeout is, what test runner to use, etc.

  • /helpers: Convenience methods for use in the models/tests to make writing tests easier.

  • /setup.ts: Run after the global setup. It starts the websocket bridge, sets up the emulators to be more consistent in the test run (for example sets the time to 12.00), and shuts down the websocket bridge. Any logic to be run before and after a test run would go here.

Other important files outside /e2e/mobile

  • e2e/mobile/detox.config.js: Contains the configurations for the emulators when running detox test and the build artifacts to be used when running detox build

  • .github/workflows/test-mobile.yml: The workflow file to kick off tests in the Github servers.


Building the App

Before running tests, you need to build a version of the Ledger Live app that Detox can interact with.

Build Commands

Navigate to the mobile E2E directory first:

cd e2e/mobile/

Run one of the following commands to build the app for testing:

Build for iOS (Release):

NODE_OPTIONS="--max-old-space-size=10240" pnpm run build:ios

Build for Android (Release):

NODE_OPTIONS="--max-old-space-size=10240" pnpm run build:android

Debug Builds: For development, you can create debug builds, which are faster to compile.

# iOS Debug
pnpm run build:ios:debug

# Android Debug
pnpm run build:android:debug

Note: Builds can be memory-intensive. The scripts are pre-configured to allocate sufficient memory.


Test Setup and Execution

Clean your local environment to remove node_modules and previous iOS and Android mobile app builds:

pnpm clean

Install dependencies:

pnpm i

There is a filtered version of this command which should be quicker and includes only dependencies needed for LLM: pnpm i --filter="live-mobile..." --filter="ledger-live".

Build dependencies for the mobile app:

pnpm build:llm:deps

Android Tests

Verify you have an emulator installed and have that match the Detox avdName (currently 'Android_Emulator') in the e2e/mobile/detox.config.js file. Be sure to make the device the correct architecture and system image. Currently this is x86_64 if you are on an Intel mac and arm64_v8a if you are on an M1 Mac (*info required for Windows and Linux*). If you are on an Intel Mac, you must run export CI=1 in the terminal session before

  • Build the apps
    • Debug: pnpm mobile e2e:build -c android.emu.debug
    • Release: pnpm mobile e2e:build -c android.emu.release
  • Run the tests
    • Debug: First, run pnpm mobile start to run Metro bundler, then in a separate terminal window run pnpm mobile e2e:test -c android.emu.debug. When developing locally, you may need to put the content of the .env.mock file in the app .env file to have the right test environment.
    • Release: pnpm mobile e2e:test -c android.emu.release

If you get an error for Android debug tests complaining that the emulator cannot find the bundled JS script, run adb reverse tcp:8081 tcp:8081 before starting the tests (but make sure the emulator is already started). This makes it possible for the emulator to access the Metro bundler on your local machine.

iOS Tests

Make sure you have the correct iPhone simulator that is listed in e2e/mobile/detox.config.js installed (currently 'iOS Simulator'). You can check if you do with applesimutils --list. Also make sure you have an iOS version installed for simulators by going to Xcode > Preferences > Components. You can try whichever version you like, but iOS 13.0 is known to work locally.

  • Build the apps
    • Debug: pnpm mobile e2e:build -c ios.sim.debug
    • Release: pnpm mobile e2e:build -c ios.sim.release
  • Run the tests
    • Debug: First, run pnpm mobile start to run Metro bundler, then in a separate terminal window run pnpm mobile e2e:test -c ios.sim.debug
    • Release: pnpm mobile e2e:test -c ios.sim.release

Running Tests

Once the app is built, you can run the tests.

Basic Commands

Make sure you are in the e2e/mobile/ directory.

Run all tests on iOS:

pnpm run test:ios

Run all tests on Android:

pnpm run test:android

Running Specific Tests

You can run a single file or tests matching a pattern, which is great for development. Make sure you are in the e2e/mobile/ directory.

Run a single test file:

pnpm run test:ios ledgerSync.spec.ts

Run tests with a name matching a pattern:

# This uses Jest's to filter tests with name swap
pnpm run test:android swap

CI & Advanced Options

Detox provides flags to control execution and artifacts, which are especially useful for CI pipelines.

# Recommended for CI: only log errors and record artifacts for failed tests
pnpm run test:ios --loglevel error --record-logs failing --record-videos failing --take-screenshots failing portfolio.spec.ts 

# Run tests in parallel to save time (adjust workers based on your machine's power)
pnpm run test:ios --workers 2 ledgerSync.spec.ts 

Writing Tests

We follow the Page Object Model (POM) pattern to keep our tests clean, readable, and maintainable.

Test File Structure

A typical test file looks like this:

// specs/myFeature.spec.ts  
describe("My Feature", () => {  

  beforeAll(async () => {  
    // Code here runs once before any test in this file.  
    // Perfect for setting up initial state (e.g., loading a wallet).  
  });  

  it("should do something amazing", async () => {  
    // 1. Arrange: Navigate or set up a specific state.  
    await app.portfolioPage.navigateToSettings();  

    // 2. Act: Perform an action.  
    await app.portfolioPage.toggleDarkMode();  

    // 3. Assert: Verify the outcome.  
    await expect(element(by.id('dark-mode-element'))).toBeVisible();  
  });  
});

Page Object Model (POM)

Instead of putting element selectors (by.id(...)) directly in tests, we abstract them into Page Objects. This makes tests much easier to read and update.

Example Page Object:

// page/settings.page.ts
export default class SettingsPage {
  darkModeSwitch = () => getElementById("settings-dark-mode-switch");
  generalSettingsButton = () => getElementById("settings-general-button");

  @Step("Navigate to general settings")
  async navigateToGeneralSettings() {
    await tapById(this.generalSettingsButton());
  }

  @Step("Expect that dark mode switch is visible")
  async expectDarkModeSwitchIsVisible() {
    await expect(this.darkModeSwitch()).toBeVisible();
  }
}

Development workflow

The workflow for adding new tests is similar to the desktop workflow. These are:

Step 1: Identify Elements

Detox has a simpler API than Playwright and other E2E test solutions like Appium and Webdriver. The easiest way to make elements usable by Detox is by adding a testId attribute to the element in the code. Make sure to put the testId at the lowest level possible in the DOM tree.

Ideally these are placed at development time so tests are easier to write in future.

For example:

<BottomDrawer
  testId="AddAccountsModal"
  isOpen={isOpened}
  onClose={onClose}
  title={t("portfolio.emptyState.addAccounts.addAccounts")}
>

Step 2: Create a page object

Page objects are methods that group together behaviours so that tests are more readable and correspond to actions users would take in the app.

To create them:

  • Use the existing helper methods in e2e/mobile/helpers for actions such as clicking, entering text...
  • Create a new .ts step file in the e2e/mobile/models directory. Make sure it is named logically.
  • Start creating methods using the following pattern:
import { getElementByText, tapByElement, /* ... */ } from "path/to/helpers";

class MyPageObjectModel {
  getSomeItemByText = () => getElementByText("Set up my Ledger");
  getSomeItemById = () => getElementById("continue");
}

async chooseToSetupLedger() {
  await tapByElement(this.getSomeItemByText());
  await tapByElement(this.getSomeItemById());
}

Step 3: Create a test file

Test files go in the e2e/mobile/specs directory. Import the relevant page object model files and follow the example to create new tests:

import { expect, waitFor /* ... */ } from "detox";
import OnboardingSteps from "../models/onboarding/onboardingSteps";
import PortfolioPage from "../models/portfolioPage";

let onboardingSteps: OnboardingSteps;
let portfolioPage: PortfolioPage;
describe("Onboarding", () => {
  beforeAll(async () => {
    // Load some configs and setup your pages here
    await loadConfig("1AccountBTC1AccountETH", true);
    onboardingSteps = new OnboardingSteps();
    onboardingSteps = new PortfolioPage();
  })

  it("onboarding step should be visible", async () => {
     // test assertions
    await expect(onboardingSteps.getSomeElement()).toBeVisible();
  });

  it("should be able to start onboarding", async () => {
    // test actions (tap on some element)
    await onboardingSteps.startOnboarding();
  });

  it("should do some other stuffs", async () => {
    await onboardingSteps.DoIOwnDevice(true);
    // ...
  })

Debugging

Things will break. Here's how to figure out why.

Key Debugging Flags

You can add these flags to any detox test command.

  • --loglevel <level>: Controls how much Detox logs. Use info or debug for more detail.
  • --take-screenshots <when>: Takes screenshots. Use failing (default) or all.
  • --record-videos <when>: Records a video of the test run. Use failing (default) or all.
  • --record-logs <when>: Saves device logs. Use failing (default) or all.

Pro Tip: For active debugging, use a command that captures everything.

detox test --configuration ios.sim.debug --loglevel info --record-logs all --record-videos all --take-screenshots all

Debugging Techniques

Pause Test Execution: You can pause the test at any point to inspect the UI manually. Simply add await new Promise(resolve => setTimeout(resolve, 10000)); to pause for 10 seconds.

Generate view XML:

import { device } from "detox"

const xml = await device.generateViewHierarchyXml();
console.log(xml);

This script generates a view hierarchy XML of the currently opened application and prints it to the console. This is helpful to see what elements and testIDs are available/visible.

Devtools: You can inspect the app using these steps:

  1. Start the Metro Server by running this command in a terminal window: pnpm mobile start
  2. Run the test with a debug version of the simulator/emulator in another terminal window
  3. Click j in the metro server's terminal window
  4. Select the debug target

This would open the devtools as a new window, there you can:

  • Navigate through the DOM
  • Hover over elements in the devtools to find them in the simulator/emulator
  • Select an element to see its attributes

Other way to start devtools:

  • Open the dev menu by clicking d in the metro server's terminal window, then select the devtools from the simulator/emulator.

Notes:

  • The devtools loads the entire DOM, which means that the elements of the current page you're in (in the simulator/emulator) are within the last elements in the devtools.
  • Test ids are usually attributed to <Text>, <View> elements. You can use the search bar to look for specific elements within the DOM.

Take Manual Screenshots: Get a snapshot of the screen at a specific moment.

await device.takeScreenshot("my-debug-screenshot");

Check for Elements: The most common issue is an element not being found. Add explicit waits to ensure the UI is ready.

await waitFor(element(by.id('my-button'))).toBeVisible().withTimeout(5000);

Reporting & Artifacts

After a test run, all generated output (screenshots, videos, logs) is saved in the e2e/mobile/artifacts directory.

Allure Reports

We use Allure to generate a web-based report that visualizes the test results.

How to Generate a Report:

  1. First, ensure your tests have run and produced results.
  2. Run the generate command:
#!/bin/bash
brew install allure
allure --version

allure generate e2e/mobile/artifacts --clean
  1. Open the report in your browser:
#!/bin/bash
# Open Allure report
allure open allure-report

The report provides a detailed breakdown of suites, tests, steps, failures, and execution trends over time.


Best Practices

Follow these guidelines to write effective and maintainable tests.

  • Do use the Page Object Model (POM): Abstract UI logic away from test logic.
  • Do write independent tests: Each test should be able to run on its own without relying on the state from a previous test. Use beforeEach for setup and afterEach for cleanup.
  • Do use explicit waits: Don't rely on fixed delays (sleep). Wait for elements to appear or for conditions to be met.
  • Don't put logic in your tests: Tests should be simple sequences of actions and assertions. Complex logic, loops, or conditionals belong in helper or page object methods.
  • Do name things clearly: A test named it("should show an error on invalid password") is much better than it("login test 2").

Troubleshooting

Stuck? Here are some common problems and their solutions.

Problem Solution
Build Fails 1. rm -rf apps/ledger-live-mobile/ios/build (for iOS) or ./gradlew clean in the android folder.
2. Re-install pods: cd apps/ledger-live-mobile && pnpm mobile pod.
3. Re-run the build command.
Environment Variable Not Set Tests fail immediately. Run echo $SEED to check if your variables are loaded. If not, export them again or add them to your ~/.zshrc.
"Element not found" The UI may be slower than your test. Add a waitFor statement to ensure the element is visible before interacting with it. Also, verify the testID in the application source code is correct.
Simulator/Emulator Issues Sometimes the simulator gets into a bad state.
- iOS: In the Simulator menu, go to Device > Erase All Content and Settings.
- Android: In the AVD Manager, wipe the data of your virtual device.

CI

⚠️ Android and iOS tests are currently switched off on the CI for PRs due to issues installing the app on the emulators and general flakiness with the runners. However the tests are running at midday and midnight daily

Triggering Release Runs from GitHub Actions

You can trigger mobile E2E tests manually using the GitHub Actions workflow. This is useful for running tests on specific branches, with different configurations, or for targeted testing scenarios.

Workflow Location

.github/workflows/test-mobile-e2e-reusable.yml

This is a reusable workflow designed to be called from another workflow via uses:.

How to Trigger

  1. Go to the GitHub Actions page for the ledger-live repository
  2. Select the "[Mobile] E2E Only - Scheduled/Manual" workflow
  3. Click "Run workflow"
  4. Configure the inputs as needed
  5. Click "Run workflow" to start the execution

Workflow Inputs

Field Type Default Description
ref String (empty) The branch which triggered this workflow
test_filter String (empty) Filter to execute only test suite spec files according to pattern(s) (e.g. to execute nftGallery.spec.ts and deeplinks.spec.ts files "nftGallery deeplinks")
tests_type Choice iOS & Android Which tests to run: Android Only, iOS Only, or iOS & Android
speculos_device Choice nanoX Speculos device to use: nanoS, nanoSP, or nanoX
production_firebase Boolean false Target Firebase Production env
enable_broadcast Boolean false Enable transaction broadcast
export_to_xray Boolean false Send results to Xray
test_execution_android String (empty) Xray execution key for Android (optional)
test_execution_ios String (empty) Xray execution key for iOS (optional)

Example Invocation

# .github/workflows/ci-mobile.yml
name: CI – Mobile E2E

on:
  workflow_dispatch:

jobs:
  e2e:
    uses: LedgerHQ/ledger-live/.github/workflows/test-mobile-e2e-reusable.yml@main
    with:
      ref: develop
      test_filter: "nftGallery deeplinks"  # leave empty or specify test files
      tests_type: 'iOS & Android'
      speculos_device: 'nanoX'
      production_firebase: true
      enable_broadcast: true
      export_to_xray: true
      test_execution_android: 'B2CQA-2461'
      test_execution_ios: 'B2CQA-2462'

Detailed Field Reference

ref

The branch which triggered this workflow. Leave empty to use the current branch.

test_filter

A space-separated list of test file patterns to filter which tests to run. For example:

  • "nftGallery" - runs only nftGallery.spec.ts
  • "nftGallery deeplinks" - runs both nftGallery.spec.ts and deeplinks.spec.ts
  • Leave empty to run all tests

tests_type

Which platform(s) to target:

  • Android Only - runs tests only on Android
  • iOS Only - runs tests only on iOS
  • iOS & Android - runs tests on both platforms

speculos_device

The Speculos emulated Ledger device to use for testing:

  • nanoS - Ledger Nano S
  • nanoSP - Ledger Nano S Plus
  • nanoX - Ledger Nano X

production_firebase

When true, targets the Firebase Production environment instead of staging.

enable_broadcast

When true, enables transaction broadcasting during tests. This allows real transactions to be sent to the blockchain.

export_to_xray

When true, test results (pass/fail + artifacts) are posted to Xray (JIRA) for tracking.

test_execution_android / test_execution_ios

Provide your Xray execution keys so we can group Android/iOS results under the right test cycle in JIRA.

Test Sharding

The workflow automatically generates test shards to optimize execution time:

  • Manual runs (workflow_dispatch): Up to 3 shards (15 tests per shard)
  • Release branches: 6 shards
  • Scheduled runs: 12 shards

The sharding is handled by the generate-shards-matrix action which:

  1. Scans the e2e/mobile directory for test files
  2. Applies the test_filter if specified
  3. Calculates the optimal number of shards based on the event type
  4. Distributes tests across shards for parallel execution

Generate Shards Matrix Action

The generate-shards-matrix action is a composite action located at tools/actions/composites/generate-shards-matrix/action.yml that dynamically creates test execution matrices for parallel test runs.

Purpose

This action optimizes E2E test execution by:

  • Reducing execution time: Tests are distributed across multiple parallel runners
  • Improving resource utilization: Better use of available CI resources
  • Enabling targeted testing: Support for filtering specific test suites
  • Adaptive sharding: Different shard counts based on event type and branch

How It Works

  1. Test File Discovery:

    • Scans the e2e/mobile directory for .spec.ts files
    • Uses the shard-tests.js script to identify test files
    • Applies the test_filter parameter to include only matching tests
  2. Shard Calculation:

    • Counts the total number of test files
    • Calculates optimal shard count based on event type:
      • workflow_dispatch (manual): min(3, ceil(test_count / 15))
      • Release branches: 6 shards
      • Scheduled runs: 12 shards

    Resource Management Strategy: For manual runs (workflow_dispatch), the shard calculation uses min(3, ceil(test_count / 15)) to optimize resource utilization, especially for iOS where we have limited runners. This ensures that:

    • Every 15 tests triggers an additional shard
    • Maximum of 3 shards to prevent resource exhaustion
    • All available runners are efficiently utilized
  3. Matrix Generation:

    • Creates a JSON matrix with shard information
    • Each shard contains: {"shard": number, "total": total_shards}
    • Outputs both the matrix and test file list for downstream jobs

Inputs

Input Type Required Description
test_directory String No Directory containing test files (default: e2e/mobile)
test_filter String No Space-separated test name filter
event_name String Yes GitHub event name (workflow_dispatch or schedule)

Outputs

Output Description
matrix JSON array of shard objects for parallel execution
test_files_for_sharding Multi-line list of all test files for sharding

Example Matrix Output

[
  {"shard": 1, "total": 3},
  {"shard": 2, "total": 3},
  {"shard": 3, "total": 3}
]

Usage in Workflow

The action is used in the workflow to:

  1. Generate execution matrix for parallel test runs
  2. Filter tests based on user input
  3. Optimize resource allocation based on test count and event type
  4. Enable targeted testing for specific test suites or features

This approach ensures efficient test execution while maintaining flexibility for different testing scenarios.


Tools and Features

Coming soon... 🚧

Trace Viewer

Detox Recorder



Tips

Animations

Detox synchronization sometime can't handle well animations, especially looping ones. You could disable either the blocking animation while you are in MOCK env (preferred) or disable the synchronization by wrapping your test code between these lines :

await device.disableSynchronization();
...
await device.enableSynchronization();

https://wix.github.io/Detox/docs/api/device#devicedisablesynchronization

You will have to wait manually (waitFor) to replace the synchronization. But be really careful about it, as it might make these tests unstable.


Resources

Happy testing! 🚀

Clone this wiki locally