Generate Absolute xpath of given webElement in RSelenium [R] - r

I'm trying to replicate what was done in this answer for java selenium in RSelenium: https://stackoverflow.com/a/27611777/7837376
I'd love to be able to do something like this:
#replicating simple RSelenium process getting all //a elements
library(RSelenium)
#start remDr etc. etc.
all_a <- remDr$findElements(using='xpath','//a')
selected_a <- all_a[[10]]
Ideally then I could generate the xpath of the selected_a element using the made up function below:
#desired function
getElementXPATH(selected_a)
I understand that quite a few different XPATH's could be specified for the same element, I'm just looking for a unique xpath identifier for the element, so any unique xpath to the element will suffice!
Thanks!

Personally, I am not a big fan absolute xpath. However, you can get the absolute xpath using javascript rather having the function in your language, which will ran faster and it's easy to port.
Here is the javascript.
// this function will return the absolute xpath of any given element
jsFunction = """window.getAbsoluteXpath =function(el){
// initialize the variables
aPath ="";
// iterate until the tag name is 'HTML'
while (el.tagName!='HTML'){
// get parent node
pEle=el.parentNode;
// check if there are more than 1 nodes with the same tagname under the parent
if(pEle.querySelectorAll(el.tagName).length>1){
//now findout the index of the current child
cIndex = 0;
pEle.querySelectorAll(el.tagName).forEach(function(cEle){
cIndex= cIndex+1;
// check if iterating ChildNode is equal to current ChildNode
if(cEle === el){
// set the aPath using index
aPath = el.tagName + "[" + cIndex + "]" + "/" +aPath;
}
})
}else{
// simply add the tagName when there is only one child with the tag name
aPath = el.tagName + "/" +aPath;
}
// set parent node as current element
el=el.parentNode;
}
// append HTML to the absolute xpath generated
return "//HTML/"+aPath.substring(0,aPath.length-1);
};"""
Now you can call this method in your javascript and pass element that you are interested in getting the absolute xpath.
Let's try to get the absolute xpath of in stackoverflow.
Note: Did not tested the below code logic due to lack of environment on my machine.
# run the javascript in browser so that you can call the function anytime in your script
remDr %>% executeScript(jsFunction, args = list())
# get stackoverflow `Achievements` link element
webElem <- remDr %>% findElement("css", "a.-link.js-achievements-button")
# # get the absolute xpath of Stackoverflow `Achievements`
remDr %>% executeScript("return getAbsoluteXpath(arguments[0])", args = list(webElem))
Screenshot: Ran the javascript in chrome browser console for evidence

Please try the below logic
function absolutePath(element) {
if (element.tagName.toLowerCase() == 'html')
return '/html[1]';
if (element === document.body)
return '/html[1]/body[1]';
var ix = 0;
var siblings = element.parentNode.childNodes;
for (var i = 0; i < siblings.length; i++) {
var sibling = siblings[i];
if (sibling === element)
return absolutePath(element.parentNode) + '/' + element.tagName.toLowerCase() + '[' + (ix + 1) + ']';
if (sibling.nodeType === 1 && sibling.tagName.toLowerCase() === element.tagName.toLowerCase())
ix++;
}}
I did try the solution provided by supputuri, but it doesnt work on all cases.
Example: Try the function provided by #suppututri on https://www.amazon.in/ , and try to find the absolute xpath for
var element=document.querySelector('#nav-xshop > a:nth-child(2)');
getAbsoluteXpath(element);
The incorrect xpath shown in the console would be: "//HTML/BODY/DIV[1]/HEADER/DIV[1]/DIV[68]/DIV[9]/DIV[1]/DIV/A[1]"

Related

Karate - get URL from <a> element using Javascript

Using Karate, I need to extract the url from tag (href). I have tried this using Javascript directly in browser:
document.querySelector('a[rel="noopener"]').href
It works in browser fine, I receive whole url address ("https://XXXXXXXX"). Then I wanted to place it to Karate using:
* def emailLink =
"""
function(){
var link = document.querySelector('a[rel="noopener"]').href;
return link;
}
"""
* print emailLink()
But it does not work. The variable = "". I need to receive the href value.
Have you any Idea how to solve this?
Thank you!
The function you are writing will not work, and please read this to understand why: https://github.com/intuit/karate/tree/master/karate-core#karate-vs-the-browser
So make this change (and also note how Karate does document.querySelector() for you):
* def linkElement = locate('a[rel="noopener"]')
* def linkValue = linkElement.attribute('href')
Note that for the second line this should also work:
* def linkValue = linkElement.script('_.href')
Or even this:
* def linkValue = linkElement.property('href')
Start getting used to the Element abstraction in Karate. It can be very useful: https://github.com/intuit/karate/tree/master/karate-core#chaining
I have just been reading https://intuit.github.io/karate/ and for just JavaScript you don't need the multi-line doc string.
* def greeter = function(title, name) { return 'hello ' + title + ' ' + name }
Try without the """ on either side of the function """.

