webdriver: get element's xpath? - webdriver

Is it possible to return a WebElement's xpath?

Not directly from WebDriver, but you can fake it if you really need to:
public String getElementXPath(WebDriver driver, WebElement element) {
return (String)((JavascriptExecutor)driver).executeScript("gPt=function(c){if(c.id!==''){return'id(\"'+c.id+'\")'}if(c===document.body){return c.tagName}var a=0;var e=c.parentNode.childNodes;for(var b=0;b<e.length;b++){var d=e[b];if(d===c){return gPt(c.parentNode)+'/'+c.tagName+'['+(a+1)+']'}if(d.nodeType===1&&d.tagName===c.tagName){a++}}};return gPt(arguments[0]).toLowerCase();", element);
}
The Javascript is from this post, minified to fit on one line. It may not be perfect, but could give you an idea of where to go. Most drivers implement the JavascriptExecutor interface and have the capability of executing Javascript in the browser. executeScript can return any primitive JavaScript type, an HTML element, or non-nested list of any of the preceding.
Not all browsers support xpath the same way, so be careful if using these xpaths to select elements. Also, not all browsers have native xpath support (cough IE cough), so it was faked in that case.

If WebElement was found by By.xpath:
on Java:
public static String GetWebElementXpath(WebElement El) throws AssertionError{
if ((El instanceof WebElement)){
Object o = El;
String text = o.toString();
/* text is smth like this
[[FirefoxDriver: firefox on WINDOWS (9170d4a5-1554-4018-adac-f3f6385370c0)] -> xpath: //div[contains(#class,'forum-topic-preview')]//div[contains(#class,'small-human')]]
*/
text = text.substring( text.indexOf("xpath: ")+7,text.length()-1);
return text;
}else { Assert.fail("Argument is not an WebElement, his actual class is:"+El.getClass()); }
return "";
}

Both of the above answers suffer from the same problem. By returning the completed XPath with the .toLowerCase() function called, any XPath containing an id with a capital letter will not work.
Example: //div[#id="deviceblock-1111"] will not work on tag <div id="deviceBlock-1111">
You could however just remove the .toLowerCase() call off the return but you'll end up with XPath's looking like this: //DIV[#id="deviceBlock-1111"]/DIV[2]/SELECT[1]/OPTION[5]
To solve this use the function below.
public String GetElementXPath(WebElement element, WebDriver driver)
{
return (String) ((JavascriptExecutor) driver).executeScript(
"getXPath=function(node)" +
"{" +
"if (node.id !== '')" +
"{" +
"return '//' + node.tagName.toLowerCase() + '[#id=\"' + node.id + '\"]'" +
"}" +
"if (node === document.body)" +
"{" +
"return node.tagName.toLowerCase()" +
"}" +
"var nodeCount = 0;" +
"var childNodes = node.parentNode.childNodes;" +
"for (var i=0; i<childNodes.length; i++)" +
"{" +
"var currentNode = childNodes[i];" +
"if (currentNode === node)" +
"{" +
"return getXPath(node.parentNode) +
'/' + node.tagName.toLowerCase() +
'[' + (nodeCount+1) + ']'" +
"}" +
"if (currentNode.nodeType === 1 && " +
"currentNode.tagName.toLowerCase() === node.tagName.toLowerCase())" +
"{" +
"nodeCount++" +
"}" +
"}" +
"};" +
"return getXPath(arguments[0]);", element);
}
This will return a correctly formatted, unique XPath from your WebElement.
//div[#id="deviceBlock-1111"]/div[2]/select[1]/option[5]

I would comment directly on dflems' answer, but I do not have the reputation to do so.
Converting the entire xpath to lower case is fine unless the xpath contains an id value that is not all lower-case. Below is a modified version of dflems' Javascript, but in Python instead of Java:
def get_xpath_from_element(driver, element):
return driver.execute_script("gPt=function(c){if(c.id!==''){return'id(\"'+c.id+'\")'}if(c===document.body){return c.tagName}var a=0;var e=c.parentNode.childNodes;for(var b=0;b<e.length;b++){var d=e[b];if(d===c){return gPt(c.parentNode)+'/'+c.tagName.toLowerCase()+'['+(a+1)+']'}if(d.nodeType===1&&d.tagName===c.tagName){a++}}};return gPt(arguments[0]);", element)
xpath selenium python javascript

