AppPasswordSetupScreenTest.imapDisabledFailure_showsThePrompt_andHelpLinkOpensTheProviderPage (and its sibling URL-open tests) flake on the CI E2E matrix with:
androidx.test.espresso.base.RootViewPicker$RootViewWithoutFocusException:
Waited for the root of the view hierarchy to have window focus ... for 10 seconds
... Root{... has-window-focus=false ...}
A single failure fails the whole ~9-min E2E leg and forces a retry.
Root cause (confirmed from CI run 28980028281, job "E2E (35)")
Intents.intended(...) internally runs onView(isRoot()).check(...), and Espresso's RootViewPicker waits up to 10s for a window-focused root:
at androidx.test.espresso.ViewInteraction.check(ViewInteraction.java:366)
at androidx.test.espresso.intent.Intents.intended(Intents.java:186)
at org.libremail.ui.accountsetup.AppPasswordSetupScreenTest...(AppPasswordSetupScreenTest.kt:110)
On the CI emulator the app window intermittently reports has-window-focus=false, so intended() times out. This is not caused by a missing intent stub — the intending(ACTION_VIEW).respondWith(...) stubs were already present and already on main; no external activity launches. The focus loss is environmental: the same run failed 8 unrelated tests at once, all sharing the Espresso RootViewPicker dependency (Intents.intended, Espresso.pressBack, a focus-dependent clipboard read), while 280+ pure-Compose tests passed in that same run. CI already disables animations and sends input keyevent 82 at boot, and this still recurs post-#454.
Fix
For the tests whose links open a URL through Compose's LocalUriHandler (the ACTION_VIEW / "browser-open" shape), inject a recording UriHandler and assert the exact URL the screen opens — instead of Espresso-Intents. This keeps the entire test on Compose interactions, which do not depend on window focus, so it is deterministic. The assertion is not weakened (it still asserts the provider page URL / host).
OutlookImapNoticeScreenTest.tappingImapHelpLink_opensTheMicrosoftArticle -> asserts host support.microsoft.com (sweep of the same pattern)
Not in scope (same root cause, no UriHandler seam)
These fire real system intents / use other RootViewPicker APIs, so they can't use this pattern and still rely on Espresso — they remain exposed to the same emulator window-focus flake and will need a separate fix (likely CI-level window focus, or an injectable launcher seam):
Local fast gate (JDK 21): assembleDebug testDebugUnitTest jacocoTestCoverageVerification compileDebugAndroidTestKotlin lintDebug ktlintCheck detekt — all green. Pure test change, so no app logging added. Local emulator E2E skipped (flaky on the Windows dev box); the CI matrix is the authoritative validation for the flake.
## Problem
`AppPasswordSetupScreenTest.imapDisabledFailure_showsThePrompt_andHelpLinkOpensTheProviderPage` (and its sibling URL-open tests) flake on the CI E2E matrix with:
```
androidx.test.espresso.base.RootViewPicker$RootViewWithoutFocusException:
Waited for the root of the view hierarchy to have window focus ... for 10 seconds
... Root{... has-window-focus=false ...}
```
A single failure fails the whole ~9-min E2E leg and forces a retry.
### Root cause (confirmed from CI run 28980028281, job "E2E (35)")
`Intents.intended(...)` internally runs `onView(isRoot()).check(...)`, and Espresso's `RootViewPicker` waits up to 10s for a **window-focused** root:
```
at androidx.test.espresso.ViewInteraction.check(ViewInteraction.java:366)
at androidx.test.espresso.intent.Intents.intended(Intents.java:186)
at org.libremail.ui.accountsetup.AppPasswordSetupScreenTest...(AppPasswordSetupScreenTest.kt:110)
```
On the CI emulator the app window intermittently reports `has-window-focus=false`, so `intended()` times out. **This is not caused by a missing intent stub** — the `intending(ACTION_VIEW).respondWith(...)` stubs were already present and already on `main`; no external activity launches. The focus loss is environmental: the same run failed **8 unrelated tests at once**, all sharing the Espresso `RootViewPicker` dependency (`Intents.intended`, `Espresso.pressBack`, a focus-dependent clipboard read), while 280+ pure-Compose tests passed in that same run. CI already disables animations and sends `input keyevent 82` at boot, and this still recurs post-#454.
## Fix
For the tests whose links open a URL through Compose's `LocalUriHandler` (the `ACTION_VIEW` / "browser-open" shape), inject a recording `UriHandler` and assert the exact URL the screen opens — instead of Espresso-Intents. This keeps the entire test on Compose interactions, which do **not** depend on window focus, so it is deterministic. The assertion is not weakened (it still asserts the provider page URL / host).
Converted:
- `AppPasswordSetupScreenTest.tappingCreateAppPasswordPage_launchesBrowserIntentToHelpUrl` -> asserts `provider.appPasswordHelpUrl`
- `AppPasswordSetupScreenTest.imapDisabledFailure_showsThePrompt_andHelpLinkOpensTheProviderPage` -> asserts host `support.google.com`
- `OutlookImapNoticeScreenTest.tappingImapHelpLink_opensTheMicrosoftArticle` -> asserts host `support.microsoft.com` (sweep of the same pattern)
## Not in scope (same root cause, no `UriHandler` seam)
These fire real system intents / use other `RootViewPicker` APIs, so they can't use this pattern and still rely on Espresso — they remain exposed to the same emulator window-focus flake and will need a separate fix (likely CI-level window focus, or an injectable launcher seam):
- `AccountPickerScreenTest.tappingOutlook_launchesTheAppAuthBrowserIntent` (AppAuth `hasComponent`)
- `OutlookImapNoticeScreenTest.tappingSignIn_launchesTheAppAuthBrowserIntent` (AppAuth `hasComponent`)
- `BatteryOptimizationStepTest.batteryStep_takeMeThere_opensThisAppsSystemSettings` (Settings intent)
- `LicenseScreenTest.systemBack_invokesOnDeclineJustLikeTheButton` (`Espresso.pressBack`)
- `ReportReviewScreenTest.tappingCopy_putsThePayloadOnTheSystemClipboard_andShowsAConfirmation` (focus-dependent clipboard read)
## Validation
Local fast gate (JDK 21): `assembleDebug testDebugUnitTest jacocoTestCoverageVerification compileDebugAndroidTestKotlin lintDebug ktlintCheck detekt` — all green. Pure test change, so no app logging added. Local emulator E2E skipped (flaky on the Windows dev box); the CI matrix is the authoritative validation for the flake.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
AppPasswordSetupScreenTest.imapDisabledFailure_showsThePrompt_andHelpLinkOpensTheProviderPage(and its sibling URL-open tests) flake on the CI E2E matrix with:A single failure fails the whole ~9-min E2E leg and forces a retry.
Root cause (confirmed from CI run 28980028281, job "E2E (35)")
Intents.intended(...)internally runsonView(isRoot()).check(...), and Espresso'sRootViewPickerwaits up to 10s for a window-focused root:On the CI emulator the app window intermittently reports
has-window-focus=false, sointended()times out. This is not caused by a missing intent stub — theintending(ACTION_VIEW).respondWith(...)stubs were already present and already onmain; no external activity launches. The focus loss is environmental: the same run failed 8 unrelated tests at once, all sharing the EspressoRootViewPickerdependency (Intents.intended,Espresso.pressBack, a focus-dependent clipboard read), while 280+ pure-Compose tests passed in that same run. CI already disables animations and sendsinput keyevent 82at boot, and this still recurs post-#454.Fix
For the tests whose links open a URL through Compose's
LocalUriHandler(theACTION_VIEW/ "browser-open" shape), inject a recordingUriHandlerand assert the exact URL the screen opens — instead of Espresso-Intents. This keeps the entire test on Compose interactions, which do not depend on window focus, so it is deterministic. The assertion is not weakened (it still asserts the provider page URL / host).Converted:
AppPasswordSetupScreenTest.tappingCreateAppPasswordPage_launchesBrowserIntentToHelpUrl-> assertsprovider.appPasswordHelpUrlAppPasswordSetupScreenTest.imapDisabledFailure_showsThePrompt_andHelpLinkOpensTheProviderPage-> asserts hostsupport.google.comOutlookImapNoticeScreenTest.tappingImapHelpLink_opensTheMicrosoftArticle-> asserts hostsupport.microsoft.com(sweep of the same pattern)Not in scope (same root cause, no
UriHandlerseam)These fire real system intents / use other
RootViewPickerAPIs, so they can't use this pattern and still rely on Espresso — they remain exposed to the same emulator window-focus flake and will need a separate fix (likely CI-level window focus, or an injectable launcher seam):AccountPickerScreenTest.tappingOutlook_launchesTheAppAuthBrowserIntent(AppAuthhasComponent)OutlookImapNoticeScreenTest.tappingSignIn_launchesTheAppAuthBrowserIntent(AppAuthhasComponent)BatteryOptimizationStepTest.batteryStep_takeMeThere_opensThisAppsSystemSettings(Settings intent)LicenseScreenTest.systemBack_invokesOnDeclineJustLikeTheButton(Espresso.pressBack)ReportReviewScreenTest.tappingCopy_putsThePayloadOnTheSystemClipboard_andShowsAConfirmation(focus-dependent clipboard read)Validation
Local fast gate (JDK 21):
assembleDebug testDebugUnitTest jacocoTestCoverageVerification compileDebugAndroidTestKotlin lintDebug ktlintCheck detekt— all green. Pure test change, so no app logging added. Local emulator E2E skipped (flaky on the Windows dev box); the CI matrix is the authoritative validation for the flake.Merge Queue Status
2026-07-09 00:42 UTC· Rule:default· triggered by merge protections2026-07-09 01:15 UTC· at65eae0f6c76e5e56ff33f6e3c6b32aa60a3f6810· mergeThis pull request spent 32 minutes 25 seconds in the queue, including 26 minutes 46 seconds running CI.
Required conditions to merge
-conflict-draftbase = maincheck-success = CI passedgithub-review-approved[🛡 GitHub repository ruleset rulemain]label != brokencheck-success = Debug buildcheck-neutral = Debug buildcheck-skipped = Debug buildcheck-success = Unit testscheck-neutral = Unit testscheck-skipped = Unit testscheck-success = CI passedcheck-neutral = CI passedcheck-skipped = CI passedmain]:check-success = @github-actions/CI passedcheck-neutral = @github-actions/CI passedcheck-skipped = @github-actions/CI passed@Mergifyio refresh
✅ Pull request refreshed