Appium: Find element by partial elementId/UID with XPath - java

I am working on a test automation for an iOS app.
I use the Appium-desktop-client (1.15.1) on MacOS (10.15) and find an element on Iphone 11 (13.3) via the Inspector with the following attributes:
screenshot from the attributes
The goal is to do the following in java with appium (6.1.0) and selenium (3.13.0):
Find this element via the elementId and get the variable value (in this case 2,6 km).
The problem is reaching the element. In the Inspector, the accessibility-id and the name are always equal to the value, which means that I cannot localize the element with them.
The elementId is not fixed, it varies after the 3rd block of numbers (that means I can only use the 45000000-0000-0000.) I can also reproduce this elementId from the Inspector using the code from the "UID" attribute.
To localize the element I tried the following:
appiumDriver.findElementByXPath("//*[contains(#id,'45000000-0000-0000')]");
appiumDriver.findElementByXPath("//*[contains(#name,'45000000-0000-0000')]");
appiumDriver.findElementByXPath("//*[contains(#uid,'45000000-0000-0000')]");
appiumDriver.findElementByXPath("//*[contains(#elementId,'45000000-0000-0000')]");
appiumDriver.findElementByXPath("//*[contains(#RuntimeId,'45000000-0000-0000')]");
appiumDriver.findElementByXPath("//*[contains(#ProcessId,'45000000-0000-0000')]");
appiumDriver.findElementByXPath("//*[contains(#class,'45000000-0000-0000')]");
However, with all these attempts to locate the element came an org.openqa.selenium.NoSuchElementException.
My question now is, what do I have to write in the xpath to find the element via the part of the elementId from the Inspector (or the attribute UID) and then be able to read out the value.

I would try this secure option :
//*[local-name(#*)="#elementId"][starts-with(normalize-space(string(#*)),"45000000-0000-0000")]
EDIT : contains option doesn't work because the elementId is not the first attribute of the element.
Could you try :
//*[#*="45000000"]
If this doesn't work let's bruteforce this with :
//*[contains(#*[2],'45000000')]
//*[contains(#*[3],'45000000')]
//*[contains(#*[4],'45000000')]
//*[contains(#*[5],'45000000')]
//*[contains(#*[6],'45000000')]
//*[contains(#*[7],'45000000')]
//*[contains(#*[8],'45000000')]
//*[contains(#*[9],'45000000')]
//*[contains(#*[10],'45000000')]
//*[contains(#*[11],'45000000')]

Related

How to read properties of DOM object via selenium Java API?

I need to read some properties of a DOM object via the selenium Java API. I'll explain my requirement via an example.
Let's say first I would like to find the <g> element highlighted in the Chrome Developer Tools (as shown below). I can easily do so via the Selenium Java API with the following code.
WebElement gElement = driver.findElement(By.xpath("//*[#data-id='node_grp_0_id52UVV33EHE7']"));
Then I would like to read several properties of this <g> object via the Selenium Java API. So, I click on this <g> element in the Chrome Developer Tools and open the Properties view on the right hand side of the Chrome Developer Tools (as shown below) in order to find the path to various properties. After finding the paths, I now would like to read several of these properties (e.g. ariaChecked and __data__.label) via the Selenium Java API as shown below:
gElement.getAttribute("ariaChecked");
gElement.getAttribute("__data__.label");
Both the above mentioned lines of code returns null.
The following code also does not return the desired property values:
element.getCssValue("ariaChecked");
element.getCssValue("__data__.label");
Does anyone know how to read various properties of a DOM object (listed in the Properties view of the Chrome Developer Tools as shown below) via the selenium Java API?
Thanks in advance!
I've found a workaround to read a property of a web element via Java Script as shown below. Nested properties are also supported by this workaround:
public String getProperty(final String name) {
return getJavascriptExecutor().executeScript("return arguments[0]." + name + ";", webElement).toString();
}
The above method can be invoked as shown below:
String name = getProperty("name");
or
String label = getProperty("__data__.label"); // This is a nested property.