public String getElementXPath(WebDriver driver, WebElement element) {
String javaScript = "function getElementXPath(elt){" +
"var path = \"\";" +
"for (; elt && elt.nodeType == 1; elt = elt.parentNode){" +
"idx = getElementIdx(elt);" +
"xname = elt.tagName;" +
"if (idx > 1){" +
"xname += \"[\" + idx + \"]\";" +
"}" +
"path = \"/\" + xname + path;" +
"}" +
"return path;" +
"}" +
"function getElementIdx(elt){" +
"var count = 1;" +
"for (var sib = elt.previousSibling; sib ; sib = sib.previousSibling){" +
"if(sib.nodeType == 1 && sib.tagName == elt.tagName){" +
"count++;" +
"}" +
"}" +
"return count;" +
"}" +
"return getElementXPath(arguments[0]).toLowerCase();";
return (String)((JavascriptExecutor)driver).executeScript(javaScript, element);
}

There is a way to get the elements XPath without the use of JavaScript.
Define starting point of outer XPath, for example body tag.
Check all possible inward tags with selenium for NoSuchElementException.
Check getText for the lists of XPaths generated.
win

public static String getXPathFromElement(WebElement element) {
String elementDescription = element.toString();
return elementDescription.substring(elementDescription.lastIndexOf("-> ") + 3, elementDescription.lastIndexOf("]"));
}
Web element toString() looks like this:
'[[FirefoxDriver: firefox on WINDOWS (ceb69f9f-bef4-455d-b626-ab439f195be6)] -> id: pageBeanfundDescription]'
I just extract the id/xpath.

/**
* This method return By reference for the WebElement passed to it as a parameter.
* #param element
* #return
*/
public static By convertWebElementToByReference(WebElement element)
{
By byLocator = null;
String elementDescription = element.toString();
String elementTypeAndValue[] = (elementDescription.substring(elementDescription.lastIndexOf("-> ") + 3, elementDescription.lastIndexOf("]"))).split(":");
switch (elementTypeAndValue[0].trim())
{
case "id": byLocator = By.id(elementTypeAndValue[1].trim());
break;
case "xpath": byLocator = By.xpath(elementTypeAndValue[1].trim());
break;
case "link text": byLocator = By.linkText(elementTypeAndValue[1].trim());
break;
case "tag name": byLocator = By.tagName(elementTypeAndValue[1].trim());
break;
case "class name": byLocator = By.className(elementTypeAndValue[1].trim());
break;
case "partial link text": byLocator = By.partialLinkText(elementTypeAndValue[1].trim());
break;
case "name": byLocator = By.name(elementTypeAndValue[1].trim());
break;
case "css selector": byLocator = By.cssSelector(elementTypeAndValue[1].trim());
break;
default:
throw new RuntimeException("Invalid locator type: " + elementTypeAndValue[0].trim());
}
return byLocator;
}

Related

How to get Text from Shadow Dom element in WebDriver

i want to fetch the text from shadow element of Dom
http://prntscr.com/e9smzg
I have tried below code but its not working..
public String ShadowRootElement(String str) {
WebElement ele = (WebElement) ((JavascriptExecutor) driver).executeScript("return arguments[0].shadowRoot",getElementByXpath(str));
return ele.findElement(By.xpath("//div[#id='inner-editor']")).getText().toString();
}
Please refer attached screenshot link for html code.
public String getEmailId(String str){
return ShadowRootElement(Repo.get("ipEmailId"));
}
First of all, the way you call ele.findElement(By.xpath("//div[#id='inner-editor']")).getText().toString(); is troublesome.
To locate elements under shadow root node,By.xpath() won't work. Only By.id() & By.cssSelector() will work as valid locators. Please refer to this post for more details.
Secondly (and unfortunately), I found even if you can locate the node under shadow root, element.getText() method would return an empty string.. Simply put it doesn't work for me either :-(
you will not be able to use xpath with shadowroots, since xpath is applied to DOM
Here, you can pull back all the elements, then use css or other to check if text exists, eg (use driver instead of session, since I wrap my driver):
public static String getAllShadowRootsText(DriverSessions session, String rootNode)
{
String elsText = "";
try {
List<SearchContext> sroots = getAllShadowRoots(session, rootNode);
for(SearchContext sroot : sroots){
// we have to specify the elements with shadowroot children, we cant just get all *
List<WebElement> els = sroot.findElements(By.cssSelector(validDomTypes));
for(WebElement el : els) {
elsText = elsText + el.getText();
}
}
}
catch (Exception e) {} // we might want to loop this, pages change and shadow roots move / go stale
return elsText;
}
public static List<SearchContext> getAllShadowRoots(DriverSessions session, String rootNode)
{
String script = ""
+ "function getShadowRoots (node, sroots, func) { "
+ "var done = func(node); "
+ "if (done) {return true;} "
+ "if ('shadowRoot' in node && node.shadowRoot) { "
+ "sroots.push(node.shadowRoot); "
+ "var done = getShadowRoots(node.shadowRoot, sroots, func); "
+ "if (done) {return true;} "
+ "} "
+ "node = node.firstChild; "
+ "while (node) { "
+ "var done = getShadowRoots(node, sroots, func); "
+ "if (done) {return true;} "
+ "node = node.nextSibling; "
+ "} "
+ "} "
+ "try { "
+ "sroots = new Array(); "
+ "getShadowRoots("+rootNode+", sroots, function (node, sroots) {}); "
+ "return sroots;"
+ "} "
+ "catch(err){return null};";
JavascriptExecutor js = (JavascriptExecutor)session.getDriver();
#SuppressWarnings("unchecked")
List<SearchContext> els = (List<SearchContext>) js.executeScript(script);
return els;
}