How to write by.cssContainingText() in chrome console?

I have to write by.cssContainingText() in chrome console .This is regarding protractor.
Inside the implement of by.cssContainingText(cssSelector, searchText), it does two steps:
run querySelectorAll(cssSelector) to get a element list
iterate the element list to find which one's text match the searchText
Following is implement code of cssContainingText(), you also can find it at github
functions.findByCssContainingText = function(cssSelector, searchText, using) {
using = using || document;
if (searchText.indexOf('__REGEXP__') === 0) {
var match = searchText.split('__REGEXP__')[1].match(/\/(.*)\/(.*)?/);
searchText = new RegExp(match[1], match[2] || '');
}
var elements = using.querySelectorAll(cssSelector);
var matches = [];
for (var i = 0; i < elements.length; ++i) {
var element = elements[i];
var elementText = element.textContent || element.innerText || '';
var elementMatches = searchText instanceof RegExp ?
searchText.test(elementText) :
elementText.indexOf(searchText) > -1;
if (elementMatches) {
matches.push(element);
}
}
return matches;
};
It's more complex to write equivalent in browser's console, but you can do as following:
Open stackoverflow site: https://stackoverflow.com/
execute following example code in browser console
expand the return result in console
mouse over on each item of return result in console, then you can notice the mouse will over on the corresponding element on page as following screenshot
Example code:
Array.from(document.querySelectorAll('li > a')).filter((it)=>{return (it.textContent || it.innerText || '').contains('Users')})
Screenshot of example:
This isn't a good way to approach it lets see the syntax
<ul>
<li class="pet">Dog</li>
<li class="pet">Cat</li>
</ul>
// Returns the li for the dog, but not cat.
var dog = element(by.cssContainingText('.pet', 'Dog'));
inside the bracket the first argument is the .classname and the second argument is the actual text .Better write it in script and check it .it'll work

Selenium WD | Trying to use an 'If' statement with logical 'or'

