Android testing: Waited for the root of the view hierarchy to have window focus

14,940

Solution 1

This error can happen when a system dialog is displayed — like "Power Off" or "Unfortunately, Launcher has stopped" (a background app crashed) — and you try to run an Espresso unit test whilst that dialog is visible.

Android system dialog: Unfortunately, Launcher has stopped.

Image credit: Android 4.0 emulator always has a crashing Launcher?

You can workaround it in code by dismissing the system dialog before running the test:

// Use the 'testing' Context
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));

// Alternative: Use the Activity for the Context
MyActivity activityUnderTest = activityTestRule.getActivity();
activityUnderTest.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));

Or send the broadcast on the command line using adb:

adb shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS

Another cause of the error is when a background app is frozen (ANR) or is running slowly, and a system dialog appears saying "Launcher isn't responding. Do you want to close it? [Wait] [OK]":

Android system dialog: Launcher isn't Responding. Do you want to close it? Wait, OK

Image credit: https://engineering.linkedin.com/blog/2016/08/introducing-and-open-sourcing-test-butler--reliable-android-test

If you try to run an Espresso test while this dialog is visible, the tests will all fail and show the "Waited for the root..." error. There's no easy way to close this dialog programmatically. Espresso cannot click these buttons, for the reasons described here: Dismiss Alert Dialog in Android Espresso Test . However, one way is to use UI Automator to press the "Wait" button in the dialog, just before a test starts:

app/build.gradle

dependencies {
    ...
    androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0"
}

ActivityUiTest.kt

companion object {
    @JvmStatic
    @BeforeClass
    fun dismissANRSystemDialog() {
        val device = UiDevice.getInstance(getInstrumentation())
        // If running the device in English Locale
        var waitButton = device.findObject(UiSelector().textContains("wait"))
        if (waitButton.exists()) {
            waitButton.click()
        }
        // If running the device in Japanese Locale
        waitButton = device.findObject(UiSelector().textContains("待機"))
        if (waitButton.exists()) {
            waitButton.click()
        }
    }
}

ActivityUiTest.java

@BeforeClass
public static void dismissANRSystemDialog() throws UiObjectNotFoundException {
    UiDevice device = UiDevice.getInstance(getInstrumentation());
    // If the device is running in English Locale
    UiObject waitButton = device.findObject(new UiSelector().textContains("wait"));
    if (waitButton.exists()) {
        waitButton.click();
    }
    // If the device is running in Japanese Locale
    waitButton = device.findObject(new UiSelector().textContains("待機"));
    if (waitButton.exists()) {
        waitButton.click();
    }
}

Alternatively, you could use adb on the command line to send screen taps or key strokes to dismiss it. For example:

# On a 320x480 screen, click at screen location [x=233,y=293] to tap an "OK" dialog button.
# Just in case there is a "Launcher isn't responding" system dialog.
adb shell input tap 233 293

or

# Send keystroke Arrow Right
sleep 3; adb shell input keyevent 22
# Send keystroke Arrow Right again
sleep 3; adb shell input keyevent 22
# Send keystroke Enter to press a button on the dialog
sleep 3; adb shell input keyevent 66

More info:

Solution 2

I had the same error, when I used a Spinner inside a DialogFragment. This is the only code that was working for me:

onView(withText(containsString("A4"))).inRoot(isPlatformPopup()).check(matches(isDisplayed()));

Solution 3

I had similar issue when a dialog popup contains spinner items (dropdown list), my click couldn't perform on any of the spinner items and got the same error. I found a solution by using onData() method with RootMatchers:

onData(anything()).inRoot(RootMatchers.isPlatformPopup()).atPosition(1).perform(click());

Note that, the index value in atPosition() is the item's index value from the spinner list.

Solution 4

Just in case if it happens to anyone's Travis build (with exact the same log). Please check this.

Had exact the same problem, and solved by creating avd with lower target version (19).

What I've tried and didn't work:

  • Adding a unlockScreen() @Before method to UI test.

  • Adding / Removing adb shell input keyevent 82 &.

  • Removing different emulator command options -no-skin or -no-audio or -no-window. Now I have -no-window there which is fine.

