Skip to content

Commit 9aa6254

Browse files
authored
chore: add GitHub Actions for Harness (#32)
This pull request adds dedicated GitHub Actions for Harness to simplify running tests on Android and iOS in the GitHub Actions environment. In a nutshell, each action wraps a couple of third-party actions for running an emulator and ensures they run as fast as possible (see AVD caching for Android). The actions can also read the Harness config, so you no longer have to duplicate configuration in both your JavaScript code and your workflows. A new property called avd has been added to the Android platform config, which includes details about the expected Android emulator.
1 parent 25240e3 commit 9aa6254

File tree

35 files changed

+5778
-269
lines changed

35 files changed

+5778
-269
lines changed

.github/workflows/e2e-tests.yml

Lines changed: 10 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -108,54 +108,12 @@ jobs:
108108
path: apps/${{ matrix.app }}/android/app/build/outputs/apk/debug/app-debug.apk
109109
key: apk-${{ matrix.app }}
110110

111-
- name: Enable KVM group perms
112-
run: |
113-
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
114-
sudo udevadm control --reload-rules
115-
sudo udevadm trigger --name-match=kvm
116-
ls /dev/kvm
117-
118-
- name: AVD cache
119-
uses: actions/cache@v4
120-
id: avd-cache
121-
with:
122-
path: |
123-
~/.android/avd/*
124-
~/.android/adb*
125-
key: avd-35-2
126-
127-
- name: List AVDs
128-
run: |
129-
$ANDROID_HOME/cmdline-tools/latest/bin/avdmanager list device -c
130-
131-
- name: Create AVD and generate snapshot for caching
132-
if: steps.avd-cache.outputs.cache-hit != 'true'
133-
uses: reactivecircus/android-emulator-runner@v2
134-
with:
135-
api-level: 35
136-
arch: x86_64
137-
profile: pixel_6
138-
disk-size: 1G
139-
heap-size: 1G
140-
force-avd-creation: false
141-
avd-name: Pixel_8_API_35
142-
disable-animations: true
143-
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
144-
script: echo "Generated AVD snapshot for caching."
145-
146-
- name: Run E2E tests
147-
uses: reactivecircus/android-emulator-runner@v2
111+
- name: Run React Native Harness
112+
uses: ./actions/android
148113
with:
149-
working-directory: apps/${{ matrix.app }}/android
150-
api-level: 35
151-
arch: x86_64
152-
force-avd-creation: false
153-
avd-name: Pixel_8_API_35
154-
disable-animations: true
155-
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
156-
script: |
157-
adb install -r "./app/build/outputs/apk/debug/app-debug.apk"
158-
pnpm nx run @react-native-harness/${{ matrix.app }}:start --args="--harnessRunner android"
114+
app: android/app/build/outputs/apk/debug/app-debug.apk
115+
runner: android
116+
projectRoot: apps/${{ matrix.app }}
159117

160118
e2e-ios:
161119
name: E2E iOS - ${{ matrix.app }}
@@ -238,21 +196,12 @@ jobs:
238196
path: ./apps/${{ matrix.app }}/ios/build/Build/Products/Debug-iphonesimulator/HarnessPlayground.app
239197
key: ios-app-${{ matrix.app }}
240198

241-
- uses: futureware-tech/simulator-action@v4
199+
- name: Run React Native Harness
200+
uses: ./actions/ios
242201
with:
243-
model: 'iPhone 16 Pro'
244-
os: iOS
245-
os_version: 18.6
246-
wait_for_boot: true
247-
erase_before_boot: false
248-
249-
- name: Install app
250-
run: |
251-
xcrun simctl install booted ./apps/${{ matrix.app }}/ios/build/Build/Products/Debug-iphonesimulator/HarnessPlayground.app
252-
253-
- name: Run E2E tests
254-
run: |
255-
HARNESS_DEBUG=true pnpm nx run @react-native-harness/${{ matrix.app }}:start --args="--harnessRunner ios"
202+
app: ios/build/Build/Products/Debug-iphonesimulator/HarnessPlayground.app
203+
runner: ios
204+
projectRoot: apps/${{ matrix.app }}
256205

257206
- name: Take screenshot after E2E tests
258207
if: failure()

actions/android/action.yml

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
name: React Native Harness for Android
2+
description: Run React Native Harness tests on Android
3+
inputs:
4+
app:
5+
description: The path to the Android app (.apk)
6+
required: true
7+
runner:
8+
description: The runner to use
9+
required: true
10+
type: string
11+
projectRoot:
12+
description: The project root directory
13+
required: false
14+
type: string
15+
runs:
16+
using: 'composite'
17+
steps:
18+
- name: Load React Native Harness configuration
19+
id: load-config
20+
shell: bash
21+
env:
22+
INPUT_RUNNER: ${{ inputs.runner }}
23+
INPUT_PROJECTROOT: ${{ inputs.projectRoot }}
24+
run: |
25+
node ${{ github.action_path }}/../shared/index.cjs
26+
- name: Verify Android config
27+
shell: bash
28+
run: |
29+
CONFIG='${{ steps.load-config.outputs.config }}'
30+
if [ -z "$CONFIG.config.device.avd" ] || [ "$CONFIG.config.device.avd" = "null" ]; then
31+
echo "Error: AVD config is required for Android emulators"
32+
echo "Please define the 'avd' property in the runner config"
33+
exit 1
34+
fi
35+
- name: Get architecture of the runner
36+
id: arch
37+
shell: bash
38+
run: |
39+
case "${{ runner.arch }}" in
40+
X64)
41+
echo "arch=x86_64" >> $GITHUB_OUTPUT
42+
;;
43+
ARM64)
44+
echo "arch=arm64-v8a" >> $GITHUB_OUTPUT
45+
;;
46+
ARM32)
47+
echo "arch=armeabi-v7a" >> $GITHUB_OUTPUT
48+
;;
49+
*)
50+
echo "arch=x86_64" >> $GITHUB_OUTPUT
51+
;;
52+
esac
53+
- name: Enable KVM group perms
54+
shell: bash
55+
run: |
56+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
57+
sudo udevadm control --reload-rules
58+
sudo udevadm trigger --name-match=kvm
59+
ls /dev/kvm
60+
- name: Compute AVD cache key
61+
id: avd-key
62+
shell: bash
63+
run: |
64+
CONFIG='${{ steps.load-config.outputs.config }}'
65+
AVD_CONFIG=$(echo "$CONFIG" | jq -c '.config.device.avd')
66+
AVD_CONFIG_HASH=$(echo "$AVD_CONFIG" | sha256sum | cut -d' ' -f1)
67+
ARCH="${{ steps.arch.outputs.arch }}"
68+
CACHE_KEY="avd-$ARCH-$AVD_CONFIG_HASH"
69+
echo "key=$CACHE_KEY" >> $GITHUB_OUTPUT
70+
- name: Restore AVD cache
71+
uses: actions/cache/restore@v4
72+
id: avd-cache
73+
with:
74+
path: |
75+
~/.android/avd
76+
~/.android/adb*
77+
key: ${{ steps.avd-key.outputs.key }}
78+
- name: Create AVD and generate snapshot for caching
79+
if: steps.avd-cache.outputs.cache-hit != 'true'
80+
uses: reactivecircus/android-emulator-runner@v2
81+
with:
82+
api-level: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.apiLevel }}
83+
arch: ${{ steps.arch.outputs.arch }}
84+
profile: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.profile }}
85+
disk-size: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.diskSize }}
86+
heap-size: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.heapSize }}
87+
force-avd-creation: false
88+
avd-name: ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
89+
disable-animations: true
90+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
91+
script: echo "Generated AVD snapshot for caching."
92+
- name: Save AVD cache
93+
if: steps.avd-cache.outputs.cache-hit != 'true'
94+
uses: actions/cache/save@v4
95+
with:
96+
path: |
97+
~/.android/avd
98+
~/.android/adb*
99+
key: ${{ steps.avd-key.outputs.key }}
100+
- name: Run E2E tests
101+
id: run-tests
102+
uses: reactivecircus/android-emulator-runner@v2
103+
with:
104+
working-directory: ${{ inputs.projectRoot }}
105+
api-level: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.apiLevel }}
106+
arch: ${{ steps.arch.outputs.arch }}
107+
force-avd-creation: false
108+
avd-name: ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
109+
disable-animations: true
110+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
111+
script: |
112+
echo $(pwd)
113+
adb install -r ${{ inputs.app }}
114+
pnpm react-native-harness --harnessRunner ${{ inputs.runner }}

actions/android/index.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"use strict";

actions/ios/action.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: React Native Harness for iOS
2+
description: Run React Native Harness tests on iOS
3+
inputs:
4+
app:
5+
description: The path to the iOS app (.app)
6+
required: true
7+
runner:
8+
description: The runner to use
9+
required: true
10+
type: string
11+
projectRoot:
12+
description: The project root directory
13+
required: false
14+
type: string
15+
runs:
16+
using: 'composite'
17+
steps:
18+
- name: Load React Native Harness configuration
19+
id: load-config
20+
shell: bash
21+
env:
22+
INPUT_RUNNER: ${{ inputs.runner }}
23+
INPUT_PROJECTROOT: ${{ inputs.projectRoot }}
24+
run: |
25+
node ${{ github.action_path }}/../shared/index.cjs
26+
- uses: futureware-tech/simulator-action@v4
27+
with:
28+
model: ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
29+
os: iOS
30+
os_version: ${{ fromJson(steps.load-config.outputs.config).config.device.systemVersion }}
31+
wait_for_boot: true
32+
erase_before_boot: false
33+
- name: Install app
34+
shell: bash
35+
working-directory: ${{ inputs.projectRoot }}
36+
run: |
37+
xcrun simctl install booted ${{ inputs.app }}
38+
- name: Run E2E tests
39+
shell: bash
40+
working-directory: ${{ inputs.projectRoot }}
41+
run: |
42+
pnpm react-native-harness --harnessRunner ${{ inputs.runner }}

actions/ios/index.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"use strict";

0 commit comments

Comments
 (0)