Skip to content

Commit 5d28f25

Browse files
committed
feat: change example app, and add maestro CI rig
1 parent ed61764 commit 5d28f25

File tree

12 files changed

+681
-25
lines changed

12 files changed

+681
-25
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
name: "Post Maestro Screenshot"
2+
description: "Posts the final Maestro test screenshot using external image hosting"
3+
inputs:
4+
platform:
5+
description: "Platform (ios or android)"
6+
required: true
7+
github-token:
8+
description: "GitHub token for posting comments"
9+
required: true
10+
default: ${{ github.token }}
11+
test-outcome:
12+
description: "Test outcome (success or failure)"
13+
required: false
14+
default: "unknown"
15+
imgbb-api-key:
16+
description: "ImgBB API key for image hosting"
17+
required: true
18+
19+
runs:
20+
using: "composite"
21+
steps:
22+
- name: Check if screenshot exists
23+
id: check-screenshot
24+
shell: bash
25+
run: |
26+
SS_FILE="/tmp/e2e-output/${{ inputs.platform }}-tests-completed.png"
27+
if [ -f "$SS_FILE" ]; then
28+
echo "exists=true" >> $GITHUB_OUTPUT
29+
echo "file_path=$SS_FILE" >> $GITHUB_OUTPUT
30+
echo "Screenshot found at $SS_FILE"
31+
else
32+
echo "exists=false" >> $GITHUB_OUTPUT
33+
echo "Screenshot not found at $SS_FILE"
34+
fi
35+
36+
- name: Upload screenshot to ImgBB
37+
if: steps.check-screenshot.outputs.exists == 'true' && github.event_name == 'pull_request'
38+
id: upload-screenshot
39+
uses: McCzarny/upload-image@v2.0.0
40+
with:
41+
path: ${{ steps.check-screenshot.outputs.file_path }}
42+
upload-method: imgbb
43+
api-key: ${{ inputs.imgbb-api-key }}
44+
expiration: 2592000 # 30 days
45+
46+
- name: Find Comment
47+
uses: peter-evans/find-comment@v3
48+
id: find-comment
49+
if: github.event_name == 'pull_request'
50+
with:
51+
issue-number: ${{ github.event.pull_request.number }}
52+
comment-author: "github-actions[bot]"
53+
body-includes: End-to-End Test Results - ${{ inputs.platform == 'ios' && 'iOS' || 'Android' }}
54+
55+
- name: Create or update PR comment (with screenshot)
56+
if: github.event_name == 'pull_request' && steps.check-screenshot.outputs.exists == 'true' && steps.upload-screenshot.outputs.url
57+
uses: peter-evans/create-or-update-comment@v4
58+
with:
59+
token: ${{ inputs.github-token }}
60+
issue-number: ${{ github.event.pull_request.number }}
61+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
62+
body: |
63+
## 🤖 End-to-End Test Results - ${{ inputs.platform == 'ios' && 'iOS' || 'Android' }}
64+
65+
**Status**: ${{ inputs.test-outcome == 'success' && '✅ Passed' || '❌ Failed' }}
66+
**Platform**: ${{ inputs.platform == 'ios' && 'iOS' || 'Android' }}
67+
**Run**: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
68+
69+
### 📸 Final Test Screenshot
70+
71+
<img width="600" alt="Maestro Test Results - ${{ inputs.platform }}" src="${{ steps.upload-screenshot.outputs.url }}" />
72+
73+
*Screenshot automatically captured from End-to-End tests and will expire in 30 days*
74+
75+
---
76+
*This comment is automatically updated on each test run.*
77+
edit-mode: replace
78+
79+
- name: Create or update PR comment (no screenshot)
80+
if: github.event_name == 'pull_request' && steps.check-screenshot.outputs.exists != 'true'
81+
uses: peter-evans/create-or-update-comment@v4
82+
with:
83+
token: ${{ inputs.github-token }}
84+
issue-number: ${{ github.event.pull_request.number }}
85+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
86+
body: |
87+
## 🤖 End-to-End Test Results - ${{ inputs.platform == 'ios' && 'iOS' || 'Android' }}
88+
89+
**Status**: ${{ inputs.test-outcome == 'success' && '✅ Passed' || '❌ Failed' }}
90+
**Platform**: ${{ inputs.platform == 'ios' && 'iOS' || 'Android' }}
91+
**Run**: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
92+
93+
### 📸 Final Test Screenshot
94+
95+
⚠️ No final screenshot available
96+
97+
---
98+
*This comment is automatically updated on each test run.*
99+
edit-mode: replace
100+
101+
- name: Create or update PR comment (upload failed)
102+
if: github.event_name == 'pull_request' && steps.check-screenshot.outputs.exists == 'true' && !steps.upload-screenshot.outputs.url
103+
uses: peter-evans/create-or-update-comment@v4
104+
with:
105+
token: ${{ inputs.github-token }}
106+
issue-number: ${{ github.event.pull_request.number }}
107+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
108+
body: |
109+
## 🤖 End-to-End Test Results - ${{ inputs.platform == 'ios' && 'iOS' || 'Android' }}
110+
111+
**Status**: ${{ inputs.test-outcome == 'success' && '✅ Passed' || '❌ Failed' }}
112+
**Platform**: ${{ inputs.platform == 'ios' && 'iOS' || 'Android' }}
113+
**Run**: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
114+
115+
### 📸 Final Test Screenshot
116+
117+
⚠️ Screenshot was captured but failed to upload. Check workflow logs for details.
118+
119+
---
120+
*This comment is automatically updated on each test run.*
121+
edit-mode: replace
122+
123+
- name: Log screenshot status
124+
shell: bash
125+
run: |
126+
if [ "${{ steps.check-screenshot.outputs.exists }}" = "true" ]; then
127+
if [ -n "${{ steps.upload-screenshot.outputs.url }}" ]; then
128+
echo "✅ Screenshot uploaded and embedded in PR comment for ${{ inputs.platform }}"
129+
echo "🔗 Image URL: ${{ steps.upload-screenshot.outputs.url }}"
130+
echo "🗑️ Delete URL: ${{ steps.upload-screenshot.outputs.delete-url }}"
131+
else
132+
echo "❌ Screenshot upload failed for ${{ inputs.platform }}"
133+
fi
134+
else
135+
echo "⚠️ No screenshot found for ${{ inputs.platform }}"
136+
fi
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: End-to-End Tests for Android
2+
3+
concurrency:
4+
group: '${{ github.workflow }}-${{ github.ref }}'
5+
cancel-in-progress: true
6+
7+
on:
8+
pull_request:
9+
types: [opened, synchronize, reopened]
10+
paths:
11+
- '.github/workflows/e2e-android-test.yml'
12+
- 'example/**'
13+
- 'cpp/**'
14+
- 'nitrogen/**'
15+
- 'src/**'
16+
push:
17+
branches: [main, feat/tests-in-ci]
18+
paths:
19+
- 'example/**'
20+
- 'cpp/**'
21+
- 'nitrogen/**'
22+
- 'src/**'
23+
24+
jobs:
25+
e2e-tests-android:
26+
runs-on: ubuntu-latest
27+
env:
28+
EMULATOR_API_LEVEL: 34
29+
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v5
33+
34+
- name: Setup Node.js
35+
uses: actions/setup-node@v5
36+
with:
37+
node-version: '20'
38+
39+
- name: Install Bun
40+
uses: oven-sh/setup-bun@v1
41+
with:
42+
bun-version: latest
43+
44+
- name: Create Directories
45+
run: |
46+
mkdir -p /tmp/e2e-output
47+
mkdir -p $HOME/.maestro/tests/
48+
49+
- name: Setup JDK
50+
uses: actions/setup-java@v4
51+
with:
52+
distribution: 'corretto'
53+
java-version: '17'
54+
cache: gradle
55+
56+
- name: Cache Android SDK
57+
uses: actions/cache@v4
58+
with:
59+
path: |
60+
"${{ env.ANDROID_SDK_ROOT }}"
61+
~/.android
62+
key: "${{ runner.os }}-android-sdk-${{ hashFiles('.github/workflows/e2e-android-test.yml') }}"
63+
restore-keys: |
64+
"${{ runner.os }}-android-sdk-"
65+
66+
- name: Install System Dependencies
67+
run: |
68+
sudo apt-get update
69+
sudo apt-get install -y libssl-dev pkg-config
70+
71+
- name: Setup Android SDK
72+
uses: android-actions/setup-android@v3
73+
74+
- name: Setup Rust
75+
uses: dtolnay/rust-toolchain@stable
76+
77+
- name: Install Android Rust Target (x86_64 only)
78+
run: |
79+
rustup target add x86_64-linux-android
80+
81+
- name: Install Dependencies
82+
run: bun install
83+
84+
- name: Install Maestro CLI v2
85+
run: |
86+
curl -fsSL "https://get.maestro.mobile.dev" | bash
87+
echo "$HOME/.maestro/bin" >> $GITHUB_PATH
88+
89+
- name: Enable KVM
90+
if: env.SKIP_KVM != 'true'
91+
run: |
92+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
93+
sudo udevadm control --reload-rules
94+
sudo udevadm trigger --name-match=kvm
95+
96+
- name: AVD cache
97+
uses: actions/cache@v4
98+
id: avd-cache
99+
with:
100+
path: |
101+
~/.android/avd/*
102+
~/.android/adb*
103+
key: avd-${{ env.EMULATOR_API_LEVEL }}
104+
105+
- name: Create AVD and Generate Snapshot for Caching
106+
if: steps.avd-cache.outputs.cache-hit != 'true'
107+
uses: reactivecircus/android-emulator-runner@v2
108+
with:
109+
api-level: ${{ env.EMULATOR_API_LEVEL }}
110+
arch: x86_64
111+
force-avd-creation: false
112+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics
113+
disable-animations: false
114+
script: echo "Generated AVD snapshot for caching."
115+
116+
- name: Make scripts executable
117+
run: chmod +x test/e2e/*.sh
118+
working-directory: ./example/
119+
120+
- name: Run E2E Tests
121+
uses: reactivecircus/android-emulator-runner@v2
122+
id: test_android
123+
continue-on-error: true
124+
with:
125+
api-level: ${{ env.EMULATOR_API_LEVEL }}
126+
arch: x86_64
127+
force-avd-creation: false
128+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics
129+
disable-animations: true
130+
working-directory: ./example/
131+
script: ./test/e2e/run-android.sh
132+
133+
- name: Upload Test Output
134+
if: always()
135+
uses: actions/upload-artifact@v4
136+
with:
137+
name: e2e-android-test-output
138+
path: |
139+
/tmp/e2e-output/**/*.log
140+
/tmp/e2e-output/**/*.png
141+
retention-days: 5
142+
143+
- name: Exit with Test Result
144+
if: always()
145+
run: |
146+
if [ "${{ steps.test_android.outcome }}" != "success" ]; then
147+
exit 1
148+
fi

.github/workflows/e2e-ios-test.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: End-to-End Tests for iOS
2+
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
5+
cancel-in-progress: true
6+
7+
on:
8+
pull_request:
9+
types: [opened, synchronize, reopened]
10+
paths:
11+
- '.github/workflows/e2e-ios-test.yml'
12+
- 'example/**'
13+
- 'cpp/**'
14+
- 'nitrogen/**'
15+
- 'src/**'
16+
push:
17+
branches: [main, feat/tests-in-ci]
18+
paths:
19+
- 'example/**'
20+
- 'cpp/**'
21+
- 'nitrogen/**'
22+
- 'src/**'
23+
24+
jobs:
25+
e2e-tests-ios:
26+
runs-on: macos-15
27+
env:
28+
RCT_USE_RN_DEP: 1
29+
RCT_USE_PREBUILT_RNCORE: 1
30+
31+
steps:
32+
- name: Checkout
33+
uses: actions/checkout@v4
34+
35+
- name: Select Xcode 16.4
36+
run: |
37+
sudo xcode-select -s "/Applications/Xcode_16.4.app/Contents/Developer"
38+
xcodebuild -version
39+
40+
- name: Install Bun
41+
uses: oven-sh/setup-bun@v1
42+
with:
43+
bun-version: latest
44+
45+
- name: Create Directories
46+
run: |
47+
mkdir -p /tmp/e2e-output
48+
mkdir -p $HOME/.maestro/tests/
49+
50+
- name: Setup Rust
51+
uses: dtolnay/rust-toolchain@stable
52+
53+
- name: Install iOS Rust Targets
54+
run: |
55+
rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
56+
57+
- name: Install Dependencies
58+
run: bun install
59+
60+
- name: Install CocoaPods
61+
working-directory: ./example
62+
run: bun pods
63+
64+
- name: Install Maestro CLI v2
65+
run: |
66+
curl -fsSL "https://get.maestro.mobile.dev" | bash
67+
echo "$HOME/.maestro/bin" >> $GITHUB_PATH
68+
69+
- name: Make scripts executable
70+
run: chmod +x test/e2e/*.sh
71+
working-directory: ./example/
72+
73+
- name: Run E2E Tests
74+
id: test_ios
75+
run: ./test/e2e/run-ios.sh
76+
working-directory: ./example/
77+
env:
78+
IOS_SIMULATOR_DEVICE: 'iPhone 16 Pro'
79+
80+
- name: Upload Test Output
81+
if: always()
82+
uses: actions/upload-artifact@v4
83+
with:
84+
name: e2e-ios-test-output
85+
path: |
86+
/tmp/e2e-output/**/*.log
87+
/tmp/e2e-output/**/*.png
88+
retention-days: 5
89+
90+
- name: Exit with Test Result
91+
if: always()
92+
run: |
93+
if [ "${{ steps.test_ios.outcome }}" != "success" ]; then
94+
exit 1
95+
fi

0 commit comments

Comments
 (0)