TypeError: window.tinyMCE.execInstanceCommand is not a function

I can't add any shortcode in my wordpress editor. it shows - Uncaught TypeError: Object [object Object] has no method 'execInstanceCommand' . plesase help me to solve this.
the code(tinymce.js)
function init() {
tinyMCEPopup.resizeToInnerSize();
}
function getCheckedValue(radioObj) {
if(!radioObj)
return "";
var radioLength = radioObj.length;
if(radioLength == undefined)
if(radioObj.checked)
return radioObj.value;
else
return "";
for(var i = 0; i < radioLength; i++) {
if(radioObj[i].checked) {
return radioObj[i].value;
}
}
return "";
}
function tjshortcodesubmit() {
var tagtext;
var tj_shortcode = document.getElementById('tjshortcode_panel');
// who is active ?
if (tj_shortcode.className.indexOf('current') != -1) {
var tj_shortcodeid = document.getElementById('tjshortcode_tag').value;
switch(tj_shortcodeid)
{
case 0:
tinyMCEPopup.close();
break;
case "button":
tagtext = "["+ tj_shortcodeid + " url=\"#\" style=\"white\" size=\"small\"] Button text [/" + tj_shortcodeid + "]";
break;
case "alert":
tagtext = "["+ tj_shortcodeid + " style=\"white\"] Alert text [/" + tj_shortcodeid + "]";
break;
case "toggle":
tagtext = "["+ tj_shortcodeid + " title=\"Title goes here\"] Content here [/" + tj_shortcodeid + "]";
break;
case "tabs":
tagtext="["+tj_shortcodeid + " tab1=\"Tab 1 Title\" tab2=\"Tab 2 Title\" tab3=\"Tab 3 Title\"] [tab]Insert tab 1 content here[/tab] [tab]Insert tab 2 content here[/tab] [tab]Insert tab 3 content here[/tab] [/" + tj_shortcodeid + "]";
break;
default:
tagtext="["+tj_shortcodeid + "] Insert you content here [/" + tj_shortcodeid + "]";
}
}
if(window.tinyMCE) {
//TODO: For QTranslate we should use here 'qtrans_textarea_content' instead 'content'
window.tinyMCE.execInstanceCommand('content', 'mceInsertContent', false, tagtext);
//Peforms a clean up of the current editor HTML.
//tinyMCEPopup.editor.execCommand('mceCleanup');
//Repaints the editor. Sometimes the browser has graphic glitches.
tinyMCEPopup.editor.execCommand('mceRepaint');
tinyMCEPopup.close();
}
return;
}
I had the same problem. Change your code to this and it should work:
if(window.tinyMCE) {
/* get the TinyMCE version to account for API diffs */
var tmce_ver=window.tinyMCE.majorVersion;
if (tmce_ver>="4") {
window.tinyMCE.execCommand('mceInsertContent', false, tagtext);
} else {
window.tinyMCE.execInstanceCommand('content', 'mceInsertContent', false, tagtext);
}
tinyMCEPopup.editor.execCommand('mceRepaint');
tinyMCEPopup.close();
}
return;
}
Note: since .js files are cached, you'll need to do a hard refresh to get this to work. If you are still seeing the same console errors, that would likely be the cause.
Scott B's answer is partially innacurate.
The point of execInstanceCommand in TinyMCE version 3 was to execute a command on a specific instance of TinyMCE in the document. Calling execCommand without specifying an instance will either use the focused instance or the first instance in the document, if none is currently focused.
To specify the instance you would like to execute your command on in TinyMCE version 4, call execCommand on the desired editor instance like so:
tinyMCE.get(editorId).execCommand(...);

Redirecting to other page ? In asp.net?

