How to detect change in view in appium(java) :android - android

I am automating test cases for android app using Appium (java).
I am stuck on an interesting scenario:
My test case is to change the view of products by clicking on an icon.
That's done.
Now I need to verify that view has been changed or not, here is where the actual problem comes.
In both the cases the parent class is android.widget.GridView and subclass is android.widget.RelativeLayout - only the the way they appear on screen is changed (First they appeared in list type and later in grid type).
I am using UIAutomator for UI elements.How to detect this change?
Any help/ suggestion /work around would be greatful
UPDATE I am attaching the screenshots of UI Automator.

You know the result view what you going to get. Just verify if that element is visible or not.
List<WebElement> view = driver.findElements(By.className("android.view.View"));
System.out.println(view);
if (view.size() > 0 || view != null) {
viewFound = true;
}

Related

Native Android UI Component not updating

I'm having issues with a Native UI Component that is used in React-Native. The situation is that I have an existing, functioning, completed layout in Android that I need to reuse in a react-native view.
The existing layout (in Android):
has a loading-layout
has a RecyclerView
does api-calls while showing the loading-layout
once done it updates the RecyclerView and hides the loading-layout
works perfectly in a native environment.
To expose this existing layout and use it in React-Native I did as told
by the docs. Exposing properties, callbacks or events was not required as the existing layout is closed and works as is.
The React-Native view:
is a simple view
that imports the existing layout as described by the docs
actually shows the initial loading-layout of the existing layout
The problem occurs when the data is loaded. With debugging you can see that the code is run to update the layout, but that is visually not shown. The loading-layout keeps showing, until you perform a scroll-gesture which redraws the screen and shows the RecyclerView with the loaded data. As if the RecyclerView was already there but just not showing.
Would anybody know what could be the problem?
Thanks is advance!
try adding this function, and change base_svg to the iew that you disered :
private final Runnable measureAndLayout = new Runnable() {
#Override
public void run() {
base_svg.measure(
View.MeasureSpec.makeMeasureSpec(base_svg.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(base_svg.getHeight(), View.MeasureSpec.EXACTLY));
base_svg.layout(base_svg.getLeft(), base_svg.getTop(), base_svg.getRight(), base_svg.getBottom());
}
};
After you add the new information to the screen call that function this way:
base_svg.post(measureAndLayout);
Check if it renders the new views that you add or remove.
To be honest I don't know the reason why. but this work for me. I think react is forcing you to just to render when react wants.
Check this issue for more understanding.

Block Url in Crome Custom Tabs android

I am able to detect which URL is being loaded in Chrome Custom Tab with help of accessibility service, and now i want to find id of back button so that i can close the tab if the url is in the block list, following is the code for click action:
List<AccessibilityNodeInfo> list = nodeInfo.
findAccessibilityNodeInfosByViewId("com.android.chrome:"id for back button"");
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: back_button " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
Everything about your code is reasonably accurate. The problem with the findAccessibilityNodeInfosByViewId function is not all views have a viewId. You're going to have to do your own crawl through the view hierarchy in order to accomplish this, and look for properties other than view ID.
If you want to see what the viewID is, you should use the Android Device Monitor Hierarchy Dump to get access to this, and also help understand other properties you might look at to see what else you can find that might be more consistent and not rely on an ID.
Here we see a typical screen dump from Android Device Monitor. As you can see the view ID for the highlighted view shows up in the information below IF it has one. Note also, that multiple views can share this ID! It is the ViewIdResourceName, NOT a unique ID.

Testing ViewPager with multiple fragments using android espresso

I am trying to test my app which uses ViewPager. Each page contains fragments but these fragments are not always visible. I want to check visibility of a fragment in the currently visible page.
onView(withId(R.id.container_weather))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
But the problem is that espresso looks are all the pages not just the current page and I get the following error:
android.support.test.espresso.AmbiguousViewMatcherException: 'with id: eu.airpatrol.android:id/container_weather' matches multiple views in the hierarchy...
I had the same problem, however using the condition isCompletelyDisplayed() solved this problem as it only takes into account the on-screen views.
So, something like this should work:
onView(allOf(withId(R.id.container_weather), isCompletelyDisplayed()))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
Note: isDisplayed() works too in some cases but it also takes views off-screen into account and won't work if the ViewPager has any other page pr fragment loaded with the same view id.
Your tests are failing because of multiple elements with the same id. You can combine conditions using allOf(...). Then use isDisplayed() to check that matched view is visible on the screen. Below example can work:
onView(allOf(
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE),
withId(R.id.container_weather)))
.check(matches(isDisplayed()));
Ran into this exact same problem. I was fortunate because the view hierarchies in my ViewPager can be easily identified by their siblings, so I was able to solve this using the hasSibling matcher, like so:
onView(
allOf(
hasSibling(withId(R.id.some_sibling)),
withId(R.id.field_to_test)
)
).perform(replaceText("123"));
Not a perfect solution as it can be slightly brittle, but in my case I think it was an acceptable compromise.
I had similar problem, where I was reusing the button layout and it was giving me a matches multiple views in the hierarchy exception.
So the easy work around I did was to create 2 different screens and have 2 different methods with different text.
Withdraw Screen:
public WithdrawScreen clickWithdraw() {
onView(allOf(withId(R.id.save_button), withText("Withdraw")))
.perform(click());
return this;
}
Deposit Screen:
public DepositScreen clickDeposit() {
onView(allOf(withId(R.id.save_button), withText("Deposit")))
.perform(click());
return this;
}
and in my tests, I create a new instance of both screens and call the above methods based on screen reference which is a bit easy to test for.
WithdrawScreen withdrawInstance = new WithdrawScreen();
withdrawInstance.clickWithdraw();
DepositScreen depositInstance = new DepositScreen();
depositInstance.clickDeposit();
The point was they were using same id - R.id.save_button for button and I was replacing text of button based on visibility of the fragment we are on.
Hope it helps.

