Skip to content

Add maestro E2E test app#1635

Merged
ajpallares merged 36 commits intomainfrom
e2e-tests-app
Apr 9, 2026
Merged

Add maestro E2E test app#1635
ajpallares merged 36 commits intomainfrom
e2e-tests-app

Conversation

@ajpallares
Copy link
Copy Markdown
Member

@ajpallares ajpallares commented Feb 27, 2026

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.

  • 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 Yarn workspace + babel-plugin-module-resolver + Metro watchFolders (same mechanism as purchaseTesterTypescript)
  • Errors from getCustomerInfo and presentPaywall are surfaced in the UI (not just logged) for Maestro screenshot capture
  • Uses testID props for reliable Maestro element targeting
  • Typed navigation with RootStackParamList
  • Includes a README and committed Podfile.lock

Counterpart PRs: purchases-flutter#1654, purchases-capacitor#699, cordova-plugin-purchases#857, purchases-unity#836, purchases-kmp#708

Follow-up PRs (stacked)

  • #1636 — Maestro test flows (YAML)
  • #1637 — CircleCI jobs to run the tests

@ajpallares ajpallares added the pr:feat A new feature label Feb 27, 2026
@ajpallares ajpallares added pr:other A code change that improves performance and removed pr:feat A new feature labels Feb 27, 2026
@RevenueCat-Danger-Bot
Copy link
Copy Markdown

RevenueCat-Danger-Bot commented Mar 30, 2026

2 Warnings
⚠️ Size check is being bypassed due to the presence of the label "danger-bypass-size-limit"
⚠️ Size increase: 252.79 KB

Generated by 🚫 Danger

Copy link
Copy Markdown
Contributor

@tonidero tonidero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 👍

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right... we can always migrate it if we want

"examples/purchaseTesterTypescript",
"react-native-purchases-ui"
"react-native-purchases-ui",
"e2e-tests/MaestroTestApp"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Not related to this line]
Should we add a command to update the pods for the new app in the boostrap command above?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could help if we want to open it locally... but I agree, I'm ok keeping as is for now!

Copy link
Copy Markdown
Member Author

@ajpallares ajpallares Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, I don't think it should harm. So I added it in 8c9c5ae

Thanks for the suggestion!

ajpallares and others added 14 commits April 8, 2026 10:12
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
- 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
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
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
ajpallares and others added 2 commits April 8, 2026 10:32
The yarn.lock got corrupted during rebase conflict resolution.
Reset to main and regenerated with yarn install.

Made-with: Cursor
@ajpallares ajpallares merged commit 4fa1e06 into main Apr 9, 2026
8 checks passed
@ajpallares ajpallares deleted the e2e-tests-app branch April 9, 2026 11:43
ajpallares added a commit that referenced this pull request Apr 9, 2026
## 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

danger-bypass-size-limit Apply this label to bypass Dangerbot's size limit. pr:other A code change that improves performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants