Skip to content

Commit 05efce8

Browse files
committed
feat: unified Github Action for Harness
1 parent 5cc5afc commit 05efce8

13 files changed

Lines changed: 473 additions & 73 deletions

File tree

.github/workflows/e2e-tests.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ jobs:
100100
key: apk-playground
101101

102102
- name: Run React Native Harness
103-
uses: ./actions/android
103+
uses: ./
104104
with:
105105
app: android/app/build/outputs/apk/debug/app-debug.apk
106106
runner: android
@@ -189,7 +189,7 @@ jobs:
189189
key: ios-app-playground
190190

191191
- name: Run React Native Harness
192-
uses: ./actions/ios
192+
uses: ./
193193
with:
194194
app: ios/build/Build/Products/Debug-iphonesimulator/HarnessPlayground.app
195195
runner: ios
@@ -238,7 +238,7 @@ jobs:
238238
pnpm nx run-many -t build --projects="packages/*"
239239
240240
- name: Run React Native Harness
241-
uses: ./actions/web
241+
uses: ./
242242
with:
243243
runner: chromium
244244
projectRoot: apps/playground
@@ -323,7 +323,7 @@ jobs:
323323
- name: Run React Native Harness (expect crash)
324324
id: crash-test
325325
continue-on-error: true
326-
uses: ./actions/android
326+
uses: ./
327327
with:
328328
app: android/app/build/outputs/apk/debug/app-debug.apk
329329
runner: android-crash-pre-rn
@@ -436,7 +436,7 @@ jobs:
436436
- name: Run React Native Harness (expect crash)
437437
id: crash-test
438438
continue-on-error: true
439-
uses: ./actions/ios
439+
uses: ./
440440
with:
441441
app: ios/build/Build/Products/Debug-iphonesimulator/HarnessPlayground.app
442442
runner: ios-crash-pre-rn

