Skip to content

Add CircleCI job for maestro E2E tests#1637

Merged
ajpallares merged 77 commits intomainfrom
add-maestro-e2e-test-ci-job
Apr 13, 2026
Merged

Add CircleCI job for maestro E2E tests#1637
ajpallares merged 77 commits intomainfrom
add-maestro-e2e-test-ci-job

Conversation

@ajpallares
Copy link
Copy Markdown
Member

@ajpallares ajpallares commented Feb 27, 2026

Summary

Adds CircleCI jobs and Fastlane lanes to build and run Maestro E2E tests for the MaestroTestApp on both iOS and Android.

CircleCI jobs

  • run-maestro-e2e-tests-ios: macOS executor (Xcode 26.3, iPhone 17 simulator), builds the test app, runs Maestro tests
  • run-maestro-e2e-tests-android: android:2024.11.1 machine image, builds a debug APK, creates an emulator via circleci/android orb, runs Maestro tests
  • Both jobs run on every PR (build-test workflow) and on a maestro_e2e_tests schedule

Fastlane lanes

  • run_maestro_e2e_tests_ios: replaces API key placeholder, installs JS + pod dependencies, builds via xcodebuild with FORCE_BUNDLING=1, installs on booted simulator, runs Maestro
  • build_maestro_app_android: replaces API key placeholder, installs JS dependencies, builds via Gradle
  • run_maestro_e2e_tests_android: installs APK via adb, runs Maestro

Implementation notes

  • FORCE_BUNDLING=1 forces the React Native build script to generate main.jsbundle in Debug mode (normally skipped because Debug expects Metro). The AppDelegate in PR1 always loads the pre-bundled JS.
  • YARN_ENABLE_IMMUTABLE_INSTALLS=false is required because Yarn Berry defaults to immutable installs when CI=true. The root yarn.lock pins react-native to 0.78.0, but MaestroTestApp/package.json declares "react-native": "^0.78.0" (a range). When Yarn resolves this range to a newer patch (e.g. 0.78.3), it needs to update the lockfile — which immutable mode forbids. We intentionally allow this drift because: (1) the MaestroTestApp is a CI-only test app rebuilt from scratch every run, (2) staying on the latest compatible patch avoids Xcode/toolchain incompatibilities, and (3) Podfile.lock is not committed for this app (gitignored in PR1) so CocoaPods resolves fresh each time too.
  • Xcode 26.3 is used instead of 26.4 because [email protected]'s bundled fmt library has consteval C++20 compilation errors on Xcode 26.4's clang. This can be upgraded to 26.4 once the root react-native version is bumped to a patch that includes the fix.
  • API key placeholder replacement is validated with a grep check that fails immediately if the placeholder was not replaced.
  • rm -rf ios/Pods is run before pod install because CircleCI's checkout does not clean untracked directories. A stale Pods/Local Podspecs from a previous build can conflict when the resolved react-native version changes between runs.
  • Test results are stored as JUnit artifacts via store_test_results and store_artifacts.

Contexts

  • e2e-tests: provides RC_E2E_TEST_API_KEY_PRODUCTION_TEST_STORE
  • maestro-e2e-tests: Maestro-specific configuration

Depends on #1636

@ajpallares ajpallares added the pr:feat A new feature label Feb 27, 2026
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch from b005a36 to 4630a5a Compare February 27, 2026 10:35
@ajpallares ajpallares added pr:other A code change that improves performance and removed pr:feat A new feature labels Feb 27, 2026
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch 2 times, most recently from 88b1ea0 to b6f68c4 Compare February 27, 2026 12:02
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch from b6f68c4 to 1b892fb Compare February 27, 2026 12:06
@ajpallares ajpallares force-pushed the add-maestro-e2e-test branch from 87fecba to 1fcde2d Compare March 25, 2026 16:09
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch from bb49db0 to d8f5df9 Compare March 25, 2026 16:10
@ajpallares ajpallares force-pushed the add-maestro-e2e-test branch from 1fcde2d to ef227dc Compare March 25, 2026 16:20
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch from d8f5df9 to 2420267 Compare March 25, 2026 16:20
@ajpallares ajpallares force-pushed the add-maestro-e2e-test branch from ef227dc to 599e3f6 Compare March 25, 2026 16:24
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch 2 times, most recently from 980e26e to 45e8f5b Compare March 25, 2026 16:45
@ajpallares ajpallares force-pushed the add-maestro-e2e-test branch from 7fb7ce4 to e52229f Compare March 25, 2026 17:09
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch 3 times, most recently from de68dd0 to c710f92 Compare March 30, 2026 08:33
@ajpallares ajpallares force-pushed the add-maestro-e2e-test branch from b451373 to 877003e Compare March 30, 2026 10:29
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch from c710f92 to 5b64b40 Compare March 30, 2026 10:29
@ajpallares ajpallares force-pushed the add-maestro-e2e-test branch from 877003e to 94450ba Compare March 30, 2026 10:32
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch from 5b64b40 to 6632446 Compare March 30, 2026 10:32
@ajpallares ajpallares force-pushed the add-maestro-e2e-test branch from 94450ba to 9ad5d97 Compare March 30, 2026 11:16
@ajpallares ajpallares force-pushed the add-maestro-e2e-test-ci-job branch 3 times, most recently from d0b20e7 to 8d1913a Compare March 30, 2026 11:45
ajpallares and others added 28 commits April 8, 2026 10:33
- Prefix Dir.chdir and maestro test paths with ../ since Fastlane
  runs from the fastlane/ subdirectory
