Skip to content

Commit 654e41b

Browse files
authored
Add maestro E2E test app (#1654)
## 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 -->
1 parent b260e4c commit 654e41b

66 files changed

Lines changed: 1622 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.dart_tool/
2+
.packages
3+
build/
4+
ios/Pods/
5+
ios/.symlinks/
6+
ios/Flutter/Flutter.framework
7+
ios/Flutter/Flutter.podspec
8+
ios/Flutter/Generated.xcconfig
9+
ios/Flutter/ephemeral/
10+
ios/Flutter/flutter_export_environment.sh
11+
.flutter-plugins
12+
.flutter-plugins-dependencies
13+
.metadata
14+
pubspec.lock

e2e-tests/MaestroTestApp/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Maestro E2E Test App
2+
3+
A minimal Flutter app used by Maestro end-to-end tests to verify RevenueCat SDK integration.
4+
5+
## Prerequisites
6+
7+
- Flutter SDK (>= 3.27.0)
8+
- Xcode (iOS) / Android Studio (Android)
9+
- [Maestro](https://maestro.mobile.dev/) CLI
10+
11+
## Setup
12+
13+
```bash
14+
flutter pub get
15+
```
16+
17+
## Running Locally
18+
19+
```bash
20+
flutter run --debug -d <simulator-or-emulator-id>
21+
```
22+
23+
## API Key
24+
25+
The app initialises RevenueCat with the placeholder `MAESTRO_TESTS_REVENUECAT_API_KEY`.
26+
In CI, the Fastlane lane replaces this placeholder with the real key from the
27+
`RC_E2E_TEST_API_KEY_PRODUCTION_TEST_STORE` environment variable (provided by the
28+
CircleCI `e2e-tests` context) before building.
29+
30+
To run locally, either:
31+
- Replace the placeholder in `lib/main.dart` with a valid API key (do **not** commit it), or
32+
- Export the env var and run the same `sed` command the Fastlane lane uses.
33+
34+
## RevenueCat Project
35+
36+
The test uses a RevenueCat project configured with:
37+
- A **V2 Paywall** (the test asserts "Paywall V2" is visible)
38+
- A `pro` entitlement (the test checks entitlement status after purchase)
39+
- The **Test Store** environment for purchase confirmation
40+
41+
## Dependencies
42+
43+
`purchases_flutter` and `purchases_ui_flutter` are referenced as local `path:`
44+
dependencies so the E2E tests always exercise the code on the current branch, not a
45+
published pub.dev version.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include: package:flutter_lints/flutter.yaml
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
gradle-wrapper.jar
2+
/.gradle
3+
/captures/
4+
/gradlew
5+
/gradlew.bat
6+
/local.properties
7+
GeneratedPluginRegistrant.java
8+
.cxx/
9+
10+
# Remember to never publicly share your keystore.
11+
# See https://flutter.dev/to/reference-keystore
12+
key.properties
13+
**/*.keystore
14+
**/*.jks
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
plugins {
2+
id("com.android.application")
3+
id("kotlin-android")
4+
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5+
id("dev.flutter.flutter-gradle-plugin")
6+
}
7+
8+
android {
9+
namespace = "com.revenuecat.automatedsdktests"
10+
compileSdk = flutter.compileSdkVersion
11+
ndkVersion = flutter.ndkVersion
12+
13+
compileOptions {
14+
sourceCompatibility = JavaVersion.VERSION_11
15+
targetCompatibility = JavaVersion.VERSION_11
16+
}
17+
18+
kotlinOptions {
19+
jvmTarget = JavaVersion.VERSION_11.toString()
20+
}
21+
22+
defaultConfig {
23+
applicationId = "com.revenuecat.automatedsdktests"
24+
minSdk = flutter.minSdkVersion
25+
targetSdk = flutter.targetSdkVersion
26+
versionCode = flutter.versionCode
27+
versionName = flutter.versionName
28+
}
29+
30+
buildTypes {
31+
release {
32+
signingConfig = signingConfigs.getByName("debug")
33+
}
34+
}
35+
}
36+
37+
flutter {
38+
source = "../.."
39+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2+
<!-- The INTERNET permission is required for development. Specifically,
3+
the Flutter tool needs it to communicate with the running application
4+
to allow setting breakpoints, to provide hot reload, etc.
5+
-->
6+
<uses-permission android:name="android.permission.INTERNET"/>
7+
</manifest>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2+
<application
3+
android:label="maestro_test_app"
4+
android:name="${applicationName}"
5+
android:icon="@mipmap/ic_launcher">
6+
<activity
7+
android:name=".MainActivity"
8+
android:exported="true"
9+
android:launchMode="singleTop"
10+
android:taskAffinity=""
11+
android:theme="@style/LaunchTheme"
12+
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
13+
android:hardwareAccelerated="true"
14+
android:windowSoftInputMode="adjustResize">
15+
<!-- Specifies an Android theme to apply to this Activity as soon as
16+
the Android process has started. This theme is visible to the user
17+
while the Flutter UI initializes. After that, this theme continues
18+
to determine the Window background behind the Flutter UI. -->
19+
<meta-data
20+
android:name="io.flutter.embedding.android.NormalTheme"
21+
android:resource="@style/NormalTheme"
22+
/>
23+
<intent-filter>
24+
<action android:name="android.intent.action.MAIN"/>
25+
<category android:name="android.intent.category.LAUNCHER"/>
26+
</intent-filter>
27+
</activity>
28+
<!-- Don't delete the meta-data below.
29+
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
30+
<meta-data
31+
android:name="flutterEmbedding"
32+
android:value="2" />
33+
</application>
34+
<!-- Required to query activities that can process text, see:
35+
https://developer.android.com/training/package-visibility and
36+
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
37+
38+
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
39+
<queries>
40+
<intent>
41+
<action android:name="android.intent.action.PROCESS_TEXT"/>
42+
<data android:mimeType="text/plain"/>
43+
</intent>
44+
</queries>
45+
</manifest>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.revenuecat.automatedsdktests
2+
3+
import io.flutter.embedding.android.FlutterFragmentActivity
4+
5+
class MainActivity : FlutterFragmentActivity()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- Modify this file to customize your launch splash screen -->
3+
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
4+
<item android:drawable="?android:colorBackground" />
5+
6+
<!-- You can insert your own image assets here -->
7+
<!-- <item>
8+
<bitmap
9+
android:gravity="center"
10+
android:src="@mipmap/launch_image" />
11+
</item> -->
12+
</layer-list>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- Modify this file to customize your launch splash screen -->
3+
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
4+
<item android:drawable="@android:color/white" />
5+
6+
<!-- You can insert your own image assets here -->
7+
<!-- <item>
8+
<bitmap
9+
android:gravity="center"
10+
android:src="@mipmap/launch_image" />
11+
</item> -->
12+
</layer-list>

0 commit comments

Comments
 (0)