Want to Retrieve Xpath of Given WebElement - java

Using Selenium WebDriver, I am having a list of all web elements in the page. I want to write one function which will return me the XPath string of passed element.
Call for Function will be like:-
String XpathOfElement = myWebDriver.getXpath(My_Web_Element)
Hint :- I think we can use javascript(using JavaScriptExecuter). But not familiar with javascript.

Check this post, I wrote code to get an absolute XPath.
public static String getAbsoluteXPath(WebElement element)
{
return (String) ((JavascriptExecutor) driver).executeScript(
"function absoluteXPath(element) {"+
"var comp, comps = [];"+
"var parent = null;"+
"var xpath = '';"+
"var getPos = function(element) {"+
"var position = 1, curNode;"+
"if (element.nodeType == Node.ATTRIBUTE_NODE) {"+
"return null;"+
"}"+
"for (curNode = element.previousSibling; curNode; curNode = curNode.previousSibling) {"+
"if (curNode.nodeName == element.nodeName) {"+
"++position;"+
"}"+
"}"+
"return position;"+
"};"+
"if (element instanceof Document) {"+
"return '/';"+
"}"+
"for (; element && !(element instanceof Document); element = element.nodeType == Node.ATTRIBUTE_NODE ? element.ownerElement : element.parentNode) {"+
"comp = comps[comps.length] = {};"+
"switch (element.nodeType) {"+
"case Node.TEXT_NODE:"+
"comp.name = 'text()';"+
"break;"+
"case Node.ATTRIBUTE_NODE:"+
"comp.name = '#' + element.nodeName;"+
"break;"+
"case Node.PROCESSING_INSTRUCTION_NODE:"+
"comp.name = 'processing-instruction()';"+
"break;"+
"case Node.COMMENT_NODE:"+
"comp.name = 'comment()';"+
"break;"+
"case Node.ELEMENT_NODE:"+
"comp.name = element.nodeName;"+
"break;"+
"}"+
"comp.position = getPos(element);"+
"}"+
"for (var i = comps.length - 1; i >= 0; i--) {"+
"comp = comps[i];"+
"xpath += '/' + comp.name.toLowerCase();"+
"if (comp.position !== null) {"+
"xpath += '[' + comp.position + ']';"+
"}"+
"}"+
"return xpath;"+
"} return absoluteXPath(arguments[0]);", element);
}