- Wait for sys.boot_completed before sending keyevent to emulator

Made-with: Cursor
Ensures workspace dependencies are properly installed from the project
root before running Fastlane lanes, matching how other CI jobs work.

Made-with: Cursor
The CI install-dependencies step runs yarn from the workspace root
first, but the Fastlane lane's redundant yarn install from the
MaestroTestApp directory triggers Yarn Berry's immutable check due
to CI=true. Allow lockfile modifications for this second install.

Made-with: Cursor
When yarn resolves to a newer react-native patch version, the
hermes-engine version changes and conflicts with the committed
Podfile.lock. Regenerating it avoids version mismatch errors.

Made-with: Cursor
The Gradle build takes ~7 minutes, during which the emulator
may become disconnected. Add adb start-server and wait-for-device
before adb install to ensure the emulator is still available.

Made-with: Cursor
Debug builds require a running Metro bundler server to serve JS.
On CI there is no Metro server, so the app fails to load.
Release builds pre-bundle the JS, so the app works standalone.

Made-with: Cursor
…put dir

- Revert iOS/Android back to Debug configuration (test store requires it)
- Add FORCE_BUNDLING=1 to iOS xcodebuild so JS bundle is embedded
  without needing Metro running on CI
- Add --test-output-dir to maestro commands so screenshots are stored
  as CI artifacts
- Add takeScreenshot steps to Maestro flow for CI debugging

Made-with: Cursor
The emulator may die (OOM) during the Gradle build step.
Add timeout-based detection and automatic emulator restart if no
device is found within 30 seconds after the build completes.

Made-with: Cursor
Follow purchases-android pattern for emulator management:
- Add circleci/android orb for proper AVD lifecycle management
- Split Fastlane lane into build_maestro_app_android + run_maestro_e2e_tests_android
- Build app first (memory-intensive), then create/start emulator
- Eliminates OOM risk from concurrent build + emulator
- Remove hacky emulator recovery logic

Made-with: Cursor
Pre-launch the app and capture simulator logs to diagnose why
the app shows the springboard instead of the Test Cases screen.

Made-with: Cursor
Remove pre-launch, container check, and simulator log dump steps that
were added during debugging. Maestro handles app launching on its own.

Made-with: Cursor
Stop deleting Podfile.lock before pod install. The lockfile is committed
(consistent with purchaseTesterTypescript) and ensures reproducible builds.

Made-with: Cursor
- Upgrade maestro iOS job to Xcode 26.4
- Add grep check after sed to fail fast if the API key placeholder
  was not replaced (e.g. env var missing or placeholder renamed)

Made-with: Cursor
Since Podfile.lock is no longer committed, use --repo-update to ensure
the pod spec repo is fresh when generating the lockfile from scratch.

Made-with: Cursor
Since yarn.lock is committed and Podfile.lock is now also committed
in PR1, we can rely on Yarn's default immutable installs (CI=true)
to enforce exact dependency resolution. This prevents version drift
that was causing hermes-engine mismatches with the committed Podfile.lock.

Also removed --repo-update from pod install since the committed
Podfile.lock provides exact version pins.

Made-with: Cursor
CircleCI's checkout doesn't clean untracked directories. If a previous
build left a Pods/ directory (which is gitignored), stale Local Podspecs
can conflict with the committed Podfile.lock when dependencies change.
Cleaning Pods/ before pod install ensures a fresh resolution from the
lockfile every time.

Made-with: Cursor
Yarn Berry defaults to immutable installs when CI=true, but the
MaestroTestApp workspace needs to resolve react-native freely (the
root lockfile pins 0.78.0, CI may resolve ^0.78.0 to a newer patch).
YARN_ENABLE_IMMUTABLE_INSTALLS=false allows this.

Also cleans ios/Pods before pod install to prevent stale Local Podspecs
from conflicting when the resolved react-native version changes between
CI runs. Uses --repo-update since Podfile.lock is not committed.

Made-with: Cursor
react-native 0.78.0's bundled fmt library has consteval C++20
compilation errors on Xcode 26.4's clang. Using 26.3 until the
root react-native version is bumped.

Made-with: Cursor
@tonidero tonidero mentioned this pull request Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:other A code change that improves performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants