I am new to appium.
I am learning appium with automation of shopping app (Flipkart).
I am trying to select element 5th element from "Recommended discount for You" list.
In order to do that I have to make horizontal scroll to reach out to that element. But I am having problem get that container element in which elements of "Recommended discount for You" is present so I can fetch the 5th element.
I guess the problem is UI of flipkart always getting change.
driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
Thread.sleep(3000);
driver.scrollToExact("Recommended Offers for You");
Thread.sleep(10000);
// Getting error on below line
AndroidElement ele = (AndroidElement) driver.findElement(By.xpath("//android.support.v7.widget.RecyclerView[#index='1']"));
for(int i=0;i<4;i++)
{
Thread.sleep(2000);
if (ele.findElement(By.xpath("android.widget.RelativeLayout[#index='2']")).isDisplayed())
{
ele.findElement(By.xpath("android.widget.RelativeLayout[#index='2']")).click();
break;
}
else
{
horizontalScroll(ele);
}
}
public void horizontalScroll(AndroidElement ele)
{
Dimension size=driver.manage().window().getSize();
int x_start=(int)(size.width*0.60);
int x_end=(int)(size.width*0.30);
int y=130;
ele.swipe(SwipeElementDirection.RIGHT,x_end,y,4000);
}
I am using uiautomator to find elements. Here is the screenshot of UI.
I have following questions:
What is best way to find elements if UI of app is as displayed.
Which app should I automate in order to learn appium.
How to automate app if UI of app is always getting change
Please Help! Thanks in advance.
Best(or rather the fastest) way to locate an element in the app is using the resource-id. If the resource-id is unavailable/same you have to locate it using xpath
If you want to Learn Appium you can start by following these tutorials by Mukesh Otwani here is the link
If the UI keeps updating the only way tackle this is keep updating your script with the changes.
Hope it helps!
In here, I see the element has a resource-id. Why not use that ?
What is best way to find elements if UI of app is as displayed.
Always, finding element by resource id is a preferred method. Because, it finds the element faster than xpath. Also, if there is an element added in between, the xpath varies, because the element in the hierarchy changes.
Which app should I automate in order to learn appium.
Any native app, to begin with use appium provided UICatalog for iOS, and apps from playstore for android.
How to automate app if UI of app is always getting change
Scalable solution to automate UI app involves creating page objects, maintaining locators in one place and not spread out through out the test cases etc. It depends on the magnitude of change, is it the navigation that has changed, the element hierarchy that has changed etc. The resource id based selection is always preferred, which sort of reduces the change in automation script, if there is a layout change in the app. But I am afraid there is no single method to tackle this issue.
If you have multiple UI elements with same resource id you can make use of index along with resource-id.
WebElement element=driver.findElements(By.id("your_id")).get(1);
In android if the view is any kind of list view , all the elements will have same id , so in that case u can use the name to tap on it or u can create xpaths and use it to perform ur actions
Related
My site generates an arbitrary number of popups (please don't judge my monitization strategy). Each popup is a complex div that I eventually want reference to via xpath, but I specifically want the last one that was added to the DOM, since that will be the one that's on top.
So I tried this
//*[#class="popupContent"][last()]
I know I could try this
(//*[#class="popupContent"])[2]
but I have no idea how many of these popups there are.
But in the case of having multiple popups on screen at once, I get a reference to the first one (I'm not sure if this is reliably the case or not). Each of these popups has a popupContainer, but for the sake of this question, it is off limits.
Given that there are multiple elements with this class, how can I get the one that is on top (and thus interactive)?
These things aren't siblings, children, or parents of each other. Also, I have no idea how many exist on the page at any given time. I also do not have any control over the content or structure of the popups beyond that I know the class for one of their internal components (popupContent).
If this is not possible, please explain why.
I'm looking for a solution that will be compatible in a Java Selenium testing environment. No jquery please.
According to this question: Do WebDriver findElements retain the Table rows order on its retrieval findElements guarantees order, so in a Selenium environment, I could get the list from that and retrieve the last item.
List<WebElement> popups = findElements(By.classname("popup"));
WebElement activePopup = popups.get(popups.size - 1);
Xpath (//*[#class="popupContent"])[last()] and code below should give you same result - last element in the DOM:
List<WebElement> popups = driver.findElements(By.className("popup"));
WebElement activePopup = popups.get(popups.size - 1);
If last element not the top one try to get focused element:
WebElement focused = driver.switchTo().activeElement();
if (focused.getAttribute("class").equals("popupContent"))
//my active/interactive popup
You can check if last one is active now:
WebElement lastOne = driver.findElement(By.xpath("(//*[#class="popupContent"])[last()]"));
if (lastOne.equals(driver.switchTo().activeElement()))
//last one is active
Another approach could be to look for the popup with the highest z-index value:
private WebElement getTopPopUp() {
List<WebElement> allPopUps = driver.findElements(By.classname("popup"));
WebElement topPopUp = allPopUs.get(0);
for(WebElement popUp : allPopUs) {
if(Integer.parseInt(popUp.getCssValue("z-index")) > Integer.parseInt(topPopUp.getCssValue("z-index"))) {
topPopUp = popUp;
}
}
return topPopUp;
}
My web application is responsive. I.e. it resizes for different screen sizes. Our developers have coded for this, and as a result, most web elements found by xpath actually have 2. One of them for full screen and one for "mobile" or small screen.
For example, if I use Firepath to search for the Xpath by just link text, it will say "2 elements found" Only one can be seen, then if I resize the browser window, that one disappears and the other one can be seen.
My question is: Can I use one Selenium script for both screen size? It would automatically use whichever element is visible or is clickable? If I script for just one of the two elements, I'll get "Element is not clickable" exception.
Or s the best solution to add all of the mobile elements to the page object separately, and create my methods something like:
if (DesktopWebElement.isVisible)
do the test for desktop
else if (MobileWebElement.isVisible)
do the test for mobile
It seems like quite a bit of extra work to add every mobile element separately and script every method in the page object like this. In the interest of saving time, I was just wondering if there's a way I could use an Xpath that finds both elements, and Selenium will find the first one that is visible.
I have the situations where the columns that get displayed vary by screen size in addition to different buttons.
My page files contain methods like
public int getScreenWidth() {
Dimension aScreen = webDriver.manage().window().getSize();
return aScreen.getWidth();
}
Then my code has things like:
if (screenWidth < 1440 ) {
...
}
Developers worked out with the business which columns dropped and at which widths.
There are screen width cutoff values set by the developers that determine which button gets displayed. So I click on the desktop or mobile button depending on the current screen width. Ask development to document the screen width cutoff values that they coded.
Have you tried ExpectedConditions.visibilityOfElementLocatedBy, as per documents it would give you a visible element attached to the DOM. This would give you ability to just interact with the element that is visible our of the two
WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 30);
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocatedBy(By.xpath(".//someXpath")));
element.click();
You can also try elementTobeClickable condition if that suits you better. Note that this is NOT going to slow your tests down, as soon as the element is visible/clickable it will be returned.
I have two buttons on a page that have really similar xpaths -
the button im trying to click -
/html/body/div[#id='wrapper']/div[#id='content']/div[#id='contentarea']/div[#id='votecontent']/div[#id='votetext']/div[#id='voteboxes']/div[#id='votenow'][2]/form/input[2]
and the other button im trying to ignore -
/html/body/div[#id='wrapper']/div[#id='content']/div[#id='contentarea']/div[#id='votecontent']/div[#id='votetext']/div[#id='voteboxes']/div[#id='votenow'][1]/form/input[2]
the only difference between the two is the
[#id='votenow'][1]
and
[#id='votenow'][2]
but I can't figure out how to interact with the one that has the votenow[2], whichever way I go about it, it always seems to interact with the first one because that's the first one it finds
this is for java using the firefox driver, any suggestions would be great :)
Just find them both and get the desired one by index:
List<WebElement> buttons = driver.findElements(By.xpath("your xpath"));
WebElement secondButton = buttons.get(1);
First
Please talk to your developers! It is really bad practice to assign the same id to two different elements (in your case buttons) on the same page! It makes life for DEV and QA unnecessarily harder than it need be!
Second
The xpath-expressions you posted already contain the differentiation between these two buttons. So you just need to find the first one and click it.
via xpath:
You can use xpath - should be enough to search for the elements with id="votenow". As said before, you can be pretty precise in this case and already filter for the 2nd button:
WebElement button02 = driver.findElement(By.xpath("//div[#id='votenow'][2]/form/input[2]"));
button02.click();
via id:
As #alecxe already pointed out, you can also first go for a more general search of several elements and then filter the right one out. Personally I would use the id in this case:
List<WebElement> buttonWrappers = driver.findElements(By.id("votenow"));
// you want the button-input-element in the 2nd wrapper element, indexing starts at 0, so do this:
WebElement button02 = buttonWrappers.get(1).findElement(By.xpath("//input[2]"));
// since it seems there are several input elements below the desired div, you can use xpath again
I am working on a development script using Selenium and Appium and I'm running into the issue of the wrong element being picked up by the Selenium Locator.
Essentially, I want to click a button that has no ID assigned to it; so the only thing I have left to identify it by is its text.
public Element button1(){
By locator = By.xpath("//android.widget.TextView[#text='button1']");
return new xElement(driver.findElement(locator), locator);
}
This is my my locator method to get the button1 object. By the way, no other button on the screen has text anywhere close to button1's text. The method click called on the button has the format:
public void clickBtn1(){
button1().click();
}
The button being clicked essentially has the text "wheelbarrow". This is just to clarify that the button being pressed has text no where close to button1's value.
I have used UI automator multiple times to confirm button1's actual text value. The weird thing is the script works occasionally, so I'm not sure what the issue is.
I have also tried a "wait for enabled" method to account for race conditions.
Try using the Appium inspector to search for your button. You can type in the xpath and search for the element to see what it finds. The other nice thing about the inspector is you can see how the native control attributes map to the Appium attributes. 'text' may not be the attribute you actually want. Also, have you tried searching on properties on the Button itself (instead of TextView)?
If it's working occasional, First try to use different element other than xpath. Second, try to give some sleep command before you perform that action like
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOfElementLocated(ByLocator(locator)));
I am using Selenium 2.35 and having a unpredictable error while trying to click on an element in firefox like this:
new Actions(driver).moveToElement(element).click().perform();
The element I've located is a < span > element with a click event tied to it. The issue I am running into is occasionally when Firefox tries to click on the element is misses and clicks on an entirely different element. If I walk through my code using the debugger the issue never occurs which leads me to believe that the FirefoxDriver is just clicking on the wrong location on the browser, having to do with a timing issue. There are dynamically loaded < div >'s on my page that make the element I want to click shift down just before I locate it and send the .click() command. I believe this is the source of my issue. I can put a Thread.sleep(500) is ensure that it has enough time to finish animating and inserting the dynamic divs, but that seems sloppy to me. Is there a way I can tell my FirefoxDriver to wait until the elements on the screen are done shifting around before it tries to send the .click() event?
Also, I have tried to send just the element.click() but that seems to fail more often.
Update (9/5/13):
The solution I came to with the help of #MrTi 's comment was the following:
private void jsClickOnElementById(String id)
{
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.id(id)));
JavascriptExecutor js = (JavascriptExecutor)driver;
StringBuilder sb = new StringBuilder();
sb.append("var x = $('#" + id + "');");
sb.append("x.click();");
js.executeScript(sb.toString());
}
Just to ensure the element is actually on the page I added the initial "wait" call, and then use the JavascriptExecutor to run some jquery and click on the element on the page. This seems to work great for my case. If anyone has help as to some drawbacks to this solution they would be greatly appreciated as I'm fairly new to the JavascriptExecutor class.
In addition to ensuring that the page is actually visible with ExpectedConditions, this situation could be solved by handling animations (transitions) on page.
It's possible to disable all the animations on page, until page is refreshed, by executing:
(JavascriptExecutor)driver.executeScript("$('body').append('<style> * {transition: none!important;}</style>')")
Also, If you know the specific element that got the animation you can check if it's animated (JQuery animation selector):
(Boolean)((JavascriptExecutor)driver).executeScript("return $("#ELEMENT_ID").is(":animated")")
If the specific element unknown you can determine if all animations have finished:
(Boolean)((JavascriptExecutor)driver).executeScript("return $(":animated").length == 0 ")
Sadly in 2018 (five years later) I can confirm element.click() in selenium doesn't always click the right element. I still have to shell-out to jquery with JavascriptExecutor to click on an element by ID via jquery $(selector).click() to hit the element with confidence.
And it's NOT just that it can't find the element due to scrolls, animation, or offscreen, it appears Selenium's wrapper of Marionette's internal DOM tree is out of sync with firefox's geckodriver somehow for dynamically loaded elements (in this case a table row)
I believe what is happening is that the dynamic loading is making your selector select something else. If you post your HTML, (especially the before/after the dynamic loading) that would be immensely helpful in writing a better selector.
However, there are a couple of ways to wait until the loading is finished.
The first to wait until (Boolean)((JavascriptExecutor)driver).executeScript("return jQuery.active == 0") returns true. This waits for all JQuery on the page to finish (which is probably what is causing the dynamic loading). Its useful, but I would recomment waiting for something else.
The second option is to wait until an element that is dynamically loaded is present:
wait.until(ExpectedConditions.presenceOfElementSelectedBy(...selector...));
I like this method, as it will wait exactly as long as you need to wait, and no longer.
The final option is to write a selector that will always select the element, even if other stuff is going on. This may be your best option, but you may also run into StaleElementReferenceExceptions.