I am trying to locate the element <a>.
HTML:
<html><head></head><body>
Click Here!
</body></html>
Below is the Selenium code using Xpath producing same results:
driver.findElement(By.xpath("self::node()/child::node()/child::body/child::a")).click(); //Statement1
driver.findElement(By.xpath("/child::node()/child::body/child::a")).click(); //Statement2
driver.findElement(By.xpath("child::node()/child::body/child::a")).click(); //Statement3
My understanding:
In Statement1, it mentions the initial context node as the self::node. Here, we are passing our entire HTMLDocument as the self::node(). So, our initial context node is set to entire HTMLDocument(which is also called as document root node(/)). So, our initial context node is set to document root node(/)
In Statement2, it mentions the initial context node absolutely as the document root node(/).
But in Statement3, the initial context node is not mentioned.
So, does it mean that if we do not mention the initial context node, then will it be set to document root node(/) by default ?
Please help to improve my understanding.
Your 1 and 3 statements are the same from the "context" standpoint (self and child are both xpath axes). Both have root of the document as the context (in Selenium it is called SearchContext).
The context is root in that case because you lookup elements right within a driver. If you will have a WebElement and try to look up elements inside that element your context won't be root for 1 and 3 statements.
Below is some more detailed explanation..
Assume we have test.html with the following content:
<A val="success A">
<B val="success B"/>
</A>
And the test like this:
#Test
public void test(){
driver.get("file:///path_to_page/test.hml");
WebElement a = driver.findElement(By.xpath("html/body/A")); // (1, 2)
System.out.println(a.getAttribute("val"));
try{
System.out.println(driver.findElement(By.xpath("body/A/B")).getAttribute("val")); // (3)
}catch (NoSuchElementException e){
System.out.println("body/A/B cannot be found as the context is root");
}
WebElement b = a.findElement(By.xpath("B")); // (4)
System.out.println(b.getAttribute("val"));
System.out.println(b.findElement(By.xpath("/html/body/A")).getAttribute("val")); // (5)
}
Here are few remarkable points:
Despite our file has the root A, browser automatically adds html tags to make the html file valid. So it adds html node and body node
Considering the previous point we start from accessing html/body/A which has the root context
Then we make sure that we cannot find B using the path body/A/B since the context is still root.
Then we can see that we can find B in the context of previously located A.
The final thing is that we are looking up /html/body/A within the context of B. Despite we use the B as search context we still can find the element because we start the path from / which means root and ignores any search context.
Related
I am trying to trigger a search on this site. First, I want to enter a search term, then click the search button. I am able to to do the second step, however I am unfortunately unable to access the search field. Below my attempt.
Start RSelenium
link_to_page<-"https://www.cec.ro/sucursale"
library(RSelenium)
rD <- rsDriver(browser = "firefox", port = 483L, verbose = F)
remDr <- rD[["client"]]
# Navigate to site, and wait
remDr$navigate(link_to_page)
Sys.sleep(5)
#Search for element by its id
remDr$findElement(using="css", "#edit-localitate--_jjPr3WukFY")
Error: Summary: NoSuchElement
Detail: An element could not be located on the page using the given search parameters.
class: org.openqa.selenium.NoSuchElementException
Further Details: run errorDetails method
There is apparently something wrong with the css selector. I checked, it's not nested in an iframe or so, but mabe it's related to the 'form' element it is nested in? Grateful for any hint. Many thanks.
The error is NoSuchElement which indicates an element could not be located on the page using the given search parameter
The second part of the classname i.e. _jjPr3WukFY is dynamically generated and is bound to change sooner/later. They may change next time you access the application afresh or even while next application startup. So can't be used in locators.
Solution
You need to consider any of the other attributes which is static in nature. Example:
remDr$findElement(using="css", "button[id^edit-localitate]")
or
remDr$findElement(using="xpath", "//button[starts-with(#id, 'edit-localitate')]")
When developing in the Ionic framework, the generated html sometimes will contain duplicate DOM elements, where all but one tree of elements is hidden with a class="ion-page-hidden" at the top level.
When using webdriverio to try and locate an element inside this tree, it find duplicated elements. Since the ion-page-hidden class is at the top of the tree, and not on the element itself, how can we use Xpath to locate only the displayed element.
I couldn't figure out any way to modify the XPath selector with a second condition since the elements are exactly the same!
So instead I have tried to use the webdriverio isDisplayed() function:
get openHamburgerMenu() { return Utils.visibleElement($$("//ion-button[#name='button-open-menu']")); }
where the Utils function is:
async visibleElement(elementArray) {
let returnElement;
elementArray.forEach(element => {
if (element.isDisplayed()) {
returnElement = element;
}
});
return returnElement;
}
but no elements are passed into the function. In the chrome browser, I can see two that match the xpath //ion-button[#name='button-open-menu']. I need the one not in the ion-page-hidden block!
tree
The tree looks like this:
app-for-homes[1]/ion-header/ion-toolbar/ion-buttons[1]/ion-button
app-for-homes[2]/ion-header/ion-toolbar/ion-buttons[1]/ion-button
where app-for-homes[2] happens to have the ion-page-hidden class.
I think it should be possible to use ancestors to identify which of the two elements, matching the xpath, does not have a 4th level ancestor with that class? But I'm way out of my depth on day one of working with xpaths...
Quick and Dirty
The desired outcome can be achieved using this Xpath:
//app-for-homes[1]/ion-header/ion-toolbar/ion-buttons/ion-button[#name='button-open-menu']
However, this only works where the order in which the elements appears is known.
Better Answer
When you have exactly 1 element that is not hidden, Xpaths allow you to look at an elements ancestors as far back as you want to identify the presence / or absence of the hidden class. In this case, we start by finding the ancestor app-for-homes which does not include the ion-page-hidden class:
//app-for-homes[not(contains(#class,'ion-page-hidden'))]
and then simply append the remainder of the path to the desired element. Full answer =
//app-for-homes[not(contains(#class,'ion-page-hidden'))]/ion-header/ion-toolbar/ion-buttons/ion-button[#name='button-open-menu']
I am facing an issue where I am unable to locate Element on webpage with any type of locator expect Absolute xpath. Here are details:
URL : https://semantic-ui.com/modules/dropdown.html#selection
Required Element Screen shot:
Manually created Xpath Screen shot( Please note that I am able to recognize Element in web application with manually created xpath but Same xpath is not working in selenium code)
But Same xpath is not working in selenium script.
PLEASE NOTE THAT I AM ABLE TO IDENTIFY SAME OBJECT WITH Absolute xpath
Please help to me to understand reason for this.
Here is code:
public static WebDriver driver;
public static void main(String[] args) {
driver= new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("https://semantic-ui.com/modules/dropdown.html");
//Selection
driver.findElement(By.xpath("//div[#class='ui selection dropdown upward']")).click();
driver.findElement(By.xpath("//div[#class='menu transition visible']/div[text()='Female']")).click();
System.out.println("Done");
This may be issue with your first x-path. You may try the following code. It may work.
driver.findElement(By.xpath("//div[#class='ui selection dropdown']").click();
driver.findElement(By.xpath("//div[#class='menu transition visible']/div[text()='Male']").click();
You are missing a preceding wildcard in you driver.FindElement XPath.
Instead of driver.findElement(By.xpath("//div..."));, do driver.findElement(By.xpath("//*div..."));.
Currently, what your code is doing is telling the XPath locator to find a first level div (not easily possible, as the first item is almost always the document body), but the wildcard of "*" tells it that it can look for the div at any level.
As an aside, please edit your answer up top with actual code instead of pictures so others with the same problem can find their solution easier.
Longshot:
You are using Chrome to look at the source, but Selenium is using Firefox.
There is a chance that the source is rendered differently between the two browsers. Specifically, your xpath is relying on an exact match of class. I do know that FireFox is notorious for modifying source output.
I haven't done any testing but I would not be surprised if the class is in a different order and it's failing on an exact match.
There are two solutions if that is the case:
Don't do a single exact match (xpath = "") but instead do a mix of contains joined with && (contains ui && contains selection && dropdown)
Don't rely on the output from the console tab. Instead "View Page Source" and view what is actually being sent instead of what is being interpreted by the browser
Find the dropdown container element firstly, then use it to find self descendant, like option etc.
driver.get("https://semantic-ui.com/modules/dropdown.html");
// find dropdown container element
WebElement dropdownWrapper = driver.findElement(
By.xpath("//div[#class='html']/div[input[#name='gender']]"));
// expand drop down options by click on the container element
dropdownWrapper.click();
// or click the down arrow
dropdownWrapper.findElement(By.css('i')).click();
// select option
dropdownWrapper.findElement(By.xpath(".//div[text()='Female']")).click();
To locate the element with text as Gender and select the option Female you can use the following Locator Strategy :
Code Block :
System.setProperty("webdriver.chrome.driver", "C:\\path\\to\\geckodriver.exe");
WebDriver driver = new FirefoxDriver();
driver.get("https://semantic-ui.com/modules/dropdown.html#selection");
driver.findElement(By.xpath("//div[#class='another dropdown example']//div[#class='ui dropdown selection']")).click();
driver.findElement(By.xpath("//div[#class='another dropdown example']//div[#class='ui dropdown selection active visible']//div[#class='menu transition visible']//div[#class='item' and contains(.,'Female')]")).click();
System.out.println("Gender Female selected.");
Console Output :
Gender Female selected.
This might be helpful to clearify how selectors are working in general:
.(dot): Dot at starting represents the current node. It tells us that the processing will start from the current node.
. .(double dot): It will select parent of current node. For example, //table/. . will return div element because div is the parent of table element.
‘*’: is used to select all the element nodes descending from the current node. For example:
/table/*: It will select all child elements of a table element.
//*: It will select all elements in the document.
//*[#id = ‘username’]: It will select any element in document which has an attribute named “id” with the specified value “username”.
#: It represents an attribute which selects id, name, className, etc. For example:
#id: It will select all elements that are defined with the id attribute in the document. No matter where it is defined in the document.
//img/#alt: It will select all the img elements that are defined with the #alt attribute.
//td[#*]: It will select all td elements with any attribute.
Here is a link to the article:
https://www.scientecheasy.com/2020/07/selenium-xpath-example.html/
I have a JTree that has two same-named nodes.
I am writing an RFT script that needs to click the second-named node but it will only click the first entry it finds.
enter image description here
By utilising ITestDataTree, I have managed to retrieve the ITestDataTreeNode I need but can't figure out how to click it.
Is there any way to convert an ITestDataTreeNode object into a GuiTestObject?
Thanks in advance,
Steven.
Finally found a solution.
simple as:
List list = new List();
list.append(new Index(0)); // root
list.append(new Index(0)); // suite
list.append(new Index(0)); // scenario
list.append(new Index(0)); // testcase
list.append(new Index(1)); // action
jTree().click(list); // expands second action node
I am trying to understand the difference between
dijit.byId and dojo.byId
For this i have taken a Button and a div .(To set the
data inside the div on click of the Button)
Show Me!
<div id="findMe">
Hiya!
</div>
This is working (dojo.byId)
function callMe()
{
var node = dojo.byId('findMe');
node.innerHTML = "Hello World";
}
But this isn't working (dijit.byId)
function callMe()
{
var node = dijit.byId('findMe');
node.innerHTML = "Hello World";
}
My understanding is , when refering to the div we need to use dojo.byId
and when we are refering to Individual components use dijit.byId
Please correct me if i am wrong .
As previously stated, dojo.byId retrieves the DOM node with that id if existent.
dijit.byId retrieves the instance of a dijit._Widget (and its subclasses), that is dojo's abstraction of UI objects, rather than the widget's DOM node. But it is important to note that dijit.byId searches through the widgets by the atrribute "widgetId", not "id". These are equal if you declare your widgets by passing a container node that already has an "id", but still dojo creates an attribute "widgetId" for every widget if not specified explicitly.
This means that widgetId and id are usually the same, but it is possible that they are different. Plus, widgetId is always set for a given widget even in cases where the id attribute of the container node is absent.
This implies that you should use dojo.byId whenever you intend to work on the DOM tree itself and dijit.byId only in case where you'd like to obtain the instance of a certain widget instance. If no widgets are present, there is no reason to use dijit.byId at all.
You are right:
dojo.byId finds elements in the DOM tree of your website with a certain ID - it searches and returns HTML elements.
dijit.byId finds dijits you created with a certain id - it searches and returns dijits (javascript objects), although these objects usually refer to a certain DOM node.