InvalidSelectorException while selecting value from the dropdown

I am trying to select value from the drodown. this is my code.
driver.findElement(By.xpath(".//*[#id='accountSelectContainer']/span/a/span[1]")).click();
driver.findElement(By.xpath("//ul[#id='ui-id-1']/li/a[equals(text(),'60091 - AFCENT')]")).click();
Now here i have hardcoded the value, which works perfect but i am reading my testdata from excel file . so instead of using direct hard code values , i want to declare my testdata in xpath and read it from excel file. so i tried to this:
Efforts
public void combobox(String testData)
{
driver.findElement(By.xpath(".//*[#id='accountSelectContainer']/span/a/span[1]")).click();
driver.findElement(By.xpath("//ul[#id='ui-id-1']/li/a[equals(text(),'"+testData+"')]")).click();
}
But i am getting the exception
org.openqa.selenium.InvalidSelectorException: invalid selector: Unable
to locate an element with the xpath expression
//ul[#id='ui-id-1']/li/a[equals(text(),'60091 - AFCENT')] because of
the following error: SyntaxError: Failed to execute 'evaluate' on
'Document': The string '//ul[#id='ui-id-1']/li/a[equals(text(),'60091
- AFCENT')]' is not a valid XPath expression.
I tried to change it to "+testData+" too instead of using '"+testData+"'
But same exception.
I tried this code too:
driver.findElement(By.xpath(".//*[#id='accountSelectContainer']/span/a/span[1]")).click();
List<WebElement> options = driver.findElements(By.xpath("//ul[#id='ui-id-1']/li"));
for (WebElement option : options) {
if(testData.equals(option.getText()))
option.click();
}
which works perfect but after this code execution , it is making my browser to wait for about 15 secs before executing next step or for quit too. i am not getting Why so ?
Please need suggestion or any ideas..
I doubt your fast attempt works perfect as xpath doesn't have equals. You would have gotten the same exception. To check for text equality use =
"//ul[#id='ui-id-1']/li/a[text()='"+testData+"']"
You can also use contains
"//ul[#id='ui-id-1']/li/a[contains(text(),'"+testData+"')]"
First: the exception you have got is because you xpath: //ul[#id='ui-id-1']/li/a[equals(text(),'60091 - AFCENT')] is not correct syntax.
Second: the code that you can run is not make your browser wait for 15s, it's just because problem about internet connection or you computer is a little slow.

How to speed up page parsing in Selenium

