Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/react-native-sdk-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@ 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"
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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand All @@ -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()}")
Expand Down
Loading