Skip to content

Add CircleCI job for maestro E2E tests#826

Open
ajpallares wants to merge 47 commits into
mainfrom
pallares/add-maestro-e2e-test-ci-job-v3
Open

Add CircleCI job for maestro E2E tests#826
ajpallares wants to merge 47 commits into
mainfrom
pallares/add-maestro-e2e-test-ci-job-v3

Conversation

@ajpallares

@ajpallares ajpallares commented Apr 27, 2026

Copy link
Copy Markdown
Member

Motivation

Adds CI jobs to build and run Maestro E2E tests on every PR commit.

Description

  • CircleCI jobs for iOS (macOS) and Android (machine: android:2025.10.1) Maestro E2E tests
  • Fastlane lanes: change_maestro_test_app_api_key, build_maestro_test_app, run_maestro_e2e_tests_ios, run_maestro_e2e_tests_android
  • Pre-boots iOS simulator in parallel with build for faster CI
  • Uses run_maestro_e2e_tests from fastlane-plugin-revenuecat_internal for retry logic
  • Crash log collection for iOS CI diagnostics
  • Xcode build phase handles Kotlin framework + Compose Resources embedding

Related PRs: #824 (E2E test app), #825 (Maestro test flows)

@ajpallares ajpallares requested a review from a team as a code owner April 27, 2026 10:35
@ajpallares ajpallares force-pushed the pallares/add-maestro-e2e-test-v3 branch from 981e194 to 8e88cba Compare April 27, 2026 10:39
@ajpallares ajpallares force-pushed the pallares/add-maestro-e2e-test-ci-job-v3 branch from 22cf6fc to 7a1eec0 Compare April 27, 2026 10:40
@ajpallares ajpallares force-pushed the pallares/add-maestro-e2e-test-v3 branch from 8e88cba to d2164aa Compare April 27, 2026 10:50
@ajpallares ajpallares force-pushed the pallares/add-maestro-e2e-test-ci-job-v3 branch from d309589 to d6fec48 Compare April 27, 2026 10:50
@ajpallares ajpallares force-pushed the pallares/add-maestro-e2e-test-v3 branch from d2164aa to 91d2e25 Compare April 27, 2026 11:13
@ajpallares ajpallares force-pushed the pallares/add-maestro-e2e-test-ci-job-v3 branch from d6fec48 to dbaca90 Compare April 27, 2026 11:13
@ajpallares ajpallares marked this pull request as draft April 27, 2026 11:30
ajpallares and others added 17 commits April 30, 2026 10:50
Covers the multi-target Package.swift / cross-project cinterop wiring
introduced for Xcode 26 compatibility: per-target scratch directories,
dependency header copying, and modulemap inclusion of dependency headers.
Also corrects the `dependencyHeaders` docstring on `SwiftBuildTask` to
describe the actual modulemap-based mechanism.

Made-with: Cursor
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The MultiTargetPackageBuilder already knows which targets depend on
which, and the test context knows which subproject registered each
target. Derive evaluationDependsOn calls automatically instead of
requiring tests to pass dependsOnSubprojects explicitly. This closes
a footgun where alphabetically-sorted subproject names could mask
a missing dependency declaration.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Avoids a hardcoded path for the dep's scratch directory assertion;
both dep and consumer now use getScratchDir(projectDir) so the test
stays correct if the production layout changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
The previous assertion checked that a root-level shared scratch dir
didn't exist, but that path would never exist anyway since the root
project has no Swift packages. Instead, assert that each scratch dir
lives under its own subproject's build directory and that they differ.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the silent `if (depHeader.exists())` guard with a `check`
assertion so a missing dependency header surfaces immediately instead
of deferring to a confusing cinterop "unresolved type" error.

Co-authored-by: Cursor <cursoragent@cursor.com>
The cinterop task already depends on swiftBuildTask, which itself
depends on each cross-project compileSwift task. The explicit
dependsOn on the cinterop task was therefore redundant and duplicated
the wiring expressed on swiftBuildTask.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…xtract moduleDir helper

Co-authored-by: Cursor <cursoragent@cursor.com>
…ralize task name, fix formatting

