Add maestro E2E test app#1635
Conversation
Generated by 🚫 Danger |
tonidero
left a comment
There was a problem hiding this comment.
Just some thoughts, but nothing really blocking. Amazing job!!
| @@ -0,0 +1,37 @@ | |||
| # Resolve react_native_pods.rb with node to allow for hoisting | |||
| require Pod::Executable.execute_command('node', ['-p', | |||
There was a problem hiding this comment.
Just to check, we still need to have a Podfile here right? As in, we can't use just SPM already for this new project?
There was a problem hiding this comment.
Not an expert on this, but it looks like SPM support of react-native is not complete yet. So if I'm not mistaken, I'm afraid we still can't use it as is. But we will migrate it as soon as it's possible 💪
| "@react-navigation/native": "^7.0.0", | ||
| "@react-navigation/native-stack": "^7.0.0", | ||
| "react": "19.0.0", | ||
| "react-native": "^0.78.0", |
There was a problem hiding this comment.
I do wonder if we should have done these tests directly with an Expo app... But I think it's fine to keep it to plain react native for now 👍
There was a problem hiding this comment.
Right... we can always migrate it if we want
| "examples/purchaseTesterTypescript", | ||
| "react-native-purchases-ui" | ||
| "react-native-purchases-ui", | ||
| "e2e-tests/MaestroTestApp" |
There was a problem hiding this comment.
[Not related to this line]
Should we add a command to update the pods for the new app in the boostrap command above?
There was a problem hiding this comment.
Good thought, but since this is a CI-only app and the Podfile.lock does not need to be committed, I don't know if adding it to bootstrap could create confusion?
The README documents how to build it locally if needed anyway. I don't have a strong opinion on this though
There was a problem hiding this comment.
It could help if we want to open it locally... but I agree, I'm ok keeping as is for now!
There was a problem hiding this comment.
Anyway, I don't think it should harm. So I added it in 8c9c5ae
Thanks for the suggestion!
e4087e0 to
8c9c5ae
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generated iOS project boilerplate from React Native 0.76.0 template with bundle ID set to com.revenuecat.maestro.e2e. Includes xcodeproj, app delegate, launch screen, privacy manifest, asset catalog, and test target. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The RN repo uses Yarn workspaces, so a standalone yarn.lock is needed to prevent Yarn from treating MaestroTestApp as part of the workspace. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
react-native-screens 4.24+ requires RN 0.77+ codegen. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Align with purchaseTesterTypescript patterns: ignore .yarn cache, .xcode.env.local, Xcode artifacts, and stop over-ignoring xcworkspace. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Objective-C AppDelegate (AppDelegate.h, AppDelegate.mm, main.m) with a Swift AppDelegate using @main annotation. Also adds required devDependencies for pod install to work correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Made-with: Cursor
The StoreKitConfiguration.storekit file was on disk but not registered as a PBXFileReference in project.pbxproj, so Xcode couldn't find it at runtime. Also fix the scheme identifier path. Made-with: Cursor
The identifier is relative to the .xcodeproj directory, so it needs the ../ prefix to reach the sibling StoreKitConfiguration.storekit. Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
- Use portal: references for react-native-purchases and react-native-purchases-ui so E2E tests exercise the local branch code instead of whatever is published on npm - Commit ios/Podfile.lock for reproducible CI builds (force-added since root .gitignore has **/Podfile.lock) - Add try/catch to presentPaywall for diagnosable test failures - Type the navigation prop with RootStackParamList - Add README with build/run instructions and API key setup Made-with: Cursor
… add testID - Add e2e-tests/MaestroTestApp to root workspaces (matching purchaseTesterTypescript pattern) - Replace portal: deps with babel module-resolver aliases to SDK source - Configure Metro watchFolders + exclusionList for out-of-root resolution - Surface presentPaywall errors in a visible <Text testID="error-message"> instead of only console.error, so Maestro can capture failures - Switch from accessibilityLabel to testID for reliable Maestro targeting - Update README to describe the workspace + module-resolver mechanism Made-with: Cursor
…ile.lock - Add .catch() to getCustomerInfo() to surface errors in the UI instead of producing an unhandled promise rejection - Replace deprecated blacklistRE with blockList in metro.config.js - Regenerate Podfile.lock under current workspace setup (was generated under the old portal: setup) Made-with: Cursor
Made-with: Cursor
Without this file, CocoaPods' use_native_modules! doesn't know where to find react-native-purchases and react-native-purchases-ui, since they aren't explicit npm dependencies of the test app. Mirrors the purchaseTesterTypescript example. Made-with: Cursor
Include react-native-purchases and react-native-purchases-ui as Gradle projects so the Android autolinker can resolve them. Mirrors the purchaseTesterTypescript example. Made-with: Cursor
RN 0.76.x is incompatible with react-native-purchases-ui's AGP 8.13.2 (NativeDeviceEventManagerSpec unresolved supertype during Kotlin compilation). - React Native 0.76.9 → 0.78.0 (+ React 19, safe-area-context 5, screens 4.5) - Gradle wrapper 8.10.2 → 8.14.4 - Kotlin 1.9.25 → 2.0.21, NDK 26 → 27, targetSdk 34 → 35 - Add allprojects repositories block and explicit Gradle project includes - Regenerate Podfile.lock and yarn.lock Made-with: Cursor
react-native-screens 4.24.0 has a codegen incompatibility with
RN 0.78 ("Unknown prop type for accessibilityContainerViewIsModal").
Pin to ~4.11.0 (matching purchaseTesterTypescript) and regenerate
both yarn.lock and Podfile.lock.
Made-with: Cursor
Made-with: Cursor
The CLI is a devDependency of react-native (not production), so it's not installed transitively. Both pod install (use_native_modules!) and Gradle autolinking (RNGP) require it to discover native modules. Made-with: Cursor
- Add ReactAppDependencyProvider for RN 0.78+ native component registration - Always use pre-bundled JS instead of Metro dev server (CI has no Metro) - Force JS bundling in Android debug builds by setting debuggableVariants = [] Made-with: Cursor
- Replace force-unwrap in bundleURL() with guard + fatalError for a more debuggable crash when main.jsbundle is missing - Add comment in babel.config.js explaining how SDK packages are resolved from repo source without being in package.json dependencies Made-with: Cursor
The committed Podfile.lock goes stale when yarn resolves a different react-native patch (hermes-engine version changes). Since this is a CI-only test app rebuilt from scratch every run, not committing the lockfile avoids the staleness problem. Made-with: Cursor
Reverting the previous decision to gitignore Podfile.lock. Instead, we'll enforce immutable yarn installs on CI (no YARN_ENABLE_IMMUTABLE_INSTALLS=false) so versions don't drift, and the committed Podfile.lock stays consistent with yarn.lock. Made-with: Cursor
MaestroTestApp is a workspace of the root package.json, so the root yarn.lock is the source of truth. The local yarn.lock was an artifact from project initialization and was pinning react-native@0.78.3 while the root lockfile pins 0.78.0, causing immutable install failures on CI. Made-with: Cursor
react-native 0.78.0 ships a fmt version with consteval C++20 errors when compiled with Xcode 26.4's clang. Pinning to 0.78.3 which includes the fix. Updated yarn.lock and Podfile.lock accordingly. Made-with: Cursor
react-native 0.78.0 ships an fmt version with consteval C++20 compilation errors on Xcode 26.4's clang. Bumping all workspaces to 0.78.3 (patch update) which includes the fix. This also keeps a single react-native version across workspaces, ensuring proper Yarn hoisting of shared dependencies like @react-native/babel-preset. Reverts the MaestroTestApp exact pin to ^0.78.0 (range) since the lockfile now resolves all ^0.78.0 ranges to 0.78.3. Made-with: Cursor
Reverting the attempt to commit Podfile.lock and bump react-native to 0.78.3. The react-native version update is out of scope for the Maestro e2e test setup. Instead, Podfile.lock remains gitignored and will be generated fresh on each CI run using YARN_ENABLE_IMMUTABLE_INSTALLS=false (in PR3) to let yarn resolve the latest compatible react-native patch. Made-with: Cursor
Add a "pods" script to MaestroTestApp matching the purchaseTesterTypescript pattern, and use `yarn workspace MaestroTestApp pods` in bootstrap. Made-with: Cursor
8c9c5ae to
8867ce7
Compare
The yarn.lock got corrupted during rebase conflict resolution. Reset to main and regenerated with yarn install. Made-with: Cursor
## Summary - Adds Maestro YAML test files for the "purchase through paywall" E2E flow - Adds `config.yaml` for Maestro test execution - Test flow: clear state → launch app → navigate to purchase screen → verify no entitlements → present V2 paywall → select "Yearly" → tap "Continue" → confirm purchase → verify "pro" entitlement - Adds `utils/confirm_purchase.yaml` utility that handles test store purchase confirmation on both iOS and Android (using regex to match platform-specific alert text) Depends on #1635 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## RevenueCat SDK > [!WARNING] > If you don't have any login system in your app, please make sure your one-time purchase products have been correctly configured in the RevenueCat dashboard as either consumable or non-consumable. If they're incorrect as consumables, RevenueCat will consume these purchases. This means that customers won't be able to restore them from version 10.0.0 onward. > Non-consumables are products that are meant to be bought only once, for example, lifetime subscriptions. Please see more information in our [docs](https://www.revenuecat.com/docs/known-store-issues/play-billing-library/restore-consumable-purchases-bc8) ### 💥 Breaking Changes This release updates to Billing Library 8.3.0 with min SDK supported of Android 6 (API 23), previously min was 21. It also removes a previous workaround used to be able to restore consumed one time products which is not available anymore. * [AUTOMATIC BUMP] Updates purchases-hybrid-common to 18.0.0 (#1729) via RevenueCat Git Bot (@RCGitBot) * [Android 10.1.2](https://github.com/RevenueCat/purchases-android/releases/tag/10.1.2) * [Android 10.1.1](https://github.com/RevenueCat/purchases-android/releases/tag/10.1.1) * [Android 10.1.0](https://github.com/RevenueCat/purchases-android/releases/tag/10.1.0) * [Android 10.0.2](https://github.com/RevenueCat/purchases-android/releases/tag/10.0.2) * [Android 10.0.1](https://github.com/RevenueCat/purchases-android/releases/tag/10.0.1) * [Android 9.29.1](https://github.com/RevenueCat/purchases-android/releases/tag/9.29.1) * [Android 10.0.0](https://github.com/RevenueCat/purchases-android/releases/tag/10.0.0) * [iOS 5.67.2](https://github.com/RevenueCat/purchases-ios/releases/tag/5.67.2) ### 🔄 Other Changes * Skip test cases list in maestro tests using launch arguments (#1722) via Antonio Pallares (@ajpallares) * Add CircleCI job for maestro E2E tests (#1637) via Antonio Pallares (@ajpallares) * Bump fastlane-plugin-revenuecat_internal from `894bb1b` to `20911d1` (#1728) via dependabot[bot] (@dependabot[bot]) * Bump fastlane-plugin-revenuecat_internal from `ceecf91` to `894bb1b` (#1726) via dependabot[bot] (@dependabot[bot]) * Update CODEOWNERS default owner to @RevenueCat/sdk (#1725) via Antonio Pallares (@ajpallares) * Add maestro E2E test for purchase through paywall (#1636) via Antonio Pallares (@ajpallares) * Add maestro E2E test app (#1635) via Antonio Pallares (@ajpallares) * Bump addressable from 2.8.9 to 2.9.0 (#1723) via dependabot[bot] (@dependabot[bot]) * Bump addressable from 2.8.7 to 2.9.0 in /examples/purchaseTesterTypescript (#1721) via dependabot[bot] (@dependabot[bot]) * Bump addressable from 2.8.7 to 2.9.0 in /examples/MagicWeather (#1720) via dependabot[bot] (@dependabot[bot])
## Summary Adds a Maestro E2E test app under `e2e-tests/MaestroTestApp/` — a minimal Flutter app with two screens used by automated Maestro flows to verify the RevenueCat purchase integration. - Two screens: "Test Cases" list and "Purchase through paywall" (presents a RevenueCat V2 paywall and displays entitlement status) - Bundle ID `com.revenuecat.automatedsdktests` - API key placeholder (`MAESTRO_TESTS_REVENUECAT_API_KEY`) replaced at CI time via `sed` - Local SDK resolution via `path:` references + `dependency_overrides` (same mechanism as `revenuecat_examples/purchase_tester`) - iOS plugins resolved via Swift Package Manager (`FlutterGeneratedPluginSwiftPackage`); CocoaPods only carries the Flutter engine - Errors from `getCustomerInfo` and `presentPaywall` are surfaced in the UI for debugging failed Maestro flows Counterpart PRs: [react-native-purchases#1635](RevenueCat/react-native-purchases#1635), [purchases-capacitor#699](RevenueCat/purchases-capacitor#699), [cordova-plugin-purchases#857](RevenueCat/cordova-plugin-purchases#857), [purchases-unity#836](RevenueCat/purchases-unity#836), [purchases-kmp#708](RevenueCat/purchases-kmp#708) ### Follow-up PRs (stacked) - [#1698](#1698) — Maestro test flows (YAML) - [#1699](#1699) — CircleCI jobs to run the tests - [#1714](#1714) — Launch argument routing to skip test list <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Adds a new, isolated test-only Flutter project with standard iOS/Android scaffolding and a simple paywall purchase flow; it does not modify the SDK runtime code paths. > > **Overview** > Introduces a new Flutter app in `e2e-tests/MaestroTestApp` intended for Maestro E2E automation, including a test-case list screen and a purchase flow that calls `RevenueCatUI.presentPaywall()` and displays active `pro` entitlement state. > > Adds full iOS/Android project scaffolding (manifests, Gradle/Xcode configs, app IDs) plus local `path:` dependencies on `purchases_flutter`/`purchases_ui_flutter`, and documents CI-time API key injection via the `MAESTRO_TESTS_REVENUECAT_API_KEY` placeholder. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit cb1f2f4. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## Summary Adds a Maestro E2E test app under `e2e-tests/MaestroTestApp/` — a minimal Capacitor app used by automated Maestro flows to verify the RevenueCat purchase integration. - Two views: "Test Cases" list and "Purchase through paywall" (presents a RevenueCat V2 paywall and displays entitlement status) - App ID `com.revenuecat.automatedsdktests` - API key placeholder (`MAESTRO_TESTS_REVENUECAT_API_KEY`) replaced at CI time via `sed` - Local SDK resolution via `file:` references (same mechanism as `example/purchase-tester`) - Errors from `getCustomerInfo` and `presentPaywall` are surfaced in the UI for debugging test failures - Includes a README and committed `package-lock.json` Counterpart PRs: [react-native-purchases#1635](RevenueCat/react-native-purchases#1635), [purchases-flutter#1654](RevenueCat/purchases-flutter#1654), [cordova-plugin-purchases#857](RevenueCat/cordova-plugin-purchases#857), [purchases-unity#836](RevenueCat/purchases-unity#836), [purchases-kmp#708](RevenueCat/purchases-kmp#708) ### Follow-up PRs (stacked) - [#700](#700) — Maestro test flows (YAML) - [#701](#701) — CircleCI jobs to run the tests - [#757](#757) — Launch-arg driven Maestro test flow routing
Summary
Adds a Maestro E2E test app under
e2e-tests/MaestroTestApp/— a minimal React Native app with two screens used by automated Maestro flows to verify the RevenueCat purchase integration.com.revenuecat.automatedsdktestsMAESTRO_TESTS_REVENUECAT_API_KEY) replaced at CI time viasedbabel-plugin-module-resolver+ MetrowatchFolders(same mechanism aspurchaseTesterTypescript)getCustomerInfoandpresentPaywallare surfaced in the UI (not just logged) for Maestro screenshot capturetestIDprops for reliable Maestro element targetingRootStackParamListPodfile.lockCounterpart PRs: purchases-flutter#1654, purchases-capacitor#699, cordova-plugin-purchases#857, purchases-unity#836, purchases-kmp#708
Follow-up PRs (stacked)