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.
Related
Goal: From a website having a lot of links, I will usually middle click the ones I want. Firefox will open them in background new tabs on the side. Now I want to do the same from my Android tablet where middle click is not available.
1) Best: Is there an easy way to middle click on a tablet ?
2) Otherwise something better than a long press to simulate right click then open in new tab ?
3) I'm currently trying a TamperMonkey solution based on other questions found here:
link.addEventListener('click', function(event){event.preventDefault();return newBackgroundTab(this);}, false);
function newBackgroundTab(node) {
var newWindow = window.open("about:blank", "_blank");
newWindow.blur();
window.focus();
newWindow.location.href = node.href;
return false;
}
It also need to set the dom.disable_window_flip option to false in Firefox.
This works on desktop, but not on Android, focus stays on the new tab even though the disable option was already on false by default.
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'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".
I am using robotium to test my app. Issue is with solo.searchText function. In my app I am using expandale listview to display category values. While testing using robotium I am cross checking whether all the categories are present. I am using the below code for that.
boolean ifCategoryLoadingFailed = false;
for(String cat: UnitTestHelperSuite.getInstance().categories){
if(solo.searchText(cat,1,true)){
//LogAdapter.verbose(TAG, "***********Found Category::"+ cat);
UnitTestingFramework.expdata.exportResult("****","Found Category::"+cat,"Success");
continue;
}
else{
ifCategoryLoadingFailed = true;
//LogAdapter.verbose(TAG, "***********Failed to Found Category::"+ cat);
UnitTestingFramework.expdata.exportResult("****","Found Category::"+cat,"Failed");
break;
}
}
It was working fine before. But now the list is not scrolling. So it is identifying only visible categories. But it is not entering the else condition.Testing is stopping here. How can I make it scrollable? please help me. I am stuck with this.
In my experience solo always scrolls down when searching for text. It does not scroll back up though. If your problem is caused by scrolling to the bottom while searching for one value, then the solution is to always call solo.scrollListToTop(listIndex) right before you call searchText() .
Below is an image of the action bar on a Samsung Tab2 action bar running 4.0.3 android ICS
from left to right we have the "back, home, recent apps, screenshot, mini-app launcher, and system menu" buttons. Im certain i am not using the correct names for all these which is why i listed them from left to right along with the above image.
I know i can override the the functionality of "back" using:
#Override
public void onBackPressed() {
// Do something custom here
}
i also know that i can NOT override or remove the home button but i was wondering if i could remove or override the "recent apps", "take screen shot", and "mini app launcher"
would be nice if i could remove the back as well..
We just rolled out almost 100 GTab2s and have another 1,000 on order. This is something we've looked into extensively, especially in regards to screenshot and the minitab bloat. You can not remove the screenshot button, nor minitab, unless you root the device and modify the system image. (in short: you can't).
You could "remove" the home button by implementing your own launcher and getting rid of touchwiz.
FWIW, Samsung's B2B folks have been very helpful in supporting our efforts, even at our relatively small quantities. If you were building, say, a kiosk app around the GTab2, you might be able to get them to supply you with a less bloated image.
static public final String[] pkgs_GT_P3113_LH2 = new String[] { "com.kobobooks.samsung.android",
"com.netflix.mediaclient",
"com.nim.discovery",
"com.osp.app.signin",
"com.peel.app",
"com.samsung.mediahub",
"com.samsung.music",
"com.sec.android.app.gamehub",
"com.sec.android.app.minimode.res",
"com.sec.android.app.music",
"com.sec.android.app.readershub",
"com.sec.android.app.samsungapps",
"com.sec.android.app.sns3",
"com.sec.android.daemonapp.ap.yahoonews",
"com.sec.android.daemonapp.ap.yahoostock.stockclock",
"com.sec.android.widgetapp.ap.yahoonews",
"com.sec.android.widgetapp.at.yahoostock.stockclock",
"com.sec.android.widgetapp.minipaper",
"com.sec.chaton",
"com.sec.minimode.music",
"com.sec.pcw",
"com.tgrape.android.radar",
"com.zinio.samsung.android" };