action.yml

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
name: React Native Harness
2+
description: Run React Native Harness tests on iOS, Android or Web
3+
inputs:
4+
runner:
5+
description: The runner to use (must match a runner name defined in your harness config)
6+
required: true
7+
type: string
8+
app:
9+
description: The path to the app (.app for iOS, .apk for Android). Not required for web.
10+
required: false
11+
type: string
12+
projectRoot:
13+
description: The project root directory
14+
required: false
15+
type: string
16+
uploadVisualTestArtifacts:
17+
description: Whether to upload visual test diff and actual images as artifacts
18+
required: false
19+
type: boolean
20+
default: 'true'
21+
harnessArgs:
22+
description: Additional arguments to pass to the Harness CLI
23+
required: false
24+
type: string
25+
default: ''
26+
runs:
27+
using: 'composite'
28+
steps:
29+
- name: Load React Native Harness configuration
30+
id: load-config
31+
shell: bash
32+
env:
33+
INPUT_RUNNER: ${{ inputs.runner }}
34+
INPUT_PROJECTROOT: ${{ inputs.projectRoot }}
35+
run: |
36+
node ${{ github.action_path }}/actions/shared/index.cjs
37+
38+
# ── iOS ──────────────────────────────────────────────────────────────────
39+
- uses: futureware-tech/simulator-action@v4
40+
if: fromJson(steps.load-config.outputs.config).platformId == 'ios'
41+
with:
42+
model: ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
43+
os: iOS
44+
os_version: ${{ fromJson(steps.load-config.outputs.config).config.device.systemVersion }}
45+
wait_for_boot: true
46+
erase_before_boot: false
47+
- name: Install app
48+
if: fromJson(steps.load-config.outputs.config).platformId == 'ios'
49+
shell: bash
50+
working-directory: ${{ inputs.projectRoot }}
51+
run: |
52+
xcrun simctl install booted ${{ inputs.app }}
53+
54+
# ── Android ──────────────────────────────────────────────────────────────
55+
- name: Verify Android config
56+
if: fromJson(steps.load-config.outputs.config).platformId == 'android'
57+
shell: bash
58+
run: |
59+
CONFIG='${{ steps.load-config.outputs.config }}'
60+
if [ -z "$CONFIG.config.device.avd" ] || [ "$CONFIG.config.device.avd" = "null" ]; then
61+
echo "Error: AVD config is required for Android emulators"
62+
echo "Please define the 'avd' property in the runner config"
63+
exit 1
64+
fi
65+
- name: Get architecture of the runner
66+
id: arch
67+
if: fromJson(steps.load-config.outputs.config).platformId == 'android'
68+
shell: bash
69+
run: |
70+
case "${{ runner.arch }}" in
71+
X64)
72+
echo "arch=x86_64" >> $GITHUB_OUTPUT
73+
;;
74+
ARM64)
75+
echo "arch=arm64-v8a" >> $GITHUB_OUTPUT
76+
;;
77+
ARM32)
78+
echo "arch=armeabi-v7a" >> $GITHUB_OUTPUT
79+
;;
80+
*)
81+
echo "arch=x86_64" >> $GITHUB_OUTPUT
82+
;;
83+
esac
84+
- name: Enable KVM group perms
85+
if: fromJson(steps.load-config.outputs.config).platformId == 'android'
86+
shell: bash
87+
run: |
88+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
89+
sudo udevadm control --reload-rules
90+
sudo udevadm trigger --name-match=kvm
91+
ls /dev/kvm
92+
- name: Compute AVD cache key
93+
id: avd-key
94+
if: fromJson(steps.load-config.outputs.config).platformId == 'android'
95+
shell: bash
96+
run: |
97+
CONFIG='${{ steps.load-config.outputs.config }}'
98+
AVD_CONFIG=$(echo "$CONFIG" | jq -c '.config.device.avd')
99+
AVD_CONFIG_HASH=$(echo "$AVD_CONFIG" | sha256sum | cut -d' ' -f1)
100+
ARCH="${{ steps.arch.outputs.arch }}"
101+
CACHE_KEY="avd-$ARCH-$AVD_CONFIG_HASH"
102+
echo "key=$CACHE_KEY" >> $GITHUB_OUTPUT
103+
- name: Restore AVD cache
104+
uses: actions/cache/restore@v4
105+
id: avd-cache
106+
if: fromJson(steps.load-config.outputs.config).platformId == 'android'
107+
with:
108+
path: |
109+
~/.android/avd
110+
~/.android/adb*
111+
key: ${{ steps.avd-key.outputs.key }}
112+
- name: Create AVD and generate snapshot for caching
113+
if: fromJson(steps.load-config.outputs.config).platformId == 'android' && steps.avd-cache.outputs.cache-hit != 'true'
114+
uses: reactivecircus/android-emulator-runner@v2
115+
with:
116+
api-level: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.apiLevel }}
117+
arch: ${{ steps.arch.outputs.arch }}
118+
profile: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.profile }}
119+
disk-size: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.diskSize }}
120+
heap-size: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.heapSize }}
121+
force-avd-creation: false
122+
avd-name: ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
123+
disable-animations: true
124+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
125+
script: echo "Generated AVD snapshot for caching."
126+
- name: Save AVD cache
127+
if: fromJson(steps.load-config.outputs.config).platformId == 'android' && steps.avd-cache.outputs.cache-hit != 'true'
128+
uses: actions/cache/save@v4
129+
with:
130+
path: |
131+
~/.android/avd
132+
~/.android/adb*
133+
key: ${{ steps.avd-key.outputs.key }}
134+
135+
# ── Web ──────────────────────────────────────────────────────────────────
136+
- name: Install Playwright Browsers
137+
if: fromJson(steps.load-config.outputs.config).platformId == 'web'
138+
shell: bash
139+
run: npx playwright install --with-deps chromium
140+
141+
# ── Shared ───────────────────────────────────────────────────────────────
142+
- name: Detect Package Manager
143+
id: detect-pm
144+
shell: bash
145+
run: |
146+
if [ -f "pnpm-lock.yaml" ]; then
147+
echo "manager=pnpm" >> $GITHUB_OUTPUT
148+
echo "runner=pnpm exec " >> $GITHUB_OUTPUT
149+
elif [ -f "yarn.lock" ]; then
150+
echo "manager=yarn" >> $GITHUB_OUTPUT
151+
echo "runner=yarn " >> $GITHUB_OUTPUT
152+
elif [ -f "bun.lock" ] || [ -f "bun.lockb" ]; then
153+
echo "manager=bun" >> $GITHUB_OUTPUT
154+
echo "runner=bunx " >> $GITHUB_OUTPUT
155+
elif [ -f "deno.lock" ]; then
156+
echo "manager=deno" >> $GITHUB_OUTPUT
157+
echo "runner=deno run -A npm:" >> $GITHUB_OUTPUT
158+
else
159+
echo "manager=npm" >> $GITHUB_OUTPUT
160+
echo "runner=npx " >> $GITHUB_OUTPUT
161+
fi
162+
- name: Run E2E tests
163+
if: fromJson(steps.load-config.outputs.config).platformId != 'android'
164+
shell: bash
165+
working-directory: ${{ inputs.projectRoot }}
166+
run: ${{ steps.detect-pm.outputs.runner }}react-native-harness --harnessRunner ${{ inputs.runner }} ${{ inputs.harnessArgs }}
167+
- name: Run E2E tests
168+
id: run-tests
169+
if: fromJson(steps.load-config.outputs.config).platformId == 'android'
170+
uses: reactivecircus/android-emulator-runner@v2
171+
with:
172+
working-directory: ${{ inputs.projectRoot }}
173+
api-level: ${{ fromJson(steps.load-config.outputs.config).config.device.avd.apiLevel }}
174+
arch: ${{ steps.arch.outputs.arch }}
175+
force-avd-creation: false
176+
avd-name: ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
177+
disable-animations: true
178+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
179+
script: |
180+
echo $(pwd)
181+
adb install -r ${{ inputs.app }}
182+
${{ steps.detect-pm.outputs.runner }}react-native-harness --harnessRunner ${{ inputs.runner }} ${{ inputs.harnessArgs }}
183+
- name: Upload visual test artifacts
184+
if: always() && inputs.uploadVisualTestArtifacts == 'true'
185+
uses: actions/upload-artifact@v4
186+
with:
187+
name: visual-test-diffs-${{ fromJson(steps.load-config.outputs.config).platformId }}
188+
path: |
189+
${{ inputs.projectRoot }}/**/__image_snapshots__/**/*-diff.png
190+
${{ inputs.projectRoot }}/**/__image_snapshots__/**/*-actual.png
191+
if-no-files-found: ignore
192+
- name: Upload crash report artifacts
193+
if: always()
194+
uses: actions/upload-artifact@v4
195+
with:
196+
name: harness-crash-reports-${{ fromJson(steps.load-config.outputs.config).platformId }}
197+
path: ${{ inputs.projectRoot }}/.harness/crash-reports/**/*
198+
if-no-files-found: ignore

actions/android/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: React Native Harness for Android
2-
description: Run React Native Harness tests on Android
2+
description: '[Deprecated] Use callstackincubator/react-native-harness instead. Run React Native Harness tests on Android'
33
inputs:
44
app:
55
description: The path to the Android app (.apk)

actions/ios/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: React Native Harness for iOS
2-
description: Run React Native Harness tests on iOS
2+
description: '[Deprecated] Use callstackincubator/react-native-harness instead. Run React Native Harness tests on iOS'
33
inputs:
44
app:
55
description: The path to the iOS app (.app)

actions/web/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: React Native Harness for Web
2-
description: Run React Native Harness tests on Web
2+
description: '[Deprecated] Use callstackincubator/react-native-harness instead. Run React Native Harness tests on Web'
33
inputs:
44
runner:
55
description: The runner to use

0 commit comments

Comments
 (0)