This code works perfectly.
public String getXpath(WebElement ele) {
String str = ele.toString();
String[] listString;
if(str.contains("xpath")
listString = str.split("xpath:");
else if(str.contains("id")
listString = str.split("id:");
String last = listString[1].trim();
return last.substring(0, last.length() - 1);
}
This above function works only if the WebElement has xpath. Suppose your element has class, then use if-else concept with "class:" as split expression.

Almost any element in DOM can have a lot of valid xPathes. For example input field on Google search page can be found as: //*[#id='lst-ib'] //*[#class='gsfi'][1] //body//div//input[3]...
Which one do you expect to get?
Actually google has this algorithm in chrome. We can copy xpath of the element and in most cases it is awfull.
So it's possible to do, and if it's really vital for you look at NHtmlUnit - you can get dom of the page, find your element there and go up to root element adding tags to path string. I guess, it's poosible to get something like //body/div/div[2]/div[3]/... But why?

Related

how to get generalized xpath for object in Selenium webdriver

String str = "some value" ;
getTestBase().getDriver().findElement(By.xpath("//h2[.='" + str + "']//parent::div//div[#id='sectionList'][1]/section/div/button")).click();
I need to put the above in loop so that [1] keeps increasing every time , how can I update the above xpath for the integer ?
String str = "some value" ;
WebDriver driver = getTestBase().getDriver();
for(int i=0; i<5; i++){
driver.findElement(By.xpath("//h2[.='" + str
+"']//parent::div//div[#id='sectionList']["+i+"]/section/div/button")).click();
}
This should solve your problem. 5 is just an arbitrary limit i assumed for writing the solution.
On the other hand you can try and get all the elements and store the elements in a list. Then parse the list to get the the buttons and click on them.
Some thing like:
String str = "some value" ;
WebDriver driver = getTestBase().getDriver();
List<WebElement> listOfButtons = driver.findElements(By.xpath("//h2[.='" + str + "']//parent::div//div[#id='sectionList']//section/div/button""));
for(WebElement el: listOfButtons){
el.click();
}

Parsing html in Jsoup

I am trying to parse html tags here using jsoup. I am new to jsoup. Basically I need to parse the tags and get the text inside those tags and apply the style mentioned in the class attribute.
I am creating a SpannableStringBuilder for that I can create substrings, apply styles and append them together with texts that have no styles.
String str = "There are <span class='newStyle'> two </span> workers from the <span class='oldStyle'>Front of House</span>";
SpannableStringBuilder text = new SpannableStringBuilder();
if (value.contains("</span>")) {
Document document = Jsoup.parse(value);
Elements elements = document.getElementsByTag("span");
if (elements != null) {
int i = 0;
int start = 0;
for (Element ele : elements) {
String styleName = type + "." + ele.attr("class");
text.append(ele.text());
int style = context.getResources().getIdentifier(styleName, "style", context.getPackageName());
text.setSpan(new TextAppearanceSpan(context, style), start, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.append(ele.nextSibling().toString());
start = text.length();
i++;
}
}
return text;
}
I am not sure how I can parse the strings that are not between any tags such as the "There are" and "worker from the".
Need output such as:
- There are
- <span class='newStyle'> two </span>
- workers from the
- <span class='oldStyle'>Front of House</span>
Full answer: you can get the text outside of the tags by getting childNodes(). This way you obtain List<Node>. Note I'm selecting body because your HTML fragment doesn't have any parent element and parsing HTML fragment with jsoup adds <html> and <body> automatically.
If Node contains only text it's of type TextNode and you can get the content using toString().
Otherwise you can cast it to Element and get the text usingelement.text().
String str = "There are <span class='newStyle'> two </span> workers from the <span class='oldStyle'>Front of House</span>";
Document doc = Jsoup.parse(str);
Element body = doc.selectFirst("body");
List<Node> childNodes = body.childNodes();
for (int i = 0; i < childNodes.size(); i++) {
Node node = body.childNodes().get(i);
if (node instanceof TextNode) {
System.out.println(i + " -> " + node.toString());
} else {
Element element = (Element) node;
System.out.println(i + " -> " + element.text());
}
}
output:
0 ->
There are
1 -> two
2 -> workers from the
3 -> Front of House
By the way: I don't know how to get rid of the first line break before There are.

Looping a list through a column in webdriver

If I'm expecting a page to display 5 offers, how would I tell webdriver to list all those 5 offers in a ul?
The Ul html code is
<ul id="more-load" class="product_list_widget pagination-centered" style="padding-top:15px;">
I think you would use
List<WebElement> allElements = driver.findElements(By.xpath("//div[#id='more-load']/ul/li"));
for (WebElement element: allElements) {
System.out.println(element.getText());
}
but I'm not sure how to print each individual offer in the Ul and match the 5 offers expected to be displayed
EDITED CODE
never mind used this and worked
WebElement allElements = driver.findElement(By.id("more-load"));
List<WebElement> liElements = allElements.findElements(By.tagName("li"));
for (int i = 0; i < liElements.size(); i++)
{
System.out.println("-------------------------------------------------------");
System.out.println(liElements.get(i).getText());
}
however if I have a column on the left side, with ul = product categories, how would I loop it to go through each individual link text and preform the same function
Let's say your 5 offers are as below:
Offer1
Offer2
Offer3
Offer4
Offer5
now your approach is correct till for loop:
then you can do something like :
String offerString = Offer1 + " " + Offer2 + " " + Offer3 + " " + Offer4 + " " + Offer5;
for (WebElement element: allElements) {
if(offerString.contains(element.getText()){
System.out.println("Offer item is: " + element.getText());
}
}
Stick with your original attempt, except change the line:
List<WebElement> allElements = driver.findElements(By.xpath("//div[#id='more-load']/ul/li"));
to
List<WebElement> allElements = driver.findElements(By.xpath("//ul[#id='more-load']/li"));
using java8:
List<String> offers = driver
.findElements(By.cssSelector("#more-load li"))
.stream()
.map(WebElement::getText)
.collect(Collectors.toList());
and you can assert offers.size() to have some expected value by any assert library
To get linked text elements in UL you can use next selector:
driver.findElements(By.cssSelector("ul a"))

Exception in thread "main" org.eclipse.swt.SWTException: Could not complete the operation due to error 800a025e

I am developing an SWT application with an embedded browser and have used the following function to implement search function in the browser.This works perfectly if I search for words with more than two letters in it. If I search for 1 letter or 2 letters, it throws the error
Exception in thread "main" org.eclipse.swt.SWTException: Could not complete the operation due to error 800a025e.
public void searchNextOccuranceOfWord(String str)
{
System.out.println("Search next button is pressed");
str=str.trim();
String script_finddown= " var supported = false;"+
"var found = false;"+
"if (window.find) {"+ // Firefox, Google Chrome, Safari
"supported = true;"+
// if some content is selected, the start position of the search
// will be the end position of the selection
"found = window.find (\""+ str+ "\",false,false,false,false,true,false);return \" found \"; "+
"}"+
"else {"+
"if (document.selection && document.selection.createRange) {"+ // Internet Explorer, Opera before version 10.5
"var textRange = document.selection.createRange ();"+
"if (textRange.findText) {"+ // Internet Explorer
"supported = true;"+
// if some content is selected, the start position of the search
// will be the position after the start position of the selection
"if (textRange.text.length > 0) {"+
"textRange.collapse (true);"+
"textRange.move (\"character\", 1);"+
"}"+
"found = textRange.findText (\""+str+ "\",1);"+
"if (found) {"+
"textRange.select ();return \"found \" ; "+
"}"+
"}"+
"}"+
"if (supported) {"+
"if (!found) {"+
"return \" not found\"; "+
"}"+
"}"+
"else {"+
"return \" not suppoerted\"; " +
"}"+
"} " ;
//boolean ex = browser.execute("window.find(\"" + str + "\" ,false,false,true,false,true,false )");
String ex = (String)browser.evaluate(script_finddown);
System.out.println(ex);
// TODO Auto-generated method stub
}
I debugged it and saw that there is problem at the statement "textRange.select ()"

Basic XML parsing issues. Cannot find node names via Java XmlStreamReader method. Any ideas?

Having no luck in parsing some basic XML. I'm doing this in the Apex language, but it's syntactically nearly identical to Java and in this case uses java.xml.stream.XMLStreamReader as its XML parsing engine.
The problem is: I'm having no luck getting to any of the actual XML node names. The getLocalName() method within the XmlStreamReader class always returns null for all nodes as I loop through them.
code is here
Very basic functionality at this point. If you run this, you will see that reader.getLocalName() always returns null and so do all accompanying methods (getNameSpace(), getLocation(), getPrefix()).
Any ideas why? I'm stuck with the XML arriving in the format it's in...so I have to parse it as-is. I could use various workarounds (regEx, counting nodes, etc.) but those are messy and not ideal.
I have reformed your code into one block that can be tested in the workbench's anonymous execution window. I run the code and then filter the Execution Log to show USER_DEBUG statements. The output shows node names and text as you would expect.
I think the key is to use the APEX methods hasText() and hasName().
String XML_STR = '<document>' + '<result>success</result>' +'<resultcode>000000</resultcode>' +
'<note></note>' + '<item>' +'<quantity>1</quantity>' +
'<fname>Bob</fname>' +'<lname>Tungsten</lname>' +
'<address>23232 Fleet Street</address>' +'<city>Santa Clara</city>' +
'<state>CA</state>' +'<zip>94105</zip>' +
'<country>United States</country>' +'<email>blahblahblah#blahblahblah.com</email>' +
'<phone>4155555555</phone>' +'</item>' +'</document>';
XmlStreamReader reader = new XmlStreamReader(XML_STR);
while (reader.hasNext()) {
System.debug('$$$ reader.getEventType(): ' + reader.getEventType());
if (reader.hasName()) {
System.debug('$$$ reader.getLocalName(): ' + reader.getLocalName());
// System.debug('$$$ reader.getNamespace(): ' + reader.getNamespace());
// System.debug('$$$ reader.getprefix(): ' + reader.getprefix());
}
if (reader.hasText()) {
System.debug('$$$ reader.getText(): ' + reader.getText());
}
System.debug('$$$ Go to next');
reader.next();
}
Here is another solution based on the recipe by Jon Mountjoy
http://developer.force.com/cookbook/recipe/parsing-xml-using-the-apex-dom-parser
private String walkThrough(DOM.XMLNode node) {
String result = '\n';
if (node.getNodeType() == DOM.XMLNodeType.COMMENT) {
return 'Comment (' + node.getText() + ')';
}
if (node.getNodeType() == DOM.XMLNodeType.TEXT) {
return 'Text (' + node.getText() + ')';
}
if (node.getNodeType() == DOM.XMLNodeType.ELEMENT) {
result += 'Element: ' + node.getName();
if (node.getText().trim() != '') {
result += ', text=' + node.getText().trim();
}
if (node.getAttributeCount() > 0) {
for (Integer i = 0; i< node.getAttributeCount(); i++ ) {
result += ', attribute #' + i + ':' + node.getAttributeKeyAt(i) + '=' + node.getAttributeValue(node.getAttributeKeyAt(i), node.getAttributeKeyNsAt(i));
}
}
for (Dom.XMLNode child: node.getChildElements()) {
result += walkThrough(child);
}
return result;
}
return ''; //should never reach here
}
private String parse(String toParse) {
DOM.Document doc = new DOM.Document();
try {
doc.load(toParse);
DOM.XMLNode root = doc.getRootElement();
return walkThrough(root);
} catch (System.XMLException e) { // invalid XML
return e.getMessage();
}
}
String XML_STR = '<document>' + '<result>success</result>' +'<resultcode>000000</resultcode>' +
'<note></note>' + '<item>' +'<quantity>1</quantity>' +
'<fname>Bob</fname>' +'<lname>Tungsten</lname>' +
'<address>23232 Fleet Street</address>' +'<city>Santa Clara</city>' +
'<state>CA</state>' +'<zip>94105</zip>' +
'<country>United States</country>' +'<email>blahblahblah#blahblahblah.com</email>' +
'<phone>4155555555</phone>' +'</item>' +'</document>';
System.debug(parse(XML_STR));

Categories