diff --git a/e2e-tests/MaestroTestApp/.gitignore b/e2e-tests/MaestroTestApp/.gitignore
new file mode 100644
index 000000000..75d7570d8
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/.gitignore
@@ -0,0 +1,54 @@
+# OSX
+.DS_Store
+
+# Xcode
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+*.xcuserstate
+**/.xcode.env.local
+
+# Android/IntelliJ
+.idea
+.gradle
+local.properties
+*.iml
+*.hprof
+.cxx/
+*.keystore
+!debug.keystore
+
+# node.js
+node_modules/
+npm-debug.log
+yarn-error.log
+
+# Ruby / CocoaPods
+**/Pods/
+/vendor/bundle/
+
+# Bundle artifact
+*.jsbundle
+
+# Temporary files created by Metro to check the health of the file watcher
+.metro-health-check*
+
+# Yarn
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/sdks
+!.yarn/versions
diff --git a/e2e-tests/MaestroTestApp/App.tsx b/e2e-tests/MaestroTestApp/App.tsx
new file mode 100644
index 000000000..52205f63a
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/App.tsx
@@ -0,0 +1,34 @@
+import React, {useEffect} from 'react';
+import {NavigationContainer} from '@react-navigation/native';
+import {createNativeStackNavigator} from '@react-navigation/native-stack';
+import Purchases from 'react-native-purchases';
+import TestCasesScreen from './src/TestCasesScreen';
+import PurchaseThroughPaywallScreen from './src/PurchaseThroughPaywallScreen';
+
+const Stack = createNativeStackNavigator();
+
+const API_KEY = 'MAESTRO_TESTS_REVENUECAT_API_KEY';
+
+export default function App() {
+ useEffect(() => {
+ Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG);
+ Purchases.configure({apiKey: API_KEY});
+ }, []);
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/e2e-tests/MaestroTestApp/app.json b/e2e-tests/MaestroTestApp/app.json
new file mode 100644
index 000000000..83f7d39aa
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/app.json
@@ -0,0 +1,4 @@
+{
+ "name": "MaestroTestApp",
+ "displayName": "MaestroTestApp"
+}
diff --git a/e2e-tests/MaestroTestApp/babel.config.js b/e2e-tests/MaestroTestApp/babel.config.js
new file mode 100644
index 000000000..f7b3da3b3
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ presets: ['module:@react-native/babel-preset'],
+};
diff --git a/e2e-tests/MaestroTestApp/index.js b/e2e-tests/MaestroTestApp/index.js
new file mode 100644
index 000000000..7cd584caf
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/index.js
@@ -0,0 +1,5 @@
+import {AppRegistry} from 'react-native';
+import App from './App';
+import {name as appName} from './app.json';
+
+AppRegistry.registerComponent(appName, () => App);
diff --git a/e2e-tests/MaestroTestApp/ios/.xcode.env b/e2e-tests/MaestroTestApp/ios/.xcode.env
new file mode 100644
index 000000000..3d5782c71
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/.xcode.env
@@ -0,0 +1,11 @@
+# This `.xcode.env` file is versioned and is used to source the environment
+# used when running script phases inside Xcode.
+# To customize your local environment, you can create an `.xcode.env.local`
+# file that is not versioned.
+
+# NODE_BINARY variable contains the PATH to the node executable.
+#
+# Customize the NODE_BINARY variable here.
+# For example, to use nvm with brew, add the following line
+# . "$(brew --prefix nvm)/nvm.sh" --no-use
+export NODE_BINARY=$(command -v node)
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestApp.xcodeproj/project.pbxproj b/e2e-tests/MaestroTestApp/ios/MaestroTestApp.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..e75efffff
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestApp.xcodeproj/project.pbxproj
@@ -0,0 +1,699 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 00E356F31AD99517003FC87E /* MaestroTestAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* MaestroTestAppTests.m */; };
+ 0C80B921A6F3F58F76C31292 /* libPods-MaestroTestApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-MaestroTestApp.a */; };
+ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
+ 68A792BEA0547B4A08428A6B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
+ 7699B88040F8A987B510C191 /* libPods-MaestroTestApp-MaestroTestAppTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-MaestroTestApp-MaestroTestAppTests.a */; };
+ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
+ A1B2C3D4E5F60001 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60002 /* AppDelegate.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
+ remoteInfo = MaestroTestApp;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 00E356EE1AD99517003FC87E /* MaestroTestAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MaestroTestAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 00E356F21AD99517003FC87E /* MaestroTestAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MaestroTestAppTests.m; sourceTree = ""; };
+ 13B07F961A680F5B00A75B9A /* MaestroTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MaestroTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = MaestroTestApp/Images.xcassets; sourceTree = ""; };
+ 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = MaestroTestApp/Info.plist; sourceTree = ""; };
+ 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = MaestroTestApp/PrivacyInfo.xcprivacy; sourceTree = ""; };
+ 19F6CBCC0A4E27FBF8BF4A61 /* libPods-MaestroTestApp-MaestroTestAppTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MaestroTestApp-MaestroTestAppTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3B4392A12AC88292D35C810B /* Pods-MaestroTestApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MaestroTestApp.debug.xcconfig"; path = "Target Support Files/Pods-MaestroTestApp/Pods-MaestroTestApp.debug.xcconfig"; sourceTree = ""; };
+ 5709B34CF0A7D63546082F79 /* Pods-MaestroTestApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MaestroTestApp.release.xcconfig"; path = "Target Support Files/Pods-MaestroTestApp/Pods-MaestroTestApp.release.xcconfig"; sourceTree = ""; };
+ 5B7EB9410499542E8C5724F5 /* Pods-MaestroTestApp-MaestroTestAppTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MaestroTestApp-MaestroTestAppTests.debug.xcconfig"; path = "Target Support Files/Pods-MaestroTestApp-MaestroTestAppTests/Pods-MaestroTestApp-MaestroTestAppTests.debug.xcconfig"; sourceTree = ""; };
+ 5DCACB8F33CDC322A6C60F78 /* libPods-MaestroTestApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MaestroTestApp.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = MaestroTestApp/LaunchScreen.storyboard; sourceTree = ""; };
+ 89C6BE57DB24E9ADA2F236DE /* Pods-MaestroTestApp-MaestroTestAppTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MaestroTestApp-MaestroTestAppTests.release.xcconfig"; path = "Target Support Files/Pods-MaestroTestApp-MaestroTestAppTests/Pods-MaestroTestApp-MaestroTestAppTests.release.xcconfig"; sourceTree = ""; };
+ A1B2C3D4E5F60002 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = MaestroTestApp/AppDelegate.swift; sourceTree = ""; };
+ ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 00E356EB1AD99517003FC87E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7699B88040F8A987B510C191 /* libPods-MaestroTestApp-MaestroTestAppTests.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 0C80B921A6F3F58F76C31292 /* libPods-MaestroTestApp.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 00E356EF1AD99517003FC87E /* MaestroTestAppTests */ = {
+ isa = PBXGroup;
+ children = (
+ 00E356F21AD99517003FC87E /* MaestroTestAppTests.m */,
+ 00E356F01AD99517003FC87E /* Supporting Files */,
+ );
+ path = MaestroTestAppTests;
+ sourceTree = "";
+ };
+ 00E356F01AD99517003FC87E /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 00E356F11AD99517003FC87E /* Info.plist */,
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
+ 13B07FAE1A68108700A75B9A /* MaestroTestApp */ = {
+ isa = PBXGroup;
+ children = (
+ A1B2C3D4E5F60002 /* AppDelegate.swift */,
+ 13B07FB51A68108700A75B9A /* Images.xcassets */,
+ 13B07FB61A68108700A75B9A /* Info.plist */,
+ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
+ 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
+ );
+ name = MaestroTestApp;
+ sourceTree = "";
+ };
+ 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
+ 5DCACB8F33CDC322A6C60F78 /* libPods-MaestroTestApp.a */,
+ 19F6CBCC0A4E27FBF8BF4A61 /* libPods-MaestroTestApp-MaestroTestAppTests.a */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Libraries;
+ sourceTree = "";
+ };
+ 83CBB9F61A601CBA00E9B192 = {
+ isa = PBXGroup;
+ children = (
+ 13B07FAE1A68108700A75B9A /* MaestroTestApp */,
+ 832341AE1AAA6A7D00B99B32 /* Libraries */,
+ 00E356EF1AD99517003FC87E /* MaestroTestAppTests */,
+ 83CBBA001A601CBA00E9B192 /* Products */,
+ 2D16E6871FA4F8E400B85C8A /* Frameworks */,
+ BBD78D7AC51CEA395F1C20DB /* Pods */,
+ );
+ indentWidth = 2;
+ sourceTree = "";
+ tabWidth = 2;
+ usesTabs = 0;
+ };
+ 83CBBA001A601CBA00E9B192 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 13B07F961A680F5B00A75B9A /* MaestroTestApp.app */,
+ 00E356EE1AD99517003FC87E /* MaestroTestAppTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ BBD78D7AC51CEA395F1C20DB /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 3B4392A12AC88292D35C810B /* Pods-MaestroTestApp.debug.xcconfig */,
+ 5709B34CF0A7D63546082F79 /* Pods-MaestroTestApp.release.xcconfig */,
+ 5B7EB9410499542E8C5724F5 /* Pods-MaestroTestApp-MaestroTestAppTests.debug.xcconfig */,
+ 89C6BE57DB24E9ADA2F236DE /* Pods-MaestroTestApp-MaestroTestAppTests.release.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 00E356ED1AD99517003FC87E /* MaestroTestAppTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "MaestroTestAppTests" */;
+ buildPhases = (
+ A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */,
+ 00E356EA1AD99517003FC87E /* Sources */,
+ 00E356EB1AD99517003FC87E /* Frameworks */,
+ 00E356EC1AD99517003FC87E /* Resources */,
+ C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */,
+ F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 00E356F51AD99517003FC87E /* PBXTargetDependency */,
+ );
+ name = MaestroTestAppTests;
+ productName = MaestroTestAppTests;
+ productReference = 00E356EE1AD99517003FC87E /* MaestroTestAppTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 13B07F861A680F5B00A75B9A /* MaestroTestApp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "MaestroTestApp" */;
+ buildPhases = (
+ C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
+ 13B07F871A680F5B00A75B9A /* Sources */,
+ 13B07F8C1A680F5B00A75B9A /* Frameworks */,
+ 13B07F8E1A680F5B00A75B9A /* Resources */,
+ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
+ 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
+ E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = MaestroTestApp;
+ productName = MaestroTestApp;
+ productReference = 13B07F961A680F5B00A75B9A /* MaestroTestApp.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 83CBB9F71A601CBA00E9B192 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1210;
+ TargetAttributes = {
+ 00E356ED1AD99517003FC87E = {
+ CreatedOnToolsVersion = 6.2;
+ TestTargetID = 13B07F861A680F5B00A75B9A;
+ };
+ 13B07F861A680F5B00A75B9A = {
+ LastSwiftMigration = 1120;
+ };
+ };
+ };
+ buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "MaestroTestApp" */;
+ compatibilityVersion = "Xcode 12.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 83CBB9F61A601CBA00E9B192;
+ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 13B07F861A680F5B00A75B9A /* MaestroTestApp */,
+ 00E356ED1AD99517003FC87E /* MaestroTestAppTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 00E356EC1AD99517003FC87E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 13B07F8E1A680F5B00A75B9A /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
+ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
+ 68A792BEA0547B4A08428A6B /* PrivacyInfo.xcprivacy in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/.xcode.env.local",
+ "$(SRCROOT)/.xcode.env",
+ );
+ name = "Bundle React Native code and images";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
+ };
+ 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp/Pods-MaestroTestApp-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp/Pods-MaestroTestApp-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp/Pods-MaestroTestApp-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-MaestroTestApp-MaestroTestAppTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-MaestroTestApp-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp-MaestroTestAppTests/Pods-MaestroTestApp-MaestroTestAppTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp-MaestroTestAppTests/Pods-MaestroTestApp-MaestroTestAppTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp-MaestroTestAppTests/Pods-MaestroTestApp-MaestroTestAppTests-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp/Pods-MaestroTestApp-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp/Pods-MaestroTestApp-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp/Pods-MaestroTestApp-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp-MaestroTestAppTests/Pods-MaestroTestApp-MaestroTestAppTests-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp-MaestroTestAppTests/Pods-MaestroTestApp-MaestroTestAppTests-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MaestroTestApp-MaestroTestAppTests/Pods-MaestroTestApp-MaestroTestAppTests-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 00E356EA1AD99517003FC87E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 00E356F31AD99517003FC87E /* MaestroTestAppTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 13B07F871A680F5B00A75B9A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A1B2C3D4E5F60001 /* AppDelegate.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 13B07F861A680F5B00A75B9A /* MaestroTestApp */;
+ targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 00E356F61AD99517003FC87E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-MaestroTestApp-MaestroTestAppTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = MaestroTestAppTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-lc++",
+ "$(inherited)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.revenuecat.maestro.e2e;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MaestroTestApp.app/MaestroTestApp";
+ };
+ name = Debug;
+ };
+ 00E356F71AD99517003FC87E /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-MaestroTestApp-MaestroTestAppTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ COPY_PHASE_STRIP = NO;
+ INFOPLIST_FILE = MaestroTestAppTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-lc++",
+ "$(inherited)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.revenuecat.maestro.e2e;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MaestroTestApp.app/MaestroTestApp";
+ };
+ name = Release;
+ };
+ 13B07F941A680F5B00A75B9A /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-MaestroTestApp.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = MaestroTestApp/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-ObjC",
+ "-lc++",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.revenuecat.maestro.e2e;
+ PRODUCT_NAME = MaestroTestApp;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 13B07F951A680F5B00A75B9A /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-MaestroTestApp.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ INFOPLIST_FILE = MaestroTestApp/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-ObjC",
+ "-lc++",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.revenuecat.maestro.e2e;
+ PRODUCT_NAME = MaestroTestApp;
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+ 83CBBA201A601CBA00E9B192 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++20";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ /usr/lib/swift,
+ "$(inherited)",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "\"$(SDKROOT)/usr/lib/swift\"",
+ "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
+ "\"$(inherited)\"",
+ );
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "-DFOLLY_NO_CONFIG",
+ "-DFOLLY_MOBILE=1",
+ "-DFOLLY_USE_LIBCPP=1",
+ "-DFOLLY_CFG_NO_COROUTINES=1",
+ "-DFOLLY_HAVE_CLOCK_GETTIME=1",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ " ",
+ );
+ REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
+ USE_HERMES = true;
+ };
+ name = Debug;
+ };
+ 83CBBA211A601CBA00E9B192 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++20";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ /usr/lib/swift,
+ "$(inherited)",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "\"$(SDKROOT)/usr/lib/swift\"",
+ "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
+ "\"$(inherited)\"",
+ );
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "-DFOLLY_NO_CONFIG",
+ "-DFOLLY_MOBILE=1",
+ "-DFOLLY_USE_LIBCPP=1",
+ "-DFOLLY_CFG_NO_COROUTINES=1",
+ "-DFOLLY_HAVE_CLOCK_GETTIME=1",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ " ",
+ );
+ REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
+ SDKROOT = iphoneos;
+ USE_HERMES = true;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "MaestroTestAppTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00E356F61AD99517003FC87E /* Debug */,
+ 00E356F71AD99517003FC87E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "MaestroTestApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 13B07F941A680F5B00A75B9A /* Debug */,
+ 13B07F951A680F5B00A75B9A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "MaestroTestApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 83CBBA201A601CBA00E9B192 /* Debug */,
+ 83CBBA211A601CBA00E9B192 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
+}
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestApp.xcodeproj/xcshareddata/xcschemes/MaestroTestApp.xcscheme b/e2e-tests/MaestroTestApp/ios/MaestroTestApp.xcodeproj/xcshareddata/xcschemes/MaestroTestApp.xcscheme
new file mode 100644
index 000000000..8909e24c8
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestApp.xcodeproj/xcshareddata/xcschemes/MaestroTestApp.xcscheme
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestApp/AppDelegate.swift b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/AppDelegate.swift
new file mode 100644
index 000000000..8329018e9
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/AppDelegate.swift
@@ -0,0 +1,27 @@
+import UIKit
+import React
+import React_RCTAppDelegate
+
+@main
+class AppDelegate: RCTAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
+ ) -> Bool {
+ self.moduleName = "MaestroTestApp"
+ self.initialProps = [:]
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+
+ override func sourceURL(for bridge: RCTBridge) -> URL? {
+ return bundleURL()
+ }
+
+ override func bundleURL() -> URL {
+ #if DEBUG
+ return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")!
+ #else
+ return Bundle.main.url(forResource: "main", withExtension: "jsbundle")!
+ #endif
+ }
+}
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestApp/Images.xcassets/AppIcon.appiconset/Contents.json b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..81213230d
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,53 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestApp/Images.xcassets/Contents.json b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/Images.xcassets/Contents.json
new file mode 100644
index 000000000..2d92bd53f
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/Images.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestApp/Info.plist b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/Info.plist
new file mode 100644
index 000000000..7330cfb5e
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/Info.plist
@@ -0,0 +1,52 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ MaestroTestApp
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ LSRequiresIPhoneOS
+
+ NSAppTransportSecurity
+
+
+ NSAllowsArbitraryLoads
+
+ NSAllowsLocalNetworking
+
+
+ NSLocationWhenInUseUsageDescription
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestApp/LaunchScreen.storyboard b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/LaunchScreen.storyboard
new file mode 100644
index 000000000..bd7ab2e26
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/LaunchScreen.storyboard
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestApp/PrivacyInfo.xcprivacy b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/PrivacyInfo.xcprivacy
new file mode 100644
index 000000000..bad327615
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestApp/PrivacyInfo.xcprivacy
@@ -0,0 +1,37 @@
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+ NSPrivacyAccessedAPITypeReasons
+
+ CA92.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategorySystemBootTime
+ NSPrivacyAccessedAPITypeReasons
+
+ 35F9.1
+
+
+
+ NSPrivacyCollectedDataTypes
+
+ NSPrivacyTracking
+
+
+
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestAppTests/Info.plist b/e2e-tests/MaestroTestApp/ios/MaestroTestAppTests/Info.plist
new file mode 100644
index 000000000..ba72822e8
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestAppTests/Info.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ BNDL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+
+
diff --git a/e2e-tests/MaestroTestApp/ios/MaestroTestAppTests/MaestroTestAppTests.m b/e2e-tests/MaestroTestApp/ios/MaestroTestAppTests/MaestroTestAppTests.m
new file mode 100644
index 000000000..05077c811
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/MaestroTestAppTests/MaestroTestAppTests.m
@@ -0,0 +1,66 @@
+#import
+#import
+
+#import
+#import
+
+#define TIMEOUT_SECONDS 600
+#define TEXT_TO_LOOK_FOR @"Welcome to React"
+
+@interface MaestroTestAppTests : XCTestCase
+
+@end
+
+@implementation MaestroTestAppTests
+
+- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
+{
+ if (test(view)) {
+ return YES;
+ }
+ for (UIView *subview in [view subviews]) {
+ if ([self findSubviewInView:subview matching:test]) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (void)testRendersWelcomeScreen
+{
+ UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
+ NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
+ BOOL foundElement = NO;
+
+ __block NSString *redboxError = nil;
+#ifdef DEBUG
+ RCTSetLogFunction(
+ ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
+ if (level >= RCTLogLevelError) {
+ redboxError = message;
+ }
+ });
+#endif
+
+ while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
+ [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+ [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+
+ foundElement = [self findSubviewInView:vc.view
+ matching:^BOOL(UIView *view) {
+ if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
+ return YES;
+ }
+ return NO;
+ }];
+ }
+
+#ifdef DEBUG
+ RCTSetLogFunction(RCTDefaultLogFunction);
+#endif
+
+ XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
+ XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
+}
+
+@end
diff --git a/e2e-tests/MaestroTestApp/ios/Podfile b/e2e-tests/MaestroTestApp/ios/Podfile
new file mode 100644
index 000000000..55ebcf33b
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/ios/Podfile
@@ -0,0 +1,37 @@
+# Resolve react_native_pods.rb with node to allow for hoisting
+require Pod::Executable.execute_command('node', ['-p',
+ 'require.resolve(
+ "react-native/scripts/react_native_pods.rb",
+ {paths: [process.argv[1]]},
+ )', __dir__]).strip
+
+platform :ios, min_ios_version_supported
+prepare_react_native_project!
+
+linkage = ENV['USE_FRAMEWORKS']
+if linkage != nil
+ Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
+ use_frameworks! :linkage => linkage.to_sym
+end
+
+target 'MaestroTestApp' do
+ config = use_native_modules!
+
+ use_react_native!(
+ :path => config[:reactNativePath],
+ :app_path => "#{Pod::Config.instance.installation_root}/.."
+ )
+
+ target 'MaestroTestAppTests' do
+ inherit! :complete
+ # Pods for testing
+ end
+
+ post_install do |installer|
+ react_native_post_install(
+ installer,
+ config[:reactNativePath],
+ :mac_catalyst_enabled => false,
+ )
+ end
+end
diff --git a/e2e-tests/MaestroTestApp/metro.config.js b/e2e-tests/MaestroTestApp/metro.config.js
new file mode 100644
index 000000000..cf31da0fa
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/metro.config.js
@@ -0,0 +1,5 @@
+const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
+
+const config = {};
+
+module.exports = mergeConfig(getDefaultConfig(__dirname), config);
diff --git a/e2e-tests/MaestroTestApp/package.json b/e2e-tests/MaestroTestApp/package.json
new file mode 100644
index 000000000..6275b8746
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "MaestroTestApp",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "android": "react-native run-android",
+ "ios": "react-native run-ios",
+ "start": "react-native start",
+ "build": "tsc",
+ "lint": "eslint ."
+ },
+ "dependencies": {
+ "@react-navigation/native": "^7.0.0",
+ "@react-navigation/native-stack": "^7.0.0",
+ "react": "^18.3.1",
+ "react-native": "^0.76.0",
+ "react-native-purchases": "*",
+ "react-native-purchases-ui": "*",
+ "react-native-safe-area-context": "^4.14.0",
+ "react-native-screens": "~4.4.0"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.25.0",
+ "@babel/preset-env": "^7.25.0",
+ "@babel/runtime": "^7.25.0",
+ "@react-native-community/cli": "^15.0.0",
+ "@react-native/babel-preset": "^0.76.0",
+ "@react-native/metro-config": "^0.76.0",
+ "@types/react": "^18.2.44",
+ "typescript": "~5.2.0"
+ }
+}
diff --git a/e2e-tests/MaestroTestApp/src/PurchaseThroughPaywallScreen.tsx b/e2e-tests/MaestroTestApp/src/PurchaseThroughPaywallScreen.tsx
new file mode 100644
index 000000000..a4b422a14
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/src/PurchaseThroughPaywallScreen.tsx
@@ -0,0 +1,53 @@
+import React, {useState, useEffect} from 'react';
+import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';
+import Purchases, {CustomerInfo} from 'react-native-purchases';
+import RevenueCatUI from 'react-native-purchases-ui';
+
+export default function PurchaseThroughPaywallScreen() {
+ const [entitlements, setEntitlements] = useState('none');
+
+ useEffect(() => {
+ const listener = Purchases.addCustomerInfoUpdateListener(
+ (info: CustomerInfo) => {
+ updateEntitlements(info);
+ },
+ );
+ Purchases.getCustomerInfo().then(updateEntitlements);
+ return () => {
+ listener.remove();
+ };
+ }, []);
+
+ const updateEntitlements = (info: CustomerInfo) => {
+ const pro = info.entitlements.active['pro'];
+ setEntitlements(pro ? 'pro' : 'none');
+ };
+
+ const presentPaywall = async () => {
+ await RevenueCatUI.presentPaywall();
+ };
+
+ return (
+
+ Entitlements: {entitlements}
+
+ Present Paywall
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: 16,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ label: {fontSize: 18, marginBottom: 20},
+ button: {padding: 16, backgroundColor: '#007AFF', borderRadius: 8},
+ buttonText: {color: 'white', fontSize: 16},
+});
diff --git a/e2e-tests/MaestroTestApp/src/TestCasesScreen.tsx b/e2e-tests/MaestroTestApp/src/TestCasesScreen.tsx
new file mode 100644
index 000000000..3f6d507de
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/src/TestCasesScreen.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';
+
+export default function TestCasesScreen({navigation}: any) {
+ return (
+
+ navigation.navigate('PurchaseThroughPaywall')}
+ accessibilityLabel="Purchase through paywall">
+ Purchase through paywall
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {flex: 1, padding: 16},
+ button: {
+ padding: 16,
+ backgroundColor: '#007AFF',
+ borderRadius: 8,
+ marginVertical: 4,
+ },
+ buttonText: {color: 'white', fontSize: 16},
+});
diff --git a/e2e-tests/MaestroTestApp/tsconfig.json b/e2e-tests/MaestroTestApp/tsconfig.json
new file mode 100644
index 000000000..e554295ff
--- /dev/null
+++ b/e2e-tests/MaestroTestApp/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "module": "commonjs",
+ "lib": ["es2017"],
+ "allowJs": true,
+ "jsx": "react-native",
+ "noEmit": true,
+ "isolatedModules": true,
+ "strict": true,
+ "moduleResolution": "node",
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "resolveJsonModule": true
+ },
+ "exclude": ["node_modules", "babel.config.js", "metro.config.js"]
+}
diff --git a/e2e-tests/MaestroTestApp/yarn.lock b/e2e-tests/MaestroTestApp/yarn.lock
new file mode 100644
index 000000000..e69de29bb