Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,68 @@ jobs:

- name: Build with Gradle
run: ./gradlew :androidApp:assembleDebug

# Launch the app on an emulator and run the Maestro flow shared with the iOS app
maestro:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
- name: set up JDK 17
uses: actions/setup-java@v6
with:
distribution: 'temurin'
java-version: 17
- uses: gradle/actions/setup-gradle@v6

# Without this the runner user can't open /dev/kvm, so the emulator falls back to
# unaccelerated software emulation, which is slow and flaky (APK installs time out).
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

# The Maps SDK key, so the route map renders (the build falls back to a blank map without it)
- name: Build android app
run: ./gradlew :androidApp:assembleDebug
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}

- name: Install Maestro
run: |
curl -fsSL "https://get.maestro.mobile.dev" | bash
echo "$HOME/.maestro/bin" >> "$GITHUB_PATH"
env:
MAESTRO_VERSION: 2.11.0

- name: Run Maestro tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 32
# Google Maps needs Google Play services, which the default system image doesn't have
target: google_apis
arch: x86_64
profile: Nexus 6
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: |
adb install androidApp/build/outputs/apk/debug/androidApp-debug.apk
maestro test --debug-output maestro-debug -e APP_ID=dev.johnoreilly.galwaybus maestro/GalwayBus.flow

# takeScreenshot output: Maestro saves it under --debug-output's hidden .maestro directory
- uses: actions/upload-artifact@v7
if: always()
with:
name: maestro-screenshot-android
path: maestro-debug/**/takeScreenshot/*.png
include-hidden-files: true

# Maestro's logs, screenshots and view hierarchy, to diagnose failures
# (Maestro writes under a hidden .maestro directory, hence include-hidden-files)
- uses: actions/upload-artifact@v7
if: failure()
with:
name: maestro-debug-android
path: maestro-debug
include-hidden-files: true
44 changes: 44 additions & 0 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,54 @@ jobs:
-configuration Debug
-sdk iphonesimulator
-destination 'generic/platform=iOS Simulator'
-derivedDataPath build/dd
build
CODE_SIGNING_ALLOWED=NO
ARCHS=arm64

# Launch the app on a simulator and run the Maestro flow shared with the Android app, so
# runtime crashes in the Kotlin/Swift interop fail CI
- name: Install Maestro
run: |
curl -fsSL "https://get.maestro.mobile.dev" | bash
echo "$HOME/.maestro/bin" >> "$GITHUB_PATH"
env:
MAESTRO_VERSION: 2.11.0

- name: Run Maestro tests
env:
# Starting Maestro's iOS driver on a cold runner can take a few minutes (default limit ~2 min)
MAESTRO_DRIVER_STARTUP_TIMEOUT: 300000
run: |
UDID=$(xcrun simctl list devices available -j | jq -r '[.devices[][] | select(.name | startswith("iPhone"))] | last | .udid')
xcrun simctl boot "$UDID"
xcrun simctl bootstatus "$UDID"
xcrun simctl install "$UDID" build/dd/Build/Products/Debug-iphonesimulator/GalwayBus.app
maestro --device "$UDID" test --debug-output maestro-debug -e APP_ID=dev.johnoreilly.galwaybus maestro/GalwayBus.flow

# takeScreenshot output: Maestro saves it under --debug-output's hidden .maestro directory
- uses: actions/upload-artifact@v7
if: always()
with:
name: maestro-screenshot-ios
path: maestro-debug/**/takeScreenshot/*.png
include-hidden-files: true

- name: Collect iOS crash reports
if: failure()
run: |
mkdir -p maestro-debug/crash-reports
find "$HOME/Library/Logs/DiagnosticReports" -name 'GalwayBus*' -exec cp {} maestro-debug/crash-reports/ \; || true

# Maestro's logs, screenshots and view hierarchy, plus the app's crash report, to diagnose failures
# (Maestro writes under a hidden .maestro directory, hence include-hidden-files)
- uses: actions/upload-artifact@v7
if: failure()
with:
name: maestro-debug-ios
path: maestro-debug
include-hidden-files: true

- name: Save Kotlin/Native (konan) cache
if: github.ref == 'refs/heads/main' && steps.konan-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Use the run button in your IDE's editor gutter, or run tests using Gradle tasks:
- Android tests: `./gradlew :shared:testAndroidHostTest`
- Desktop tests: `./gradlew :shared:jvmTest`
- iOS tests: `./gradlew :shared:iosSimulatorArm64Test`
- UI tests: a [Maestro](https://maestro.mobile.dev/) flow shared by the Android and iOS apps, run by CI on an
emulator and a simulator (e.g. `maestro test -e APP_ID=dev.johnoreilly.galwaybus maestro/GalwayBus.flow`)

## Full set of Kotlin Multiplatform / Compose / SwiftUI samples

Expand Down
22 changes: 22 additions & 0 deletions maestro/GalwayBus.flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Shared by the Android and iOS apps: CI passes APP_ID with -e
appId: ${APP_ID}
---
- launchApp:
clearState: true

- assertVisible: "No saved stops yet"

# Routes and bus positions come from live APIs, so allow for a slow network
- tapOn: "Routes"
- extendedWaitUntil:
visible: "Seacrest - Eyre Square - Merlin Park"
timeout: 30000
- tapOn: "Seacrest - Eyre Square - Merlin Park"

- extendedWaitUntil:
visible: "Route 402"
timeout: 30000
- extendedWaitUntil:
visible: "Updated.*"
timeout: 30000
- takeScreenshot: GalwayBus
Loading