What can I do in case if I load the page in Selenium and then I have to do like 100 different parsing requests to this page?
At this moment I use different driver.findElement(By...) and the problem is that every time it is a http (get/post) request from java into selenium. From this case one simple page parsing costs me like 30+ seconds (too much).
I think that I must get source code (driver.getPageSource()) from first request and then parse this string locally (my page does not change while I parse it).
Can I build some kind of HTML object from this string to keep working with WebElement requests?
Do I have to use another lib to build HTML object? (for example - jsoup) In this case I will have to rebuild my parsing requests from webelement's and XPath.
Anything else?
When you call findElement, there is no need for Selenium to parse the page to find the element. The parsing of the HTML happens when the page is loaded. Some further parsing may happen due to JavaScript modifications to the page (like when doing element.innerHTML += ...). What Selenium does is query the DOM with methods like .getElementsByClassName, .querySelector, etc. This being said, if your browser is loaded on a remote machine, things can slow down. Even locally, if you are doing a huge amount of round-trip to between your Selenium script and the browser, it can impact the script's speed quite a bit. What can you do?
What I prefer to do when I have a lot of queries to do on a page is to use .executeScript to do the work on the browser side. This can reduce dozens of queries to a single one. For instance:
List<WebElement> elements = (List<WebElement>) ((JavascriptExecutor) driver)
.executeScript(
"var elements = document.getElementsByClassName('foo');" +
"return Array.prototype.filter.call(elements, function (el) {" +
" return el.attributes.whatever.value === 'something';" +
"});");
(I've not run the code above. Watch out for typos!)
In this example, you'd get a list of all elements of class foo that have an attribute named whatever which has a value equal to something. (The Array.prototype.filter.call rigmarole is because .getElementsByClassName returns something that behaves like an Array but which is not an Array so it does not have a .filter method.)
Parsing locally is an option if you know that the page won't change as you examine it. You should get the page's source by using something like:
String html = (String) ((JavascriptExecutor) driver).executeScript(
"return document.documentElement.outerHTML");
By doing this, you see the page exactly in the way the browser interpreted it. You will have to use something else than Selenium to parse the HTML.
Maybe try evaluating your elements only when you try to use them?
I dont know about the Java equivalent, but in C# you could do something similar to the following, which would only look for the element when it is used:
private static readonly By UsernameSelector = By.Name("username");
private IWebElement UsernameInputElement
{
get { return Driver.FindElement(UsernameSelector); }
}

Selenium/ Java how to verify the this complex text on page

I want to verify below text(HTML code) is present on page which as // characters , etc using selenium /jav
<div class="powatag" data-endpoint="https://api-sb2.powatag.com" data-key="b3JvYmlhbmNvdGVzdDErYXBpOjEyMzQ1Njc4" data-sku="519" data-lang="en_GB" data-type="bag" data-style="bg-act-left" data-colorscheme="light" data-redirect=""></div>
Appreciate any help on this
I believe you're looking for:
String textToVerify = "some html";
boolean bFoundText = driver.getPageSource.contains(textToVerify)
Assert.assertTrue(bFoundText);
Note, this checks the page source of the last loaded page as detailed here in the javadoc. I've found this to also take longer to execute, especially when dealing with large source codes. As such, this method is more prone to failure than validating the attributes and values and the answer from Breaks Software is what I utilize when possible, only with an xpath selector
As Andreas commented, you probably want to verify individual attributes of the div element. since you specifically mentioned the "//", I'm guessing that you are having trouble with the data-endpoint attribute. I'm assuming that your data-sku attribute will bring you to a unique element, so Try something like this (not verified):
String endpoint = driver.findElement(
new By.ByCssSelector("div[data-sku='519']")).getAttribute("data-endpoint");
assertTrue("https://api-sb2.powatag.com", endpoint);

selecting list value by index number using selenium java

I tried using the select() method in my code, but Eclipse is showing an error. Is select() an inbuilt method of Selenium? I don't get it.
select(driver2.findElement(By.xpath("//*#id='webLossReport.contact.address.state']")),index=i);
Eclipse says "The method select(WebElement, int) is undefined for the type entry" and it is giving me an option to create a method in this class.
Please let me know how others are using it. My requirement is to "select a list value based on Index number"
Update: Code Posted as requested,
WebElement LSD = driver2.findElement(By.xpath("//select[#id='webLossReport.lossInformation.locationOfLoss.state']"));
List <WebElement> LLS = LossStateDropdown.findElements(By.tagName("option"));
int i= LLS.size();
select(driver2.findElement(By.xpath("//*#id='webLossReport.contact.address.state']")),index=i);
You're somehow lost between Selenium RC and Selenium WebDriver. Assuming you want to use WebDriver, see this doc, it explains it all.
You can either do the following - it directly finds the third <option> tag in the specified <select> and clicks it:
driver.findElement(By.xpath("id('selectsId')/option[3]")).click();
or this using the Select class:
Select sel = new Select(driver.findElement(By.id("selectsId")));
sel.selectByIndex(3);
In C#, I solved using this:
selenium.Select("id=yourID", "index=" + i.ToString());
I'm not familiar with this library, but the Selenium reference page gives the following signature for select:
select(java.lang.String selectLocator, java.lang.String optionLocator)
In your code, the second argument is index=i which is assigning index to the value of i, and then returning that int. What string were you planning on passing as the second argument? "index=i"? "index=" + i?

Categories