From 31301bb16cdade0cc5924682d2d08eb7fd0750a7 Mon Sep 17 00:00:00 2001 From: floydkim Date: Sun, 1 Mar 2026 22:25:28 +0900 Subject: [PATCH 1/5] chore(e2e): dismiss Android system overlays before flow assertions --- e2e/flows-partial-rollback/01-update-to-latest.yaml | 4 ++++ .../02-rollback-to-previous.yaml | 4 ++++ e2e/flows-rollback/01-rollback.yaml | 4 ++++ e2e/flows/01-app-launch.yaml | 6 +++++- e2e/flows/02-restart-no-crash.yaml | 6 +++++- e2e/flows/03-update-flow.yaml | 4 ++++ e2e/flows/shared/android-dismiss-overlays.yaml | 11 +++++++++++ 7 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 e2e/flows/shared/android-dismiss-overlays.yaml diff --git a/e2e/flows-partial-rollback/01-update-to-latest.yaml b/e2e/flows-partial-rollback/01-update-to-latest.yaml index 0616a3d3..fbe02231 100644 --- a/e2e/flows-partial-rollback/01-update-to-latest.yaml +++ b/e2e/flows-partial-rollback/01-update-to-latest.yaml @@ -2,6 +2,10 @@ appId: ${APP_ID} --- # App should be on binary (after phase 2 rollback) - launchApp +- runFlow: + when: + platform: Android + file: ../flows/shared/android-dismiss-overlays.yaml - assertVisible: "React Native.*" - assertNotVisible: "UPDATED!" diff --git a/e2e/flows-partial-rollback/02-rollback-to-previous.yaml b/e2e/flows-partial-rollback/02-rollback-to-previous.yaml index db910b00..9b024e41 100644 --- a/e2e/flows-partial-rollback/02-rollback-to-previous.yaml +++ b/e2e/flows-partial-rollback/02-rollback-to-previous.yaml @@ -2,6 +2,10 @@ appId: ${APP_ID} --- # App should be on 1.0.2 (from previous update) - launchApp +- runFlow: + when: + platform: Android + file: ../flows/shared/android-dismiss-overlays.yaml - assertVisible: "UPDATED!" - tapOn: "Get update metadata" - waitForAnimationToEnd: diff --git a/e2e/flows-rollback/01-rollback.yaml b/e2e/flows-rollback/01-rollback.yaml index 8f6a56be..ba61bf60 100644 --- a/e2e/flows-rollback/01-rollback.yaml +++ b/e2e/flows-rollback/01-rollback.yaml @@ -2,6 +2,10 @@ appId: ${APP_ID} --- # App should have the update installed from phase 1 - launchApp +- runFlow: + when: + platform: Android + file: ../flows/shared/android-dismiss-overlays.yaml - assertVisible: "UPDATED!" # Check for updates — server has disabled the release, triggers rollback to binary diff --git a/e2e/flows/01-app-launch.yaml b/e2e/flows/01-app-launch.yaml index 8ca6a7cf..ee92a270 100644 --- a/e2e/flows/01-app-launch.yaml +++ b/e2e/flows/01-app-launch.yaml @@ -1,7 +1,11 @@ appId: ${APP_ID} --- - launchApp +- runFlow: + when: + platform: Android + file: shared/android-dismiss-overlays.yaml - assertVisible: "React Native.*" - assertVisible: "Check for updates" - assertVisible: "Clear updates" -- assertVisible: "Restart app" \ No newline at end of file +- assertVisible: "Restart app" diff --git a/e2e/flows/02-restart-no-crash.yaml b/e2e/flows/02-restart-no-crash.yaml index 4d760768..340c5a4a 100644 --- a/e2e/flows/02-restart-no-crash.yaml +++ b/e2e/flows/02-restart-no-crash.yaml @@ -1,9 +1,13 @@ appId: ${APP_ID} --- - launchApp +- runFlow: + when: + platform: Android + file: shared/android-dismiss-overlays.yaml - assertVisible: "React Native.*" - tapOn: "Restart app" - waitForAnimationToEnd: timeout: 5000 - assertVisible: "React Native.*" -- assertVisible: "Check for updates" \ No newline at end of file +- assertVisible: "Check for updates" diff --git a/e2e/flows/03-update-flow.yaml b/e2e/flows/03-update-flow.yaml index c940ec4c..7a55ad97 100644 --- a/e2e/flows/03-update-flow.yaml +++ b/e2e/flows/03-update-flow.yaml @@ -1,6 +1,10 @@ appId: ${APP_ID} --- - launchApp +- runFlow: + when: + platform: Android + file: shared/android-dismiss-overlays.yaml - assertVisible: "React Native.*" # Ensure clean state — clear any previous updates diff --git a/e2e/flows/shared/android-dismiss-overlays.yaml b/e2e/flows/shared/android-dismiss-overlays.yaml new file mode 100644 index 00000000..13af93d9 --- /dev/null +++ b/e2e/flows/shared/android-dismiss-overlays.yaml @@ -0,0 +1,11 @@ +--- +# Dismiss common Android system prompts that can cover the app right after launch in CI. +- tapOn: + text: "(?i)^(allow|ok|continue|got it|while using the app|only this time|allow all the time)$" + optional: true +- tapOn: + text: "(?i)^(allow|ok|continue|got it|while using the app|only this time|allow all the time)$" + optional: true +- tapOn: + text: "(?i)^wait$" + optional: true From e511b44c9c2f560ab6dbb2afcc2584c56d6c85fa Mon Sep 17 00:00:00 2001 From: floydkim Date: Sun, 1 Mar 2026 22:33:13 +0900 Subject: [PATCH 2/5] chore(e2e): dismiss iOS system overlays before flow assertions --- e2e/flows-partial-rollback/01-update-to-latest.yaml | 4 ++++ e2e/flows-partial-rollback/02-rollback-to-previous.yaml | 4 ++++ e2e/flows-rollback/01-rollback.yaml | 4 ++++ e2e/flows/01-app-launch.yaml | 4 ++++ e2e/flows/02-restart-no-crash.yaml | 4 ++++ e2e/flows/03-update-flow.yaml | 4 ++++ e2e/flows/shared/ios-dismiss-overlays.yaml | 8 ++++++++ 7 files changed, 32 insertions(+) create mode 100644 e2e/flows/shared/ios-dismiss-overlays.yaml diff --git a/e2e/flows-partial-rollback/01-update-to-latest.yaml b/e2e/flows-partial-rollback/01-update-to-latest.yaml index fbe02231..aab6efba 100644 --- a/e2e/flows-partial-rollback/01-update-to-latest.yaml +++ b/e2e/flows-partial-rollback/01-update-to-latest.yaml @@ -6,6 +6,10 @@ appId: ${APP_ID} when: platform: Android file: ../flows/shared/android-dismiss-overlays.yaml +- runFlow: + when: + platform: iOS + file: ../flows/shared/ios-dismiss-overlays.yaml - assertVisible: "React Native.*" - assertNotVisible: "UPDATED!" diff --git a/e2e/flows-partial-rollback/02-rollback-to-previous.yaml b/e2e/flows-partial-rollback/02-rollback-to-previous.yaml index 9b024e41..133e6bb4 100644 --- a/e2e/flows-partial-rollback/02-rollback-to-previous.yaml +++ b/e2e/flows-partial-rollback/02-rollback-to-previous.yaml @@ -6,6 +6,10 @@ appId: ${APP_ID} when: platform: Android file: ../flows/shared/android-dismiss-overlays.yaml +- runFlow: + when: + platform: iOS + file: ../flows/shared/ios-dismiss-overlays.yaml - assertVisible: "UPDATED!" - tapOn: "Get update metadata" - waitForAnimationToEnd: diff --git a/e2e/flows-rollback/01-rollback.yaml b/e2e/flows-rollback/01-rollback.yaml index ba61bf60..49efd491 100644 --- a/e2e/flows-rollback/01-rollback.yaml +++ b/e2e/flows-rollback/01-rollback.yaml @@ -6,6 +6,10 @@ appId: ${APP_ID} when: platform: Android file: ../flows/shared/android-dismiss-overlays.yaml +- runFlow: + when: + platform: iOS + file: ../flows/shared/ios-dismiss-overlays.yaml - assertVisible: "UPDATED!" # Check for updates — server has disabled the release, triggers rollback to binary diff --git a/e2e/flows/01-app-launch.yaml b/e2e/flows/01-app-launch.yaml index ee92a270..ec4e8b0d 100644 --- a/e2e/flows/01-app-launch.yaml +++ b/e2e/flows/01-app-launch.yaml @@ -5,6 +5,10 @@ appId: ${APP_ID} when: platform: Android file: shared/android-dismiss-overlays.yaml +- runFlow: + when: + platform: iOS + file: shared/ios-dismiss-overlays.yaml - assertVisible: "React Native.*" - assertVisible: "Check for updates" - assertVisible: "Clear updates" diff --git a/e2e/flows/02-restart-no-crash.yaml b/e2e/flows/02-restart-no-crash.yaml index 340c5a4a..2e88a0a1 100644 --- a/e2e/flows/02-restart-no-crash.yaml +++ b/e2e/flows/02-restart-no-crash.yaml @@ -5,6 +5,10 @@ appId: ${APP_ID} when: platform: Android file: shared/android-dismiss-overlays.yaml +- runFlow: + when: + platform: iOS + file: shared/ios-dismiss-overlays.yaml - assertVisible: "React Native.*" - tapOn: "Restart app" - waitForAnimationToEnd: diff --git a/e2e/flows/03-update-flow.yaml b/e2e/flows/03-update-flow.yaml index 7a55ad97..3b9468ce 100644 --- a/e2e/flows/03-update-flow.yaml +++ b/e2e/flows/03-update-flow.yaml @@ -5,6 +5,10 @@ appId: ${APP_ID} when: platform: Android file: shared/android-dismiss-overlays.yaml +- runFlow: + when: + platform: iOS + file: shared/ios-dismiss-overlays.yaml - assertVisible: "React Native.*" # Ensure clean state — clear any previous updates diff --git a/e2e/flows/shared/ios-dismiss-overlays.yaml b/e2e/flows/shared/ios-dismiss-overlays.yaml new file mode 100644 index 00000000..c012994a --- /dev/null +++ b/e2e/flows/shared/ios-dismiss-overlays.yaml @@ -0,0 +1,8 @@ +--- +# Dismiss common iOS system prompts that can cover the app right after launch in CI. +- tapOn: + text: "(?i)^(allow|ok|continue|not now|allow once|allow while using app|allow paste)$" + optional: true +- tapOn: + text: "(?i)^(allow|ok|continue|not now|allow once|allow while using app|allow paste)$" + optional: true From 6969a3ae6ea68f66ef1cc174222dd0db4442e624 Mon Sep 17 00:00:00 2001 From: floydkim Date: Sun, 1 Mar 2026 23:10:22 +0900 Subject: [PATCH 3/5] chore(e2e): use simulator UDID for iOS CI runs --- .github/workflows/e2e-matrix.yml | 28 +++++++++++++++++++++------- e2e/helpers/build-app.ts | 3 ++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e-matrix.yml b/.github/workflows/e2e-matrix.yml index 7024c55a..eee25c70 100644 --- a/.github/workflows/e2e-matrix.yml +++ b/.github/workflows/e2e-matrix.yml @@ -98,14 +98,28 @@ jobs: - name: Select iOS simulator id: boot-simulator run: | - SIMULATOR_NAME="$(xcrun simctl list devices available | awk -F '[()]' '/iPhone/ {print $1; exit}' | xargs)" - if [ -z "$SIMULATOR_NAME" ]; then + SIMULATOR_UDID="$(xcrun simctl list devices available -j | node -e ' + const fs = require("fs"); + const input = fs.readFileSync(0, "utf8"); + const data = JSON.parse(input); + const runtimes = Object.values(data.devices); + for (const devices of runtimes) { + for (const device of devices) { + if (device.isAvailable && typeof device.name === "string" && device.name.startsWith("iPhone")) { + process.stdout.write(device.udid); + process.exit(0); + } + } + } + process.exit(1); + ')" + if [ -z "$SIMULATOR_UDID" ]; then echo "No available iPhone simulator found." exit 1 fi - xcrun simctl boot "$SIMULATOR_NAME" || true - xcrun simctl bootstatus "$SIMULATOR_NAME" -b - echo "simulator=$SIMULATOR_NAME" >> "$GITHUB_OUTPUT" + xcrun simctl boot "$SIMULATOR_UDID" || true + xcrun simctl bootstatus "$SIMULATOR_UDID" -b + echo "simulator_udid=$SIMULATOR_UDID" >> "$GITHUB_OUTPUT" - name: Reset Xcode module cache run: rm -rf ~/Library/Developer/Xcode/DerivedData/ModuleCache.noindex @@ -117,9 +131,9 @@ jobs: E2E_RETRY_DELAY_SEC: "30" run: | if [ "${{ startsWith(matrix.app, 'Expo') }}" = "true" ]; then - npm run e2e -- --app "${{ matrix.app }}" --platform ios --simulator "${{ steps.boot-simulator.outputs.simulator }}" --framework expo --retry-count "$E2E_RETRY_COUNT" --retry-delay-sec "$E2E_RETRY_DELAY_SEC" + npm run e2e -- --app "${{ matrix.app }}" --platform ios --simulator "${{ steps.boot-simulator.outputs.simulator_udid }}" --framework expo --retry-count "$E2E_RETRY_COUNT" --retry-delay-sec "$E2E_RETRY_DELAY_SEC" else - npm run e2e -- --app "${{ matrix.app }}" --platform ios --simulator "${{ steps.boot-simulator.outputs.simulator }}" --retry-count "$E2E_RETRY_COUNT" --retry-delay-sec "$E2E_RETRY_DELAY_SEC" + npm run e2e -- --app "${{ matrix.app }}" --platform ios --simulator "${{ steps.boot-simulator.outputs.simulator_udid }}" --retry-count "$E2E_RETRY_COUNT" --retry-delay-sec "$E2E_RETRY_DELAY_SEC" fi e2e-android: diff --git a/e2e/helpers/build-app.ts b/e2e/helpers/build-app.ts index c0fbb84c..4de080f1 100644 --- a/e2e/helpers/build-app.ts +++ b/e2e/helpers/build-app.ts @@ -59,11 +59,12 @@ async function buildIos(appPath: string, simulator?: string): Promise { await executeCommand("npm", ["run", "setup:pods"], appPath); const sim = simulator ?? getBootedSimulator()?.name ?? DEFAULT_SIMULATOR; + const targetSimulator = resolveSimulator(sim); const args = [ "react-native", "run-ios", "--mode", "Release", "--no-packager", - "--simulator", sim, + "--udid", targetSimulator.udid, ]; console.log(`[command] npx ${args.join(" ")} (cwd: ${appPath})`); return executeCommand("npx", args, appPath); From 324341b2523df550b7fdc067c219c1f5c8eceecf Mon Sep 17 00:00:00 2001 From: floydkim Date: Sun, 1 Mar 2026 23:14:25 +0900 Subject: [PATCH 4/5] chore(e2e): add config headers to shared maestro overlay flows --- e2e/flows/shared/android-dismiss-overlays.yaml | 1 + e2e/flows/shared/ios-dismiss-overlays.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/e2e/flows/shared/android-dismiss-overlays.yaml b/e2e/flows/shared/android-dismiss-overlays.yaml index 13af93d9..b4559e5f 100644 --- a/e2e/flows/shared/android-dismiss-overlays.yaml +++ b/e2e/flows/shared/android-dismiss-overlays.yaml @@ -1,3 +1,4 @@ +appId: ${APP_ID} --- # Dismiss common Android system prompts that can cover the app right after launch in CI. - tapOn: diff --git a/e2e/flows/shared/ios-dismiss-overlays.yaml b/e2e/flows/shared/ios-dismiss-overlays.yaml index c012994a..932021bd 100644 --- a/e2e/flows/shared/ios-dismiss-overlays.yaml +++ b/e2e/flows/shared/ios-dismiss-overlays.yaml @@ -1,3 +1,4 @@ +appId: ${APP_ID} --- # Dismiss common iOS system prompts that can cover the app right after launch in CI. - tapOn: From b763e5f257a9e43d9fc82f753427aeb5a7858d7f Mon Sep 17 00:00:00 2001 From: floydkim Date: Sun, 1 Mar 2026 23:51:30 +0900 Subject: [PATCH 5/5] chore(e2e): run overlay dismissal only in initial flow --- e2e/flows-partial-rollback/01-update-to-latest.yaml | 8 -------- e2e/flows-partial-rollback/02-rollback-to-previous.yaml | 8 -------- e2e/flows-rollback/01-rollback.yaml | 8 -------- e2e/flows/02-restart-no-crash.yaml | 8 -------- e2e/flows/03-update-flow.yaml | 8 -------- 5 files changed, 40 deletions(-) diff --git a/e2e/flows-partial-rollback/01-update-to-latest.yaml b/e2e/flows-partial-rollback/01-update-to-latest.yaml index aab6efba..0616a3d3 100644 --- a/e2e/flows-partial-rollback/01-update-to-latest.yaml +++ b/e2e/flows-partial-rollback/01-update-to-latest.yaml @@ -2,14 +2,6 @@ appId: ${APP_ID} --- # App should be on binary (after phase 2 rollback) - launchApp -- runFlow: - when: - platform: Android - file: ../flows/shared/android-dismiss-overlays.yaml -- runFlow: - when: - platform: iOS - file: ../flows/shared/ios-dismiss-overlays.yaml - assertVisible: "React Native.*" - assertNotVisible: "UPDATED!" diff --git a/e2e/flows-partial-rollback/02-rollback-to-previous.yaml b/e2e/flows-partial-rollback/02-rollback-to-previous.yaml index 133e6bb4..db910b00 100644 --- a/e2e/flows-partial-rollback/02-rollback-to-previous.yaml +++ b/e2e/flows-partial-rollback/02-rollback-to-previous.yaml @@ -2,14 +2,6 @@ appId: ${APP_ID} --- # App should be on 1.0.2 (from previous update) - launchApp -- runFlow: - when: - platform: Android - file: ../flows/shared/android-dismiss-overlays.yaml -- runFlow: - when: - platform: iOS - file: ../flows/shared/ios-dismiss-overlays.yaml - assertVisible: "UPDATED!" - tapOn: "Get update metadata" - waitForAnimationToEnd: diff --git a/e2e/flows-rollback/01-rollback.yaml b/e2e/flows-rollback/01-rollback.yaml index 49efd491..8f6a56be 100644 --- a/e2e/flows-rollback/01-rollback.yaml +++ b/e2e/flows-rollback/01-rollback.yaml @@ -2,14 +2,6 @@ appId: ${APP_ID} --- # App should have the update installed from phase 1 - launchApp -- runFlow: - when: - platform: Android - file: ../flows/shared/android-dismiss-overlays.yaml -- runFlow: - when: - platform: iOS - file: ../flows/shared/ios-dismiss-overlays.yaml - assertVisible: "UPDATED!" # Check for updates — server has disabled the release, triggers rollback to binary diff --git a/e2e/flows/02-restart-no-crash.yaml b/e2e/flows/02-restart-no-crash.yaml index 2e88a0a1..49b5e349 100644 --- a/e2e/flows/02-restart-no-crash.yaml +++ b/e2e/flows/02-restart-no-crash.yaml @@ -1,14 +1,6 @@ appId: ${APP_ID} --- - launchApp -- runFlow: - when: - platform: Android - file: shared/android-dismiss-overlays.yaml -- runFlow: - when: - platform: iOS - file: shared/ios-dismiss-overlays.yaml - assertVisible: "React Native.*" - tapOn: "Restart app" - waitForAnimationToEnd: diff --git a/e2e/flows/03-update-flow.yaml b/e2e/flows/03-update-flow.yaml index 3b9468ce..c940ec4c 100644 --- a/e2e/flows/03-update-flow.yaml +++ b/e2e/flows/03-update-flow.yaml @@ -1,14 +1,6 @@ appId: ${APP_ID} --- - launchApp -- runFlow: - when: - platform: Android - file: shared/android-dismiss-overlays.yaml -- runFlow: - when: - platform: iOS - file: shared/ios-dismiss-overlays.yaml - assertVisible: "React Native.*" # Ensure clean state — clear any previous updates