From 5bfc0379109e0a71f7d93e1045c43ab9c01e164b Mon Sep 17 00:00:00 2001 From: dianaKhortiuk-frontegg Date: Tue, 30 Jun 2026 16:57:57 +0200 Subject: [PATCH 1/2] fix(e2e): harden Android E2E against launcher ANR and slow cold start --- .github/workflows/react-native-sdk-e2e.yml | 18 +++++++++++- .../demo/e2e/utils/UiTestInstrumentation.kt | 29 +++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/.github/workflows/react-native-sdk-e2e.yml b/.github/workflows/react-native-sdk-e2e.yml index a4cd639..3d65d31 100644 --- a/.github/workflows/react-native-sdk-e2e.yml +++ b/.github/workflows/react-native-sdk-e2e.yml @@ -121,8 +121,24 @@ jobs: adb shell settings put global window_animation_scale 0 adb shell settings put global transition_animation_scale 0 adb shell settings put global animator_duration_scale 0 + adb shell settings put global hide_error_dialogs 1 sleep 5 - ./gradlew :app:connectedDebugAndroidTest --no-daemon -Pandroid.testInstrumentationRunnerArguments.LOGIN_EMAIL="$LOGIN_EMAIL" -Pandroid.testInstrumentationRunnerArguments.LOGIN_PASSWORD="$LOGIN_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.LOGIN_WRONG_PASSWORD="$LOGIN_WRONG_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_1="$TENANT_NAME_1" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_2="$TENANT_NAME_2" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_EMAIL="$GOOGLE_EMAIL" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_PASSWORD="$GOOGLE_PASSWORD" + run_e2e() { + ./gradlew :app:connectedDebugAndroidTest --no-daemon -Pandroid.testInstrumentationRunnerArguments.LOGIN_EMAIL="$LOGIN_EMAIL" -Pandroid.testInstrumentationRunnerArguments.LOGIN_PASSWORD="$LOGIN_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.LOGIN_WRONG_PASSWORD="$LOGIN_WRONG_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_1="$TENANT_NAME_1" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_2="$TENANT_NAME_2" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_EMAIL="$GOOGLE_EMAIL" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_PASSWORD="$GOOGLE_PASSWORD" + } + # The emulator's launcher intermittently ANRs under load, hiding the app and failing + # an otherwise-passing run. Retry the test task (same emulator) so a random hiccup + # doesn't red the gate; pass on the first green attempt. + for attempt in 1 2 3; do + echo "::group::Android E2E attempt $attempt" + if run_e2e; then echo "::endgroup::"; exit 0; fi + echo "::endgroup::" + echo "Android E2E attempt $attempt failed (likely emulator/launcher flake); retrying" + adb shell am force-stop com.frontegg.demo || true + adb shell settings put global hide_error_dialogs 1 || true + sleep 5 + done + exit 1 - name: Collect JUnit reports if: always() diff --git a/example/android/app/src/androidTest/java/com/frontegg/demo/e2e/utils/UiTestInstrumentation.kt b/example/android/app/src/androidTest/java/com/frontegg/demo/e2e/utils/UiTestInstrumentation.kt index 6eaf9a0..50d0673 100644 --- a/example/android/app/src/androidTest/java/com/frontegg/demo/e2e/utils/UiTestInstrumentation.kt +++ b/example/android/app/src/androidTest/java/com/frontegg/demo/e2e/utils/UiTestInstrumentation.kt @@ -34,13 +34,24 @@ class UiTestInstrumentation( activityName: String = "com.frontegg.demo.MainActivity", applicationPackage: String = "com.frontegg.demo", ) { + // CI flake guard: stop the system from popping "… isn't responding" dialogs over the app + // when the emulator is slow under load (the launcher ANRs and hides the app under test). + runCatching { uiDevice.executeShellCommand("settings put global hide_error_dialogs 1") } + val intent = Intent(Intent.ACTION_MAIN).apply { setClassName(targetContext, activityName) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) } - targetContext.startActivity(intent) - uiDevice.wait(Until.hasObject(By.pkg(applicationPackage).depth(0)), 5_000) + // A slow cold start can leave the launcher (not the app) foregrounded on the first try, + // so retry the launch until the app package is actually on top. + repeat(3) { + targetContext.startActivity(intent) + dismissBlockingSystemDialogs() + if (uiDevice.wait(Until.hasObject(By.pkg(applicationPackage).depth(0)), 15_000)) { + return + } + } } fun terminateApp(applicationPackage: String = "com.frontegg.demo") { @@ -55,11 +66,25 @@ class UiTestInstrumentation( val deadline = SystemClock.uptimeMillis() + timeout while (SystemClock.uptimeMillis() < deadline) { uiDevice.findObject(selector)?.let { return it } + dismissBlockingSystemDialogs() delay(250) } return null } + /** + * CI flake guard: under load the emulator's launcher (or the app) can ANR, and the + * system shows an "… isn't responding" dialog on top of the app, hiding the views + * under test. Tap "Wait" (android:id/aerr_wait) to dismiss it and let the process + * recover, so a transient ANR doesn't fail the whole suite. + */ + private fun dismissBlockingSystemDialogs() { + uiDevice.findObject(By.res("android:id/aerr_wait"))?.let { + it.click() + delay(500) + } + } + fun requireView(selector: BySelector, timeout: Long = defaultTimeoutMs): UiObject2 = waitForView(selector, timeout) ?: error("Timed out waiting for $selector after ${timeout}ms. ${dumpVisible()}") From f5fc895bc13a9a26e7bd69c15edb6fc0e0083ee1 Mon Sep 17 00:00:00 2001 From: dianaKhortiuk-frontegg Date: Tue, 30 Jun 2026 18:09:27 +0200 Subject: [PATCH 2/2] =?UTF-8?q?ci(e2e):=20fix=20Android=20E2E=20retry=20?= =?UTF-8?q?=E2=80=94=20the=20emulator-runner=20executes=20the=20script=20l?= =?UTF-8?q?ine-by-line,=20so=20the=20multi-line=20run=5Fe2e()=20function?= =?UTF-8?q?=20broke;=20collapse=20the=20retry=20to=20a=20single=20line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/react-native-sdk-e2e.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/.github/workflows/react-native-sdk-e2e.yml b/.github/workflows/react-native-sdk-e2e.yml index 3d65d31..db55ff1 100644 --- a/.github/workflows/react-native-sdk-e2e.yml +++ b/.github/workflows/react-native-sdk-e2e.yml @@ -123,22 +123,7 @@ jobs: adb shell settings put global animator_duration_scale 0 adb shell settings put global hide_error_dialogs 1 sleep 5 - run_e2e() { - ./gradlew :app:connectedDebugAndroidTest --no-daemon -Pandroid.testInstrumentationRunnerArguments.LOGIN_EMAIL="$LOGIN_EMAIL" -Pandroid.testInstrumentationRunnerArguments.LOGIN_PASSWORD="$LOGIN_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.LOGIN_WRONG_PASSWORD="$LOGIN_WRONG_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_1="$TENANT_NAME_1" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_2="$TENANT_NAME_2" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_EMAIL="$GOOGLE_EMAIL" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_PASSWORD="$GOOGLE_PASSWORD" - } - # The emulator's launcher intermittently ANRs under load, hiding the app and failing - # an otherwise-passing run. Retry the test task (same emulator) so a random hiccup - # doesn't red the gate; pass on the first green attempt. - for attempt in 1 2 3; do - echo "::group::Android E2E attempt $attempt" - if run_e2e; then echo "::endgroup::"; exit 0; fi - echo "::endgroup::" - echo "Android E2E attempt $attempt failed (likely emulator/launcher flake); retrying" - adb shell am force-stop com.frontegg.demo || true - adb shell settings put global hide_error_dialogs 1 || true - sleep 5 - done - exit 1 + for attempt in 1 2 3; do echo "Android E2E attempt $attempt"; if ./gradlew :app:connectedDebugAndroidTest --no-daemon -Pandroid.testInstrumentationRunnerArguments.LOGIN_EMAIL="$LOGIN_EMAIL" -Pandroid.testInstrumentationRunnerArguments.LOGIN_PASSWORD="$LOGIN_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.LOGIN_WRONG_PASSWORD="$LOGIN_WRONG_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_1="$TENANT_NAME_1" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_2="$TENANT_NAME_2" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_EMAIL="$GOOGLE_EMAIL" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_PASSWORD="$GOOGLE_PASSWORD"; then exit 0; fi; echo "Android E2E attempt $attempt failed (likely emulator/launcher flake); retrying"; adb shell am force-stop com.frontegg.demo || true; adb shell settings put global hide_error_dialogs 1 || true; sleep 5; done; exit 1 - name: Collect JUnit reports if: always()