In web application [asp.net], i write the code to redirect to other page with in the solution, but it is giving exception.
Response.Redirect("abc.aspx");
can you help me, thank you.
WeeklyAttendanceMailStatusBE obj = new WeeklyAttendanceMailStatusBE();
obj.CreatedBy = Session["xxx"].ToString();
obj.Sesssionid = Session.SessionID.ToString();
obj.StatusDate = Convert.ToDateTime(DateTime.Now.Year.ToString() + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day.ToString() + " " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString());
obj.Types = "Insert";
if (radLastWeek.Checked == true)
{
obj.Flags = 1;
obj.LastWeekStatus = 1;
int result = new WeeklyAttendanceMailStatusBL().InsertWeeklyAttendanceMailStatus(obj);
if (result > 0)
{
Response.Redirect("abc.html");
}
else
{
DateTime dt = DateTime.Now;
ScriptManager.RegisterStartupScript(this, GetType(), "message", "<script> alert('Not Submited Mail Status')</status>", false);
}
}
else
{
}
May be this is because of "thread is being aborted" exception, handle exception with try catch statements or specific catch class called ThreadAbortException
Please refer this for more information.
Try this :
Response.Redirect("abc.aspx",false);
Add a second argument (boolean value) that indicates whether to stop running the current page.
Reference :
HOW TO: Use Response.Redirect in ASP.NET with Visual Basic .NET

fusiontable query for where

Is there any way to generate where condition based on user input dynamically.I have a select box with the option '>','<','equals','starts with','ends with'.Based on this condition where clause should be generated and query should be executed.Please help me.i need examples.
since i have around 80 columns in my table i cant use if else loop.
function querymap()
{
var querypass=document.getElementById('query-pass').value.replace(/'/g, "\\'");
if(querypass=='hhSanitHouseType')
{
var operator=document.getElementById('operatorstring').value.replace(/'/g, "\\'");
if(operator=='>')
{
var textvalue=document.getElementById("text-value").value.replace(/'/g, "\\'");
layer.setQuery("SELECT 'geometry',hhSanitHouseType FROM " + tableid + " WHERE 'hhSanitHouseType' > '" + textvalue + "'");
}
}
else
{
alert("false");
}
}
Maybe you can check this example, especially the function generateWhere(columnName, low, high).
You don't have to use if/else for your operator, just check for a valid input (i.e. that the operator is one of '>','<','equals','starts with','ends with') and then pass it directly to your query, something like that
var operator = ...;
var textvalue = ...;
layer.setQuery("SELECT 'geometry',hhSanitHouseType FROM " + tableid + " WHERE 'hhSanitHouseType'" + operator + " '" + textvalue + "'");

keep JavaScript array variable during post-back

How can I keep the values of ;ctrlIDhdnImageSourceArrayJs,ctrlIDhdnElementsArayJsHidden during a web page life-cycle...
"<script language = javascript>
debugger;
var ctrlIDhdnImageSourceArrayJs = '" + this.hdnImageSourceArrayJs.ClientID + #"';
var ctrlIDhdnElementsArayJsHidden = '" + this.hdnElementsArayJsHidden.ClientID + #"';
var loaderF = function getImagesData()
{
var fieldNamesList=[" + fieldNames + #"];
return KrediKartUtils.LoadImagesData('0','" + KrediKartiRow.Row.SmartPenFormNo + #"',fieldNamesList,LoadImagesDataCallBack);
};
function LoadImagesDataCallBack()
{
if(images.length > 0)
{
var numImages = 10; /*10 ar 10 ar göster*/
while(images.length > 0 && numImages-- > 0)
{
document.getElementById(elements.shift()).src =images.shift();
}
/* setTimeout(fetchImages, 1000); *//*1sn de bir*/
LoadImagesDataCallBack();
}
}
if('False' == '" + Page.IsPostBack.ToString() + #"')
{
var images=[" + imageSourceArrayJs + #"];
var elements=[" + elementsArayJs + #"];
document.getElementById(ctrlIDhdnImageSourceArrayJs).value="""+imageSourceArrayJs+#""";
document.getElementById(ctrlIDhdnElementsArayJsHidden).value="""+elementsArayJs+#""";
window.onload = loaderF;
}else{
var images=[document.getElementById(ctrlIDhdnImageSourceArrayJs).value];
var elements=[document.getElementById(ctrlIDhdnElementsArayJsHidden).value];
LoadImagesDataCallBack();
}
</script>";
To keep any value or values during a new request, you will need to include those values with the request.
This means that for a GET, you need to include the values in the querystring
?myValue=1&myArray=1,2,3,4,5
And for a POST you need to append your values to the posted data (usually by placing them inside form fields during the "onsubmit" event.
You can then either process these server side and write out their value to the page or retrieve them from the document.location using JavaScript.

Resources