Co-authored-by: Cursor <cursoragent@cursor.com>
…ernal

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add maestro E2E test app for 3.0.0

Adds a minimal Kotlin Multiplatform Compose app under e2e-tests/MaestroTestApp
for running Maestro E2E tests against the RevenueCat SDK.

Adapted for the 3.0.0 architecture:
- No CocoaPods dependency — iOS builds via Gradle + purchases-ios submodule
- Uses projects.knUi for native iOS paywall types
- Paywall presented as a native modal on iOS to work around Compose
  Multiplatform's UIKitViewController accessibility bridge limitation
  that prevents Maestro from seeing embedded SwiftUI content
- Android uses inline Compose Paywall which works with Maestro
- Uses RCPaywallViewControllerDelegateProtocol (not the removed PaywallProxy)

Made-with: Cursor

* Fix build.gradle.kts for Kotlin 2.3.20

Remove deprecated kotlinOptions { jvmTarget } block — the android {}
compileOptions block already sets Java compatibility, and Kotlin 2.3.20
no longer supports the old KotlinCommonOptions.jvmTarget accessor.

Made-with: Cursor

* Exclude MaestroTestApp from binary compatibility validation

The E2E test app is an application, not a library — it has no public
API surface to validate.

Made-with: Cursor

* Use SDK's Paywall composable directly instead of custom PaywallPresenter

Replace the expect/actual PaywallPresenter with the SDK's own
Paywall(options) composable — the E2E app should test the KMP public
API, not a custom native presentation layer.

Made-with: Cursor

* Skip test cases list in maestro tests using launch arguments

Pass e2e_test_flow as a launchApp argument so the app navigates
directly to the target test case screen, making tests faster.
The Test Cases list is preserved for manual/local usage.

Made-with: Cursor

* Refactor test case routing to use a central registry

Extract TEST_CASES registry to a dedicated TestCases.kt file, matching
the pattern used by purchases-capacitor and purchases-flutter. The
TestCasesScreen now dynamically builds from the registry, and App.kt
routes by flow key instead of a hardcoded Screen enum.

Made-with: Cursor

* Use version catalog for MaestroTestApp minSdk

Match the SDK's minSdk (21) instead of hardcoding 24.

Made-with: Cursor

* Document launch arguments and how to add new test cases

Made-with: Cursor

* Revert MaestroTestApp minSdk to 24 (required by revenuecatui)

The revenuecatui module has minSdk 24, which is higher than the SDK's
global android-minSdk of 21. Since MaestroTestApp depends on
revenuecatui, it must use minSdk 24.

Made-with: Cursor

* Simplify customer info fetching, remove delegate

Re-fetch customer info when the paywall is dismissed instead of
maintaining a global delegate. Also add a comment explaining the
minSdk 24 requirement.

Made-with: Cursor

* Review feedback: fix build phase and update README

- Remove reference to non-existent disable-x64.init.gradle.kts from the
  Xcode build phase (ARCHS/EXCLUDED_ARCHS env vars already handle this)
- Update README to reflect that Xcode handles the Kotlin framework build
  via its build phase, and clarify the CI vs local build flow

Made-with: Cursor

* Add .gitignore for MaestroTestApp

Covers Xcode build output and user-specific project data.

Made-with: Cursor

* Update README API key section with lane name and local run instructions

Made-with: Cursor

* Move API key commit warning to apply to both local run methods

Made-with: Cursor

* Remove unused testTag modifiers from PurchaseThroughPaywallScreen

Maestro uses visible text, not Compose test tags.

Made-with: Cursor

* Add maestro E2E test flows

Adds Maestro YAML test flows under e2e-tests/maestro/:
- purchase_through_paywall: navigates to the paywall screen, waits for
  the V2 paywall to render, confirms the test-store purchase, and
  verifies the "pro" entitlement is active
- confirm_purchase: reusable subflow for the sandbox purchase dialog
- config.yaml: sets the app ID for all flows

Made-with: Cursor

* Use launch arguments to skip test cases list in maestro tests

Navigate directly to the target test case screen via e2e_test_flow
launch argument instead of tapping through the test cases list.

