Skip to content

Commit a7702b4

Browse files
authored
Run test app on CI (#133)
* Add mocha-remote to test-app * Build Android test app for active arch only * Extend GHA workflow to run test app * Upgrade mocha-remote to avoid spawning via shell and propagate SIGTERM on Linux * Split CI job and run test app based on labels * Add labeled to types of pull_request events triggering check * Use existing labels * Cleaning test app scripts * Build Android in release mode (saving space) * Disabling AVD cache to save space
1 parent 55895aa commit a7702b4

File tree

5 files changed

+836
-53
lines changed

5 files changed

+836
-53
lines changed

.github/workflows/check.yml

Lines changed: 111 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
name: Check
22

3+
env:
4+
# Version here should match the one in React Native template and packages/cmake-rn/src/cli.ts
5+
NDK_VERSION: 27.1.12297006
6+
37
on:
48
push:
59
branches:
610
- main
711
pull_request:
12+
types: [labeled, opened, synchronize, reopened]
813

914
concurrency:
1015
group: ${{ github.workflow }}-${{ github.ref }}
@@ -21,13 +26,66 @@ jobs:
2126
node-version: lts/jod
2227
- run: npm ci
2328
- run: npm run lint
24-
test:
29+
unit-tests:
2530
strategy:
2631
fail-fast: false
2732
matrix:
28-
runner: [ubuntu-latest, windows-latest, macos-latest]
29-
name: Test (${{ matrix.runner }})
33+
runner:
34+
- ubuntu-latest
35+
- windows-latest
36+
- macos-latest
3037
runs-on: ${{ matrix.runner }}
38+
name: Unit tests (${{ matrix.runner }})
39+
steps:
40+
- uses: actions/checkout@v4
41+
- uses: actions/setup-node@v4
42+
with:
43+
node-version: lts/jod
44+
- name: Set up JDK 17
45+
uses: actions/setup-java@v3
46+
with:
47+
java-version: "17"
48+
distribution: "temurin"
49+
- name: Setup Android SDK
50+
uses: android-actions/setup-android@v3
51+
with:
52+
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
53+
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
54+
- run: npm ci
55+
- run: npm run bootstrap
56+
- run: npm test
57+
test-ios:
58+
if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'Apple 🍎')
59+
name: Test app (iOS)
60+
runs-on: macos-latest
61+
steps:
62+
- uses: actions/checkout@v4
63+
- uses: actions/setup-node@v4
64+
with:
65+
node-version: lts/jod
66+
- name: Set up JDK 17
67+
uses: actions/setup-java@v3
68+
with:
69+
java-version: "17"
70+
distribution: "temurin"
71+
- name: Setup Android SDK
72+
uses: android-actions/setup-android@v3
73+
with:
74+
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
75+
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
76+
- run: npm ci
77+
- run: npm run bootstrap
78+
- run: npm run pod-install
79+
working-directory: apps/test-app
80+
- name: Run tests (iOS)
81+
run: npm run test:ios
82+
# TODO: Enable release mode when it works
83+
# run: npm run test:ios -- --mode Release
84+
working-directory: apps/test-app
85+
test-android:
86+
if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'Android 🤖')
87+
name: Test app (Android)
88+
runs-on: ubuntu-latest
3189
steps:
3290
- uses: actions/checkout@v4
3391
- uses: actions/setup-node@v4
@@ -40,9 +98,55 @@ jobs:
4098
distribution: "temurin"
4199
- name: Setup Android SDK
42100
uses: android-actions/setup-android@v3
43-
# Version here should match the one in React Native template and packages/cmake-rn/src/cli.ts
44-
- run: sdkmanager --install "ndk;27.1.12297006"
45-
- run: rustup target add x86_64-linux-android aarch64-linux-android aarch64-apple-ios-sim
101+
with:
102+
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
103+
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
46104
- run: npm ci
47105
- run: npm run bootstrap
48-
- run: npm test
106+
- name: Clone patched Hermes version
107+
shell: bash
108+
run: |
109+
REACT_NATIVE_OVERRIDE_HERMES_DIR=$(npx react-native-node-api vendor-hermes --silent)
110+
echo "REACT_NATIVE_OVERRIDE_HERMES_DIR=$REACT_NATIVE_OVERRIDE_HERMES_DIR" >> $GITHUB_ENV
111+
working-directory: apps/test-app
112+
# - name: Setup Android Emulator cache
113+
# uses: actions/cache@v4
114+
# id: avd-cache
115+
# with:
116+
# path: |
117+
# ~/.android/avd/*
118+
# ~/.android/adb*
119+
# key: ${{ runner.os }}-avd-29
120+
# See https://github.com/marketplace/actions/android-emulator-runner#running-hardware-accelerated-emulators-on-linux-runners
121+
- name: Enable KVM group perms
122+
run: |
123+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
124+
sudo udevadm control --reload-rules
125+
sudo udevadm trigger --name-match=kvm
126+
- name: Build weak-node-api for all architectures
127+
run: npm run build-weak-node-api -- --android
128+
working-directory: packages/host
129+
- name: Build ferric-example for all architectures
130+
run: npm run build -- --android
131+
working-directory: packages/ferric-example
132+
- name: Run tests (Android)
133+
timeout-minutes: 75
134+
uses: reactivecircus/android-emulator-runner@v2
135+
with:
136+
api-level: 29
137+
force-avd-creation: false
138+
emulator-options: -no-snapshot-save -no-metrics -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
139+
disable-animations: true
140+
arch: x86
141+
ndk: ${{ env.NDK_VERSION }}
142+
cmake: 3.22.1
143+
working-directory: apps/test-app
144+
script: |
145+
# Setup port forwarding to Mocha Remote
146+
adb reverse tcp:8090 tcp:8090
147+
# Uninstall the app if already in the snapshot (unlikely but could result in a signature mismatch failure)
148+
adb uninstall com.microsoft.reacttestapp || true
149+
# Build, install and run the app
150+
npm run test:android -- --mode Release
151+
# Wait a bit for the sub-process to terminate, before terminating the emulator
152+
sleep 5

apps/test-app/App.tsx

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,73 @@
11
import React from "react";
2-
import { StyleSheet, Text, View, Button } from "react-native";
2+
import { StyleSheet, View, SafeAreaView } from "react-native";
33

4-
/* eslint-disable @typescript-eslint/no-require-imports -- We're using require to defer crashes */
4+
import {
5+
MochaRemoteProvider,
6+
ConnectionText,
7+
StatusEmoji,
8+
StatusText,
9+
} from "mocha-remote-react-native";
510

6-
// import { requireNodeAddon } from "react-native-node-api";
7-
import nodeAddonExamples from "react-native-node-addon-examples";
8-
// import * as ferricExample from "ferric-example";
11+
import nodeAddonExamples from "@react-native-node-api/node-addon-examples";
912

10-
function App(): React.JSX.Element {
13+
function loadTests() {
14+
for (const [suiteName, examples] of Object.entries(nodeAddonExamples)) {
15+
describe(suiteName, () => {
16+
for (const [exampleName, requireExample] of Object.entries(examples)) {
17+
it(exampleName, () => {
18+
requireExample();
19+
});
20+
}
21+
});
22+
}
23+
24+
describe("ferric-example", () => {
25+
it("exports a callable sum function", () => {
26+
// eslint-disable-next-line @typescript-eslint/no-require-imports
27+
const exampleAddon = require("ferric-example");
28+
const result = exampleAddon.sum(1, 3);
29+
if (result !== 4) {
30+
throw new Error(`Expected 1 + 3 to equal 4, but got ${result}`);
31+
}
32+
});
33+
});
34+
}
35+
36+
export default function App() {
1137
return (
12-
<View style={styles.container}>
13-
<Text style={styles.title}>React Native Node-API Modules</Text>
14-
{Object.entries(nodeAddonExamples).map(([suiteName, examples]) => (
15-
<View key={suiteName} style={styles.suite}>
16-
<Text>{suiteName}</Text>
17-
{Object.entries(examples).map(([exampleName, requireExample]) => (
18-
<Button
19-
key={exampleName}
20-
title={exampleName}
21-
onPress={requireExample}
22-
/>
23-
))}
38+
<MochaRemoteProvider tests={loadTests}>
39+
<SafeAreaView style={styles.container}>
40+
<ConnectionText style={styles.connectionText} />
41+
<View style={styles.statusContainer}>
42+
<StatusEmoji style={styles.statusEmoji} />
43+
<StatusText style={styles.statusText} />
2444
</View>
25-
))}
26-
<View key="ferric-example" style={styles.suite}>
27-
<Text>ferric-example</Text>
28-
<Button
29-
title={"Ferric Example: sum(1, 3)"}
30-
onPress={() =>
31-
console.log("1+3 = " + require("ferric-example").sum(1, 3))
32-
}
33-
/>
34-
</View>
35-
</View>
45+
</SafeAreaView>
46+
</MochaRemoteProvider>
3647
);
3748
}
3849

3950
const styles = StyleSheet.create({
4051
container: {
4152
flex: 1,
42-
justifyContent: "center",
53+
backgroundColor: "#fff",
54+
},
55+
statusContainer: {
56+
flex: 1,
4357
alignItems: "center",
58+
justifyContent: "center",
4459
},
45-
suite: {
46-
borderWidth: 1,
47-
width: "96%",
48-
margin: 10,
49-
padding: 10,
60+
statusEmoji: {
61+
fontSize: 30,
62+
margin: 30,
63+
textAlign: "center",
5064
},
51-
title: {
65+
statusText: {
5266
fontSize: 20,
67+
margin: 20,
68+
textAlign: "center",
69+
},
70+
connectionText: {
71+
textAlign: "center",
5372
},
5473
});
55-
56-
export default App;

apps/test-app/package.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
"private": true,
44
"version": "0.1.0",
55
"scripts": {
6-
"android": "react-native run-android",
7-
"build:android": "react-native bundle --entry-file index.js --platform android --dev true --bundle-output dist/main.android.jsbundle --assets-dest dist/res",
8-
"ios": "react-native run-ios",
6+
"metro": "react-native start --no-interactive",
7+
"android": "react-native run-android --no-packager --active-arch-only",
8+
"ios": "react-native run-ios --no-packager",
99
"pod-install": "cd ios && pod install",
10-
"start": "react-native start"
10+
"test:android": "mocha-remote --exit-on-error -- concurrently --kill-others-on-fail --passthrough-arguments npm:metro 'npm:android -- {@}' --",
11+
"test:ios": "mocha-remote --exit-on-error -- concurrently --passthrough-arguments --kill-others-on-fail npm:metro 'npm:ios -- {@}' --"
1112
},
1213
"dependencies": {
1314
"@babel/core": "^7.26.10",
@@ -21,7 +22,11 @@
2122
"@react-native/typescript-config": "0.79.0",
2223
"@rnx-kit/metro-config": "^2.0.1",
2324
"@types/react": "^19.0.0",
25+
"concurrently": "^9.1.2",
2426
"ferric-example": "^0.1.0",
27+
"mocha": "^11.6.0",
28+
"mocha-remote-cli": "^1.13.2",
29+
"mocha-remote-react-native": "^1.13.2",
2530
"react": "19.0.0",
2631
"react-native": "0.79.1",
2732
"react-native-node-addon-examples": "*",

0 commit comments

Comments
 (0)