diff --git a/.circleci/config.yml b/.circleci/config.yml index 594ed81fa..143c99192 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,7 @@ version: 2.1 orbs: rn: react-native-community/react-native@8.0.1 + android: circleci/android@3.1.0 revenuecat: revenuecat/sdks-common-config@3.16.0 aliases: @@ -288,6 +289,67 @@ jobs: name: release command: bundle exec fastlane release + run-maestro-e2e-tests-ios: + executor: + name: rn/macos + resource_class: m4pro.medium + xcode_version: '26.3' + steps: + - checkout + - install-ruby-3-2-0 + - install-dependencies: + machine: "macos" + - revenuecat/install-gem-mac-dependencies: + cache-version: v2 + - revenuecat/install-maestro + - run: + name: Boot iOS Simulator + command: | + xcrun simctl boot "iPhone 17" || true + xcrun simctl bootstatus "iPhone 17" -b + - run: + name: Run Maestro E2E Tests (iOS) + command: bundle exec fastlane run_maestro_e2e_tests_ios + no_output_timeout: 15m + - store_test_results: + path: fastlane/test_output + - store_artifacts: + path: fastlane/test_output + + run-maestro-e2e-tests-android: + machine: + image: android:2024.11.1 + resource_class: xlarge + steps: + - checkout + - run: + name: Install Ruby and Bundler + command: | + gem install bundler + bundle install + - install-dependencies: + machine: "unix" + - run: + name: Build Maestro app (Android) + command: bundle exec fastlane build_maestro_app_android + no_output_timeout: 15m + - android/create_avd: + avd_name: test-e2e + system_image: system-images;android-34;google_apis;x86_64 + install: true + - android/start_emulator: + avd_name: test-e2e + post_emulator_launch_assemble_command: "" + - revenuecat/install-maestro + - run: + name: Run Maestro E2E Tests (Android) + command: bundle exec fastlane run_maestro_e2e_tests_android + no_output_timeout: 15m + - store_test_results: + path: fastlane/test_output + - store_artifacts: + path: fastlane/test_output + update-hybrid-common-versions: description: "Creates a PR updating purchases-hybrid-common to latest release" <<: *base-mac-job @@ -321,6 +383,14 @@ workflows: - expo_android - expo_ios - expo_web + - run-maestro-e2e-tests-ios: + context: + - maestro-e2e-tests + - e2e-tests + - run-maestro-e2e-tests-android: + context: + - maestro-e2e-tests + - e2e-tests # Hold and deploy phase - only on release branches - hold: @@ -367,6 +437,26 @@ workflows: jobs: - revenuecat/automatic-bump + maestro-e2e-tests-ios: + when: + or: + - equal: ["maestro_e2e_tests", << pipeline.schedule.name >>] + jobs: + - run-maestro-e2e-tests-ios: + context: + - maestro-e2e-tests + - e2e-tests + + maestro-e2e-tests-android: + when: + or: + - equal: ["maestro_e2e_tests", << pipeline.schedule.name >>] + jobs: + - run-maestro-e2e-tests-android: + context: + - maestro-e2e-tests + - e2e-tests + update-hybrid-common-versions: when: equal: [ upgrade-hybrid-common, << pipeline.parameters.action >> ] diff --git a/e2e-tests/MaestroTestApp/App.tsx b/e2e-tests/MaestroTestApp/App.tsx index 3db3bd941..aa43e0879 100644 --- a/e2e-tests/MaestroTestApp/App.tsx +++ b/e2e-tests/MaestroTestApp/App.tsx @@ -14,15 +14,27 @@ const Stack = createNativeStackNavigator(); const API_KEY = 'MAESTRO_TESTS_REVENUECAT_API_KEY'; -export default function App() { +const TEST_FLOW_SCREEN_MAP: Record = { + purchase_through_paywall: 'PurchaseThroughPaywall', +}; + +type AppProps = { + e2e_test_flow?: string; +}; + +export default function App(props: AppProps) { useEffect(() => { Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG); Purchases.configure({apiKey: API_KEY}); }, []); + const initialRoute = + (props.e2e_test_flow && TEST_FLOW_SCREEN_MAP[props.e2e_test_flow]) || + 'TestCases'; + return ( - + Bool { self.moduleName = "MaestroTestApp" self.dependencyProvider = RCTAppDependencyProvider() - self.initialProps = [:] + + var props: [String: Any] = [:] + if let testFlow = UserDefaults.standard.string(forKey: "e2e_test_flow") { + props["e2e_test_flow"] = testFlow + } + self.initialProps = props return super.application(application, didFinishLaunchingWithOptions: launchOptions) } diff --git a/e2e-tests/maestro/e2e_tests/purchase_through_paywall.yaml b/e2e-tests/maestro/e2e_tests/purchase_through_paywall.yaml index 955c50004..6650f0dbb 100644 --- a/e2e-tests/maestro/e2e_tests/purchase_through_paywall.yaml +++ b/e2e-tests/maestro/e2e_tests/purchase_through_paywall.yaml @@ -8,11 +8,12 @@ name: Purchase through paywall --- - clearState - pressKey: home -- launchApp -- assertVisible: "Test Cases" -- tapOn: - text: "Purchase through paywall" -- assertVisible: "Entitlements: none" +- launchApp: + arguments: + e2e_test_flow: purchase_through_paywall +- extendedWaitUntil: + visible: "Entitlements: none" + timeout: 30000 - assertVisible: "Present Paywall" - tapOn: text: "Present Paywall" diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 431984e81..9f770a48c 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -319,6 +319,37 @@ def check_no_git_tag_exists(version_number) end end +desc "Run maestro E2E tests on iOS" +lane :run_maestro_e2e_tests_ios do + Dir.chdir("../e2e-tests/MaestroTestApp") do + sh("sed -i '' 's/MAESTRO_TESTS_REVENUECAT_API_KEY/'\"$RC_E2E_TEST_API_KEY_PRODUCTION_TEST_STORE\"'/g' App.tsx") + sh("grep -q MAESTRO_TESTS_REVENUECAT_API_KEY App.tsx && echo 'ERROR: API key placeholder was not replaced' && exit 1 || true") + sh("YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install") + sh("rm -rf ios/Pods && cd ios && pod install --repo-update") + sh("FORCE_BUNDLING=1 xcodebuild -workspace ios/MaestroTestApp.xcworkspace -scheme MaestroTestApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17' -derivedDataPath build") + sh("xcrun simctl install booted build/Build/Products/Debug-iphonesimulator/MaestroTestApp.app") + end + sh("mkdir -p test_output") + sh("maestro test --format junit --output test_output/report.xml --test-output-dir test_output ../e2e-tests/maestro/") +end + +desc "Build maestro E2E test app for Android" +lane :build_maestro_app_android do + Dir.chdir("../e2e-tests/MaestroTestApp") do + sh("sed -i 's/MAESTRO_TESTS_REVENUECAT_API_KEY/'\"$RC_E2E_TEST_API_KEY_PRODUCTION_TEST_STORE\"'/g' App.tsx") + sh("grep -q MAESTRO_TESTS_REVENUECAT_API_KEY App.tsx && echo 'ERROR: API key placeholder was not replaced' && exit 1 || true") + sh("YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install") + sh("cd android && ./gradlew assembleDebug") + end +end + +desc "Run maestro E2E tests on Android (emulator must be running)" +lane :run_maestro_e2e_tests_android do + sh("adb install ../e2e-tests/MaestroTestApp/android/app/build/outputs/apk/debug/app-debug.apk") + sh("mkdir -p test_output") + sh("maestro test --format junit --output test_output/report.xml --test-output-dir test_output ../e2e-tests/maestro/") +end + private_lane :update_sample_podfile_lock_with_retry do |options| pod_name = options[:pod_name]