This post goes over how to set up and run React Native Detox tests on GitHub Actions:
Prerequisites
You have a React Native project with Detox set up.
iOS
Create a GitHub Actions workflow named e2e-ios.yml
:
mkdir -p .github/workflows && touch .github/workflows/e2e-ios.yml
Name your workflow, set up the event that triggers the workflow, and create a job that runs on macOS:
name: e2e-ios
on: push
jobs:
e2e-ios:
runs-on: macos-latest
steps:
# ...
iOS Steps
Check out the repository:
- name: Checkout repository
uses: actions/checkout@v3
Set up Node.js:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version-file: .node-version
If you’re using NVM, replace .node-version
with .nvmrc
.
Install node_modules with Yarn:
- name: Install Yarn dependencies
run: yarn --frozen-lockfile --prefer-offline
Install applesimutils with Homebrew:
- name: Install macOS dependencies
run: |
brew tap wix/brew
brew install applesimutils
If you want to speed up the step, you can disable Homebrew’s auto update and install cleanup:
env:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
Set up Ruby, run bundle install, and cache gems:
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
ruby/setup-ruby will get the Ruby version from .ruby-version
.
Install and cache CocoaPods:
- name: Cache CocoaPods
id: cache-cocoapods
uses: actions/cache@v3
with:
path: ios/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('ios/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
- name: Install CocoaPods
if: steps.cache-cocoapods.outputs.cache-hit != 'true'
run: cd ios ; pod install ; cd -
Optionally, you can clean and build Detox framework cache:
- name: Detox rebuild framework cache
run: yarn detox rebuild-framework-cache
Build and cache Detox iOS (release) build:
- name: Cache Detox build
id: cache-detox-build
uses: actions/cache@v3
with:
path: ios/build
key: ${{ runner.os }}-detox-build
restore-keys: |
${{ runner.os }}-detox-build
- name: Detox build
run: yarn detox build --configuration ios.sim.release
This builds the iOS app using the ios.sim.release
configuration in .detoxrc.js
.
Run Detox tests:
- name: Detox test
run: yarn detox test --configuration ios.sim.release --cleanup --headless --record-logs all
If you get the error Exceeded timeout of 120000ms while setting up Detox environment
, then increase the testRunner.jest.setupTimeout
in .detoxrc.js
.
If there’s a failure, upload the Detox artifacts so you can download them after the workflow ends:
- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v3
with:
name: detox-artifacts
path: artifacts
iOS Workflow
Here’s the full E2E workflow for iOS (see code):
# .github/workflows/e2e-ios.yml
name: e2e-ios
on: push
jobs:
e2e-ios:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version-file: .node-version
- name: Install Yarn dependencies
run: yarn --frozen-lockfile --prefer-offline
- name: Install macOS dependencies
run: |
brew tap wix/brew
brew install applesimutils
env:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Cache CocoaPods
id: cache-cocoapods
uses: actions/cache@v3
with:
path: ios/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('ios/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
- name: Install CocoaPods
if: steps.cache-cocoapods.outputs.cache-hit != 'true'
run: cd ios ; pod install ; cd -
- name: Detox rebuild framework cache
run: yarn detox rebuild-framework-cache
- name: Cache Detox build
id: cache-detox-build
uses: actions/cache@v3
with:
path: ios/build
key: ${{ runner.os }}-detox-build
restore-keys: |
${{ runner.os }}-detox-build
- name: Detox build
run: yarn detox build --configuration ios.sim.release
- name: Detox test
run: yarn detox test --configuration ios.sim.release --cleanup --headless --record-logs all
- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v3
with:
name: detox-artifacts
path: artifacts
Android
Before you start, make sure you first patch your project with the additional Android configuration.
Create a GitHub Actions workflow named e2e-android.yml
:
mkdir -p .github/workflows && touch .github/workflows/e2e-android.yml
Name your workflow, set up the event that triggers the workflow, and create a job that runs on macOS:
name: e2e-android
on: push
jobs:
e2e-android:
runs-on: macos-latest
steps:
# ...
Android Steps
Check out the repository:
- name: Checkout repository
uses: actions/checkout@v3
Set up Node.js:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version-file: .node-version # .nvmrc
Install node_modules with Yarn:
- name: Install Yarn dependencies
run: yarn --frozen-lockfile --prefer-offline
Set up Java:
- name: Setup Java
uses: actions/setup-java@v3
with:
cache: gradle
distribution: temurin
java-version: 17
Build and cache Detox Android (release) build:
- name: Cache Detox build
id: cache-detox-build
uses: actions/cache@v3
with:
path: ios/build
key: ${{ runner.os }}-detox-build
restore-keys: |
${{ runner.os }}-detox-build
- name: Detox build
run: yarn detox build --configuration android.emu.release
This builds the Android app using the android.emu.release
configuration in .detoxrc.js
.
Get the Android Virtual Device (AVD) name from .detoxrc.js
:
- name: Get device name
id: device
run: node -e "console.log('AVD_NAME=' + require('./.detoxrc').devices.emulator.device.avdName)" >> $GITHUB_OUTPUT
Run Detox tests using reactivecircus/android-emulator-runner:
- name: Detox test
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 31
arch: x86_64
avd-name: ${{ steps.device.outputs.AVD_NAME }}
script: yarn detox test --configuration android.emu.release --headless --record-logs all
Make sure not to pass the --cleanup
option in the script or else it will throw an error:
detox[27246] i adb: error: device 'emulator-5554' not found
If there’s a failure, upload the Detox artifacts so you can download them after the workflow ends:
- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v3
with:
name: detox-artifacts
path: artifacts
Android Workflow
Here’s the full E2E workflow for Android (see code):
# .github/workflows/e2e-android.yml
name: e2e-android
on: push
jobs:
e2e-android:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: yarn
node-version-file: .node-version
- name: Install Yarn dependencies
run: yarn --frozen-lockfile --prefer-offline
- name: Setup Java
uses: actions/setup-java@v3
with:
cache: gradle
distribution: temurin
java-version: 17
- name: Cache Detox build
id: cache-detox-build
uses: actions/cache@v3
with:
path: android/app/build
key: ${{ runner.os }}-detox-build
restore-keys: |
${{ runner.os }}-detox-build
- name: Detox build
run: yarn detox build --configuration android.emu.release
- name: Get device name
id: device
run: node -e "console.log('AVD_NAME=' + require('./.detoxrc').devices.emulator.device.avdName)" >> $GITHUB_OUTPUT
- name: Detox test
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 31
arch: x86_64
avd-name: ${{ steps.device.outputs.AVD_NAME }}
script: yarn detox test --configuration android.emu.release --headless --record-logs all
- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v3
with:
name: detox-artifacts
path: artifacts