I have a step in my test that goes into several html pages and looks for an element on the screen. That element can have 2 different CSS class names while looking the same in the website (visually speaking) , I have to use an if statement with a logical 'or' to identify them
if (Status == driver.findElement(By.cssSelector(".inlineblock.redClockBigIcon.middle.isOpenExchBig-2")) || Status == driver.findElement(By.cssSelector(".inlineblock.redClockBigIcon.middle.isOpenExchBig-1")));
System.out.println("Stock is closed");)
I expected that if one of the 2 elements would appear, the Eclipse would
recognize it. Well - The second element out of the 2 appeared - and for some reason I've got an exception error. The if statement gave attention only to the first condition in the if, and ignored the second.
org.openqa.selenium.NoSuchElementException: no such element:
locate element: {"method":"css
selector","selector":".inlineblock.redClockBigIcon.middle.isOpenExchBig-2"}Unable to
How can I make the || to work in this 'if' statement?
Thanks
Screenshots of the elements
In your above logic, you have Status which is an already existing WebElement that you're comparing against another Webelement that you're looking up. I don't think this is what your intention was so I'm going to make some assumptions in a solution.
First: Find all of the elements that might exist with your desired selector (Note I'm using findElements instead of findElement)
List<WebElement> clockIconThingies = driver.findElements(By.cssSelector(".inlineblock.redClockBigIcon.middle.isOpenExchBig-2, .inlineblock.redClockBigIcon.middle.isOpenExchBig-1"));
Second: Check if that found anything
if(clockIconThingies.size() > 0)
{
System.out.println("Stock is closed");
}
Alternatively for your css selector, from the image it looks like you might not need to do an or at all and just look for the class redClockBigIcon like this:
List<WebElement> clockIconThingies = driver.findElements(By.cssSelector(".redClockBigIcon"));
You can try using try catch block:
boolean isFirstElemPresent = true;
try{
driver.findElement(By.cssSelector(".inlineblock.redClockBigIcon.middle.isOpenExchBig-1"));
}catch (NoSuchElementException e){
isFirstElemPresent = false;
}
if(isFirstElemPresent == false)
driver.findElement(By.cssSelector(".inlineblock.redClockBigIcon.middle.isOpenExchBig-2"));
OrTo avoid try catch block, use below code snap:
List<WebElement> elements = driver.findElements(By.className("redClockBigIcon"));
if (elements.size() == 0) {
System.out.println("Element is not present");
} else {
System.out.println("Element is present");
}

Very complicated XQuery transformation

I have a very complex XQuery to write (at least by my standards).
Here is my input xml:
<testRequest>
<request1>
<Line>1</Line>
<action>addtoName</action>
</request1>
<request2>
<Line>2</Line>
<action>addtoSpace</action>
</request2>
<request3>
<Line>3<Line>
<action>addtospace</action>
</request3>
</testRequest>
In my output xml, the actions should be attached as attributes to the "request1" elements. So, based on the action element under a request1 element, the attribute for the request1 element should be one of the following:
if action = IgnoreCase(addtoName), the request1 element should be <request1 action=insertingname>
if action = IgnoreCase(addtoSpace), the request1 element should be <request1 action=updatingspace>
Not only this, but also, I need to add an attribute to the element, based on the action values underneath it.
So, I have to traverse each of the elements under a element and see if any of the elements are equal to "addtospace" if yes, then I need to get the corresponding values of the elements and make up the attribute for the element. From the above xml, my attribute for the element should be,
<testRequest lineFiller="Line Like 2_* AND Line Like 3_*>, where 2 and 3 are the respective line numbers.
and if there are no elements with element= addtoSpace, then the attribute for the element should be "changed".
So, in summary, my transformed xml should look like this:
<testRequest lineFiller="Line Like 2_* AND Line Like 3_*>
<request1 action=insertingname>
<Line>1</Line>
<action>addtoName</action>
</request1>
<request2 action=updatingspace>
<Line>2</Line>
<action>addtoSpace</action>
</request2>
<request3 action=updatingspace>
<Line>3<Line>
<action>addtospace</action>
</request3>
</testRequest>
Any help to accomplish this humungous task will be greatly appreciated.
thanks!!!
You should define functions to generate the attributes that you need to add to your elements.
For adding to the "request" element, this should work:
declare function local:getaction($x) {
if (lower-case($x/action) = "addtoname") then attribute action {"insertingspace"} else
if (lower-case($x/action) = "addtospace") then attribute action {"updatingspace"} else
()
};
The linefiller attribute can be created similarly:
declare function local:getfiller($x) {
attribute lineFiller {
if ($x/*[lower-case(action) = "addtospace"]) then
string-join(
for $r in $x/*[lower-case(action) = "addtospace"]
return concat("Line Like ",$r/Line,"_*")
, " AND ")
else "change"
}
};
Then to put it all together, fun a simple for loop over your original document, adding in the attributes where needed:
let $doc:=<<your original document>>
return
<testRequest>
{ local:getfiller($doc) }
{ for $r in $doc/* return
element { name($r) } {
local:getaction($r),
$r/*
}
}
</testRequest>
EDIT: enhanced getfiller function to return "change" if there are no actions

How to get full category path from a keyword in Tridion

Can any one help me to get full category path from a given keyword. I am giving one example as below,
Example:
Category 1----> Keyword 1 -----> Keyword 11,
say from metadata i got the value "Keyword 11", but i need whole path i.e. /Category 1/ Keyword 1/Keyword 11.
Can anyone help me how to achieve this in Template Building Block using c#.
Maybe you can try and play with one of the following:
keyword.ParentKeywords recursively to create the path you are looking for.
OrganizationalItem oi = keyword.OrganizationalItem; // to get all the organizational items
keyword.OwningRepository
Hope that helps!
Below code should help you to get the path.
bool isRecursive = false;
KeywordField kwdField = (KeywordField)metaFields["kwdField"];
Keyword curKwd = new Keyword(kwdField.Value.Id, engine.GetSession());
string kwdPath = curKwd.Title;
while (!isRecursive) {
if (curKwd.ParentKeywords.Count > 0){
foreach (Keyword kwd in curKwd.ParentKeywords) {
kwdPath = kwd.Title + "/" + kwdPath;
}
curKwd = curKwd.ParentKeywords[0];
} else {
isRecursive = true;
}
}
kwdPath = curKwd.OrganizationalItem.Title + "/" + kwdPath;

Resources