Made-with: Cursor

* Use exact strings in confirm_purchase instead of regex

Both iOS and Android now show the same text, so regex patterns
are no longer needed. Matches the Capacitor approach.

Made-with: Cursor

* Remove redundant appId from confirm_purchase subflow

The parent flow already sets appId, so the subflow doesn't need it.

Made-with: Cursor

* Restore appId in confirm_purchase subflow

Maestro requires appId in any flow file that has a config section (---
separator). This is not redundant - without it Maestro fails with
"Either 'url' or 'appId' must be specified in the config section."

Made-with: Cursor
Base automatically changed from pallares/add-maestro-e2e-test-v3 to 3.0.0-dev May 4, 2026 11:52
@ajpallares ajpallares changed the base branch from 3.0.0-dev to pallares/xcode-26-compat May 4, 2026 11:54
- iOS job uses xcode26 executor (matching other jobs in this branch)
- Android job uses machine:android:2025.10.1 with emulator
- Fastlane lanes: change_maestro_test_app_api_key, build_maestro_test_app_ios,
  build_maestro_test_app_android, run_maestro_e2e_tests_ios/android
- Pre-boots iOS simulator in parallel with build for faster CI
- Uses run_maestro_e2e_tests from fastlane-plugin-revenuecat_internal for retry logic
- Crash/logcat collection on failure for diagnostics

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares force-pushed the pallares/add-maestro-e2e-test-ci-job-v3 branch from 66e5543 to 4e586c4 Compare May 4, 2026 11:59
ajpallares and others added 3 commits May 4, 2026 14:08
Xcode 26 ships with iPhone 17 as the default simulator.

Co-authored-by: Cursor <cursoragent@cursor.com>
Base automatically changed from pallares/xcode-26-compat to 3.0.0-dev May 5, 2026 06:32
@JayShortway JayShortway force-pushed the 3.0.0-dev branch 2 times, most recently from 268ad87 to f49600b Compare May 6, 2026 12:00
Base automatically changed from 3.0.0-dev to main May 7, 2026 07:22
ajpallares and others added 2 commits May 21, 2026 18:46
…e2e-test-ci-job-v3

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	.circleci/config.yml
#	CHANGELOG-LATEST.md
#	CHANGELOG.md
#	Gemfile.lock
#	VERSIONS.md
#	core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/AdTracker.ios.kt
#	core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/Purchases.ios.kt
#	fastlane/Fastfile
#	gradle/libs.versions.toml
#	gradle/wrapper/gradle-wrapper.properties
#	kn-core/build.gradle.kts
#	kn-ui/api/kn-ui.klib.api
#	kn-ui/build.gradle.kts
#	mappings/src/iosMain/kotlin/com/revenuecat/purchases/kmp/mappings/AdEventTypes.ios.kt
#	mappings/src/iosMain/kotlin/com/revenuecat/purchases/kmp/mappings/EntitlementVerificationMode.ios.kt
#	mappings/src/iosMain/kotlin/com/revenuecat/purchases/kmp/mappings/Price.ios.kt
#	mappings/src/iosMain/kotlin/com/revenuecat/purchases/kmp/mappings/PurchasesAreCompletedBy.ios.kt
#	mappings/src/iosMain/kotlin/com/revenuecat/purchases/kmp/mappings/StoreKitVersion.ios.kt
#	mappings/src/iosMain/kotlin/com/revenuecat/purchases/kmp/mappings/StoreMessageType.ios.kt
#	mappings/src/iosMain/kotlin/com/revenuecat/purchases/kmp/mappings/StoreProduct.ios.kt
#	revenuecatui/src/iosMain/kotlin/com/revenuecat/purchases/kmp/ui/revenuecatui/PaywallOptionsKtx.kt
#	revenuecatui/src/iosMain/kotlin/com/revenuecat/purchases/kmp/ui/revenuecatui/UIKitCustomerCenter.kt
#	revenuecatui/src/iosMain/kotlin/com/revenuecat/purchases/kmp/ui/revenuecatui/UIKitPaywall.kt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants