Skip to content

Commit cfe247e

Browse files
committed
ci(workflows): add Maestro E2E tests for Android and iOS
1 parent b6b7721 commit cfe247e

File tree

6 files changed

+148
-0
lines changed

6 files changed

+148
-0
lines changed

.github/workflows/e2e.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: E2E
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
jobs:
11+
e2e-android:
12+
runs-on: ubuntu-latest
13+
timeout-minutes: 30
14+
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Set up Python
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: '3.11'
23+
24+
- name: Set up Java 17
25+
uses: actions/setup-java@v4
26+
with:
27+
distribution: 'temurin'
28+
java-version: '17'
29+
30+
- name: Install PythonNative
31+
run: pip install -e .
32+
33+
- name: Install Maestro
34+
run: |
35+
curl -Ls "https://get.maestro.mobile.dev" | bash
36+
echo "$HOME/.maestro/bin" >> $GITHUB_PATH
37+
38+
- name: Build, install, and run E2E tests
39+
uses: reactivecircus/android-emulator-runner@v2
40+
with:
41+
api-level: 31
42+
arch: x86_64
43+
script: |
44+
export PATH="$HOME/.maestro/bin:$PATH"
45+
cd examples/hello-world
46+
pn run android
47+
sleep 5
48+
cd ../..
49+
maestro test tests/e2e/android.yaml
50+
51+
e2e-ios:
52+
runs-on: macos-latest
53+
timeout-minutes: 30
54+
55+
steps:
56+
- name: Checkout
57+
uses: actions/checkout@v4
58+
59+
- name: Set up Python
60+
uses: actions/setup-python@v5
61+
with:
62+
python-version: '3.11'
63+
64+
- name: Install PythonNative
65+
run: pip install -e .
66+
67+
- name: Install Maestro and idb
68+
run: |
69+
curl -Ls "https://get.maestro.mobile.dev" | bash
70+
echo "$HOME/.maestro/bin" >> $GITHUB_PATH
71+
brew tap facebook/fb && brew install idb-companion
72+
73+
- name: Build and launch iOS app
74+
working-directory: examples/hello-world
75+
run: pn run ios
76+
77+
- name: Run E2E tests
78+
run: maestro --platform ios test tests/e2e/ios.yaml

CONTRIBUTING.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,38 @@ release/v0.2.0
256256
hotfix/cli-regression
257257
```
258258

259+
### E2E tests (Maestro)
260+
261+
End-to-end tests use [Maestro](https://maestro.dev/) to drive the hello-world example on real emulators and simulators.
262+
263+
```bash
264+
# Install Maestro (one-time)
265+
curl -Ls "https://get.maestro.mobile.dev" | bash
266+
267+
# For iOS, also install idb-companion
268+
brew tap facebook/fb && brew install idb-companion
269+
```
270+
271+
Build and launch the app first, then run the tests:
272+
273+
```bash
274+
cd examples/hello-world
275+
276+
# Android (emulator must be running)
277+
pn run android
278+
maestro test ../../tests/e2e/android.yaml
279+
280+
# iOS (simulator must be running; --platform ios needed when an Android emulator is also connected)
281+
pn run ios
282+
maestro --platform ios test ../../tests/e2e/ios.yaml
283+
```
284+
285+
Test flows live in `tests/e2e/flows/` and cover main page rendering, counter interaction, and multi-page navigation. The `e2e.yml` workflow runs these automatically on pushes to `main` and PRs.
286+
259287
### CI
260288

261289
- **CI** (`ci.yml`): runs formatter, linter, type checker, and tests on every push and PR.
290+
- **E2E** (`e2e.yml`): builds the hello-world example on Android (Linux emulator) and iOS (macOS simulator), then runs Maestro flows. Triggers on pushes to `main`, PRs, and manual dispatch.
262291
- **PR Lint** (`pr-lint.yml`): validates the PR title against Conventional Commits format (protects squash merges) and checks individual commit messages via commitlint (protects rebase merges). Recommended: add the **PR title** job as a required status check in branch-protection settings.
263292
- **Release** (`release.yml`): runs on merge to `main`; computes version, generates changelog, tags, creates GitHub Release, and (when `DRAFT_RELEASE` is `"false"`) publishes to PyPI.
264293
- **Docs** (`docs.yml`): deploys documentation to GitHub Pages on push to `main`.

tests/e2e/android.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
appId: com.pythonnative.android_template
2+
env:
3+
APP_ID: com.pythonnative.android_template
4+
---
5+
- runFlow: flows/main_page.yaml
6+
- runFlow: flows/navigation.yaml

tests/e2e/flows/main_page.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
appId: ${APP_ID}
2+
---
3+
# Verify main page renders correctly and the counter works.
4+
- launchApp
5+
- assertVisible: "Hello from PythonNative Demo!"
6+
- assertVisible: "Tapped 0 times"
7+
- assertVisible: "Tap me"
8+
- assertVisible: "Go to Second Page"
9+
- tapOn: "Tap me"
10+
- assertVisible: "Tapped 1 times"
11+
- tapOn: "Tap me"
12+
- assertVisible: "Tapped 2 times"

tests/e2e/flows/navigation.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
appId: ${APP_ID}
2+
---
3+
# Navigate through all three pages and back to main.
4+
- launchApp
5+
- assertVisible: "Hello from PythonNative Demo!"
6+
- tapOn: "Go to Second Page"
7+
- assertVisible: "Greetings from MainPage"
8+
- assertVisible: "Go to Third Page"
9+
- assertVisible: "Back"
10+
- tapOn: "Go to Third Page"
11+
- assertVisible: "Third Page"
12+
- assertVisible: "You navigated two levels deep."
13+
- assertVisible: "Back to Second"
14+
- tapOn: "Back to Second"
15+
- assertVisible: "Greetings from MainPage"
16+
- tapOn: "Back"
17+
- assertVisible: "Hello from PythonNative Demo!"

tests/e2e/ios.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
appId: com.pythonnative.ios-template
2+
env:
3+
APP_ID: com.pythonnative.ios-template
4+
---
5+
- runFlow: flows/main_page.yaml
6+
- runFlow: flows/navigation.yaml

0 commit comments

Comments
 (0)