I'm trying to test the scenario like below.
Here is my code:
#Test
public void testRenameList() {
addNewList();
// Long click on the list
onView(recyclerViewItemWithText(mNewListName)).perform(longClick());
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
}
The following error occures on the last line:
android.support.test.espresso.AmbiguousViewMatcherException: '((is displayed on the screen to the user and with content description: is "More options") or (is displayed on the screen to the user and with class name: a string ending with "OverflowMenuButton"))' matches multiple views in the hierarchy.
I think, Espresso finds two menu: from ActionMode and from ActionBar.
The question is how to open menu exactly from ActionMode?
I've solved this issue by using UIAutomator.
I've replaced the line
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
by
UiObject btnMenu = mDevice.findObject(new UiSelector().description(mActivity.getString(R.string.menu_button_identifier)));
btnMenu.click();
The value of R.string.menu_button_identifier in English is "More options".
Related
My Android application has a button to download a file and then send it to an application on the device. Android pops up a screen listing the Applications on the device for the user to select which application to use.
I would like to automate this flow but i can not see how I can automate clicking on the Application Picker that Android presents. I presume this is because it is outside of my application.
I tried using Android Studio's "Record Expresso Test", I performed the following test steps
click on the action which sends my image to an app on the device (action1)
saw the Android Application picker appear and chose photos
Clicked back to close photos app and go back to my app
click on a different action in my app (action2)
I see in the recorded test code for steps 1 and 4 above, but nothing for 2 and 3. Therefore it makes me think that Expresso can not be used for this particular test flow.
Does anyone know how I could test this flow using Expresso?
EDIT:
Thank you to "John O'Reilly" for recommending UI Automator. I can see that I can use the UI Automator code within my Expresso test successfully. However I am having trouble writing a precise verification of the Application Selector.
The selector will have a title of "Open With". Using Android Device Monitor I can see the hierarchy of objects as illustrated below.
Some classes and IDs are internal so I can not search on those things. I do not want to code to look for a specific application as when the test is run on another machine it may not have that application. I just need to verify that the application picker has been displayed.
// the app selector has a FrameLayout as one of its parent views, and a child Text View which has the "Open With" title
UiObject labelOnly = new UiObject(new UiSelector()
.className("android.widget.FrameLayout")
.childSelector(new UiSelector()
.className("android.widget.TextView")
.text(openWithLabel)
)
);
boolean labelOnly_exists = labelOnly.exists();
// the app selector has a FrameLayout as one of its parent views, and a child ListView (containing the apps)
UiObject listOnly = new UiObject(new UiSelector()
.className("android.widget.FrameLayout")
.childSelector(new UiSelector()
.className("android.widget.ListView")
)
);
boolean listOnly_exists = listOnly.exists();
// I can use the listView to search for a specific app, but this makes the tests fragile if a different device does not have that app installed
UiObject listAndAppName = new UiObject(new UiSelector()
.className("android.widget.ListView")
.instance(0)
.childSelector(new UiSelector()
.text("Photos")));
boolean listAndAppName_exists = listAndAppName.exists();
How could i write a statement that verifies that what is on the screen is the application picker? I was hoping maybe have a selector that searches for a FrameLayout which has a child textView containing "Open With" and also contains a child ListView. With these 2 checks together it should identify only the application picker.
The credit for the answer to this question should go to John O'Reilly who pointed me to use the UI Automator.
I resolved the issue of checking what Android screen is invoked when my test clicks on an action by just checking there is a TextView on the screen with the title I am expecting. Its not perfect as this will pass if there is any TextView on the screen with the text so does not precisely check its the application picker.
However, for my test this should be enough of a check as my app (which would be behind the app picker) should not have a TextView with the expected title, so if the title is found its pretty much likely to be the application picker.
public static boolean verifyAndroidScreenTitlePresent(String title) {
UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
UiObject titleTextUI = new UiObject(new UiSelector()
.className("android.widget.TextView")
.text(title)
);
boolean titleExists = titleTextUI.exists();
// close the app selector to go back to our app so we can carry on with Expresso
mDevice.pressBack();
return titleExists;
}
I have the below espresso test:
openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext());
// if I Thread.sleep() here, I can see that the MenuItem has been clicked already
onView(withText("Sign in")) //<= click on the MenuItem
.perform(click());
onView(withId(R.id.signupButton)) //<= click the signup button in my UI
.perform(click());
The first line up there opens the overflow menu and clicks the first item at the same time (which happens to be the signin item). So the test fails because it cannot find the MenuItem view. Is there anything I am doing wrong ? I am using an emulator API 22, compiling agains targetSdk 24 and using espresso 2.2.1.
Try that:
public class EspressoMatchers {
public static Matcher<View> withOverflowMenuButton() {
return anyOf(allOf(isDisplayed(), withContentDescription("More options")),
allOf(isDisplayed(), withClassName(endsWith("OverflowMenuButton"))));
}
}
To open overflow menu:
onView(allOf(EspressoMatchers.withOverflowMenuButton(),
isDescendantOfA(withId(R.id.toolbar)))).perform(click());
Then it should work fine. Just use right ID for your Toolbar. I know this is just a copy from Espresso class, but I also ran into this issue and this helped me.
Please remember to always click the menu items "by name", not by their ID, as ID won't work. So your "click item" should be fine:
onView(withText("Sign in")) //<= click on the MenuItem
.perform(click());
I am using the uiautomator tool to write some Automated Tests for my app.
Here is the code that is problematic:
UiScrollable appViews = new UiScrollable(new UiSelector().scrollable(true));
appViews.setAsHorizontalList(); // works on API 17+
UiObject settingsApp = appViews.getChildByText(new UiSelector().className(android.widget.TextView.class.getName()), "Settings");
settingsApp.clickAndWaitForNewWindow();
When I run this on a KitKat phone, phone goes to home screen, then clicks "Apps" then selects "Apps tab" (this part of the code has been omitted from this post for clarity) and then it starts looking for the "Settings" icon - it scrolls horizontally once to the left, the "Downloaded" tab gets selected, then it scrolls back to the right, Settings is not there so the test fails.
I then got the phone in my hands and scrolled once more to the left and there "Settings" was.
My question is why didnt it scroll through all the pages until it found where "Settings" was?
You can set the maximum number of swipes that suits your needs via UiScrollable.setMaxSearchSwipes(), which adjusts the number of scrolls allowed when performing a scroll action in search of a child element.
I looked through the documentation of UIScrollable here:
https://android.googlesource.com/platform/frameworks/testing/+/f612e6a05f47b28ae0f5715545658c08dd759dd7/uiautomator/library/src/com/android/uiautomator/core/UiScrollable.java
And I found this method:
scrollBackward()
So I decided to take care of the problem on my own this way:
boolean settingsFound = false;
while(!settingsFound) {
appViews.scrollBackward();
try {
UiObject settingsApp = appViews.getChildByText(new UiSelector().className(android.widget.TextView.class.getName()), "Settings", true);
settingsApp.clickAndWaitForNewWindow();
settingsFound = true;
} catch (Exception e){
e.printStackTrace();
}
}
This code kept scrolling to the right until "Settings" was visible and then clicked on it.
I have automated go to setting in android phone using UI Automator and i clicked the menu using "getUiDevice().pressMenu();" and its opened menu with 3 sub menu item and i want to click the second menu by using name or index or id , please help how to click the sub menu in Android UIAutomator ?
This overflow menu icon ImageView with the three dots has no id ...
but one can get it by it's description:
UiObject2 menuButton = this.mDevice.findObject(By.desc("More options"));
/* open the menu */
if(menuButton != null && menuButton.isClickable()) {
menuButton.click();
menuButton.recycle();
} else {
Assert.fail();
}
Then one can click menu items by their index:
ArrayList<UiObject2> menu = (ArrayList<UiObject2>) this.mDevice.findObject(By.clazz("android.widget.ListView")).getChildren();
/* click the menu item at index 0 */
UiObject2 menu_action = menu.get(0);
if (menu_action != null && menu_action.isClickable()) {
menu_action.click();
menu_action.recycle();
} else {
Assert.fail();
}
Use clazz method is working
UiObject2 item = mDevice.findObject(By.clazz("android.widget.ListView"));
You can use 'uiautomatorviewer' tool and view the text/description/resource id/index of your menu items displayed there.
To use uiautomatorviewer tool :
[1] Connect your device.
[2] Open command prompt and type 'uiautomatorviewer'.
[3] Open the particular screen of whose elements you want to view (in this case - your menu items).
[4] Press the green 'Device Screenshot' button next to file open in top left.
Hover on the screenshot to view the text/description/resource id/index of the elements of the screen and you can use whatever is being displayed there.
I'm compiling ADW source code.
(https://github.com/AnderWeb/android_packages_apps_Launcher/archives/b28e9e417c81857cfd2e0265c3c6d409d6c8d0f1)
I wanna add widget more quickly,
so I add a menu item:
menu.add(MENU_GROUP_NORMAL, MENU_ADD_WIDGET, 0, R.string.menu_widget)
.setIcon(android.R.drawable.ic_menu_compass);
and the relevant action when clicked:
case MENU_ADD_WIDGET:
showAppWidgetList();
return true;
the showAppWidgetList() does the same thing as when you click Menu->Add->Widget .
Then I click "my widget item" and the PickWidget dialog shows up,
after clicking a widget,
nothing happens!!!
No widget config or position setting dialog appears.
On the other hand,
if I click Menu->Add->Widget and add a widget,
then Menu->"my widget item" and pick a widget,
then everything works just fine.
How strange!!!
Anybody know what's wrong with it??
It's because Menu->Add->Widget does something before your showAppWidgetList(), you need to do the same thing before showAppWidgetList().
Analyze the source code, you can find out that the Launcher does the following:
mAddItemCellInfo = mMenuAddInfo;
mWaitingForResult = true;
showAppWidgetList();
// your method