Finally, changing from

echo no | android create avd --force -n test -t android-24 --abi armeabi-v7a

to

echo no | android create avd --force -n test -t android-19 --abi armeabi-v7a

perfectly solve the issue.

Share:
14,940
Kimmy
Author by

Kimmy

Updated on July 06, 2022

Comments

  • Kimmy
    Kimmy almost 2 years

    In Android Ui testing, I want to click on a spinner item in a dialog, but it pop up with this error:

    va.lang.RuntimeException: Waited for the root of the view hierarchy to have window focus and not be requesting layout for over 10 seconds. If you specified a non default root matcher, it may be picking a root that never takes focus. Otherwise, something is seriously wrong. Selected Root:
    Root{application-window-token=android.view.ViewRootImpl$W@2dac97c7, window-token=android.view.ViewRootImpl$W@2dac97c7, has-window-focus=false, layout-params-type=1, layout-params-string=WM.LayoutParams{(0,0)(fillxfill) sim=#10 ty=1 fl=#81810100 pfl=0x8 wanim=0x1030461 surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x0}, decor-view-string=MultiPhoneDecorView{id=-1, visibility=VISIBLE, width=1600, height=2560, has-focus=true, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
    . All Roots:
    Root{application-window-token=android.view.ViewRootImpl$W@3c913e1, window-token=android.view.ViewRootImpl$W@21b23506, has-window-focus=true, layout-params-type=1002, layout-params-string=WM.LayoutParams{(310,600)(722x480) gr=#10000033 sim=#1 ty=1002 fl=#1860200 fmt=-3 wanim=0x10302db surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x0}, decor-view-string=PopupViewContainer{id=-1, visibility=VISIBLE, width=722, height=480, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
    Root{application-window-token=android.view.ViewRootImpl$W@3c913e1, window-token=android.view.ViewRootImpl$W@3c913e1, has-window-focus=false, layout-params-type=2, layout-params-string=WM.LayoutParams{(0,0)(wrapxwrap) gr=#11 sim=#20 ty=2 fl=#1800002 pfl=0x8 fmt=-3 wanim=0x1030462 surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x10}, decor-view-string=DecorView{id=-1, visibility=VISIBLE, width=1136, height=1058, has-focus=true, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
    Root{application-window-token=android.view.ViewRootImpl$W@2dac97c7, window-token=android.view.ViewRootImpl$W@2dac97c7, has-window-focus=false, layout-params-type=1, layout-params-string=WM.LayoutParams{(0,0)(fillxfill) sim=#10 ty=1 fl=#81810100 pfl=0x8 wanim=0x1030461 surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x0}, decor-view-string=MultiPhoneDecorView{id=-1, visibility=VISIBLE, width=1600, height=2560, has-focus=true, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
    at android.support.test.espresso.base.RootViewPicker.get(RootViewPicker.java:99)
    at android.support.test.espresso.ViewInteractionModule.provideRootView(ViewInteractionModule.java:69)
    at android.support.test.espresso.ViewInteractionModule_ProvideRootViewFactory.get(ViewInteractionModule_ProvideRootViewFactory.java:23)
    at android.support.test.espresso.ViewInteractionModule_ProvideRootViewFactory.get(ViewInteractionModule_ProvideRootViewFactory.java:9)
    at android.support.test.espresso.base.ViewFinderImpl.getView(ViewFinderImpl.java:68)
    at android.support.test.espresso.ViewInteraction$1.run(ViewInteraction.java:120)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:145)
    at android.app.ActivityThread.main(ActivityThread.java:6117)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
    

    I have tried

    onData(allOf(is(instanceOf(String.class)),containsString("A4"))).inRoot(isPlatformPopup()).perform(click());
    

    and

    onView(withText(containsString("A4"))).inRoot(isFocusable()).check(matches(isDisplayed()));
    

    and

    onView(withText(containsString("A4"))).inRoot(withDecorView(not(getActivity().getWindow().getDecorView()))).check(matches(isDisplayed()));
    

    but none of them works... Can anyone tell me how to get the ralavant root please?