Should I use several activities for an app with several screens?

I'm new to Android and I'm building a simple application to start with. It consists of a client with three screens. In the first screen the user is prompted for an Ip to connect to a server (I use an EditText and a button). If the connection is successfully established, some data will be retrieved from the server and the client will show the data on a blank screen (I use a TextView). This would be the second screen. Then, the user could ask the server for detailed information about any data that has been retrieved from the server, which would be the third screen (I use a TextView again).
The problem is that I don't know what's the best way to go about it. I have currently one activity and one XML file containing all the components of the view (EditText, button, TextView). Until now, I've been using setVisibility(View.GONE);to hide certain components depending on the screen the user is in. (For example in the first screen I have to hide both TextViews).
One of the problems I'm facing is that when I put the phone in a horizontal position the components I had hidden show up again. I don't know if hiding views is the ideal thing to do for my purpose.
I've thought that maybe I should use more than one activity, shouldn't I?
I really appreciate any help you can give me to structure my first app.
I would definitely recommend splitting up your App into multiple Activities/Fragments. Depending on how big the logic for each screen gets you will be glad you did it later on because each Activity only has one responsibility.
Look at your mail app for example. You got your List Activity showing you all your mails and then when you select one it starts the Detail Activity showing you the content of your mail. Each Activity is only responsible for one thing which make each one easier to write and maintain.
It also simplifies your layout definitions because each one only contains the relevant parts.
Seems like this is coming up a lot. Android destroys and recreates and Activity when the configuration changes. Screen rotation is part of the orientation. In order to avoid that, the Activity is responsible for retaining state. The mechanisms given for that are the onCreate and onSaveInstanceState. In your example, you could do something like the following:
int uiPhase = 1;
#Override
void onCreate( Bundle data ) {
uiPhase = data.getInt( "uiPhase", 1 );
// inflate layout
setPhase( uiPhase );
}
// invoke the following each time your screen changes
void setPhase( int newPhase ) {
uiPhase = newPhase;
switch( uiPhase ) {
case 1: // show UI elements for first screen, hide others
break;
case 2: // show UI elements for second screen, hide others
break;
case 3: // show UI elements for third screen, hide others
break;
}
}
#Override
void onSaveInstanceState( Bundle data ) {
data.put( "uiPhase", uiPhase );
}
I didn't want to complicate the pattern above too much, but a good method for setting visibility is as follows:
phase1view.setVisibility( uiPhase == 1 ? View.VISIBLE : View.GONE );
phase2view.setVisibility( uiPhase == 2 ? View.VISIBLE : View.GONE );
phase3view.setVisibility( uiPhase == 3 ? View.VISIBLE : View.GONE );
That pulls the repetition in the setPhase method quite a bit together.
Set button visibility to GONE (button will be completely "removed" -- the buttons space will be available for another widgets) or INVISIBLE (button will became "transparent" -- its space will not be available for another widgets):
use in place of
setVisibility(View.GONE)
change to
setVisibility(View.INVISIBLE) and try

"Resetting" Robotium searchText

I have a unique situation here. I am testing an app using Robotium and I am doing this under "black box" conditions. In my test, I have a tab titled 'all' and it is at the top of the screen, and I want to test that when it is clicked, all the available items are listed. What happens though, is instead of clicking the 'all' tab, the app titled "Advanced Call Manager" is being clicked. I assume that this is because 'all' is part of 'call' and due to the the way Robotium works, it clicks on 'all' even though it is part of 'call'. After you look at my code, you might understand what my issue is.
So my question is:
Is there a way to "reset" Robotium so that when it does its search for text, it starts at the top of the page? Here is my code:
solo.waitForText("all");
bw.write("Verify presence of 'all' filter on " + BUSINESS + "-COMMUNICATION page\",\"");
if(solo.searchText("all")==false){
bw.write("FAILED \",\" \'all\' filter not found \"\n\"");
}else if(solo.searchText("all")==true){
bw.write("PASSED \",\" \'all\' filter present\"\n\"");
bw.write("Verify functionality of 'all' filter\",\"");
solo.clickOnText("all");
solo.sleep(5000);
solo.takeScreenshot();
bw.write("Screenshot taken\",\"" +"See photo: "+ photoFormat.format(new Date()).toString()+"\"\n\"");
}
Any help would be appreciated!
The string passed to solo.clickOnText() is actually a RegEx. So I think you can solve your problem by passing a RegEx that matches 'all' but not 'call'.
ViewGroup class will be helpful in this case as you are using tabs in your code.Now find the view using getChildAt() method.
ViewGroup tabs = (ViewGroup) solo.getView(android.R.id.tabs);
View viewYouWantToDoStuffWith = tabs.getChildAt(x); //change x to the index you want.
Now you can perform the click event on tab like below.
solo.clickOnView(viewYouWantToDoStuffWith);

Categories

Resources