Difference between contains and equals in XPath - selenium - selenium

Hey All I gave a question regarding xpath locator in selenium.
I have a test that if I use the next code:
locator = By.xpath("//div[#class='ant-notification-notice ant-notification-notice-closable ng-trigger ng-trigger-notificationMotion']");`
everything is working since I user "class="
However if I change it and use contains :
locator = By.xpath("//div[contains(#class, 'ant-notification-notice ant-notification-notice-closable ng-trigger ng-trigger-notificationMotion')]");
I get
ERROR: no such element: Unable to locate element: {"method":"xpath","selector":"//div[contains(#class, 'ant-notification-notice ant-notification-notice-closable ng-trigger ng-trigger-notificationMotion')]"}
I do not understand what is the difference, and why If I use contains it not find.

The xpath as:
//div[#class='ant-notification-notice ant-notification-notice-closable ng-trigger ng-trigger-notificationMotion']
is checking #class attribute values lexically for the string:
ant-notification-notice ant-notification-notice-closable ng-trigger ng-trigger-notificationMotion
Where as xpath as:
//div[contains(#class, 'ant-notification-notice ant-notification-notice-closable ng-trigger ng-trigger-notificationMotion')]
is checking the substring ant-notification-notice ant-notification-notice-closable ng-trigger ng-trigger-notificationMotion within the #class attribute value.
I feel it would be a better idea to restrict the #class attributes as follows:
//div[contains(#class, 'ant-notification-notice') and contains(#class, 'ant-notification-notice-closable')][contains(#class, 'ng-trigger') and contains(#class, 'ng-trigger-notificationMotion')]
You can find a relevant discussion in Why does google-chrome-devtools identifies less number of elements through XPath then number of elements identified through CssSelector

Related

Choose the correct element from the list of objects with the same className

Quick one, i am trying to avoid using xpath and using css selectors due to performance issues xpath can have so i would like to know the right approach of locating for example "A" in the list
<div class="input-search-suggests" xpath="1">
<div class="input-search-suggests-item">A</div>
<div class="input-search-suggests-item">B</div>
<div class="input-search-suggests-item">C</div>
</div>
Currently i am locating A using xpath / span but it would be indeed sufficient locating all elements and then grabbing A from the list that have same class which is "input-search-suggests-item"
#FindBy(xpath = "//span[contains(text(),'A')]")
CSS_SELECTOR does not have support for direct text what xpath has.
What this means is, for the below xpath
xpath = "//span[contains(text(),'A')]"
based on text A you can not write a css selector.
Instead to locate A using css selector, you can do :
div.input-search-suggests > div.input-search-suggests-item
In Selenium something like this :
#FindBy(cssSelector= "div.input-search-suggests > div.input-search-suggests-item")
Even though it will have 3 matching nodes, but findElement will take the first web element.
Also you may wanna look at nth-child(n)
div.input-search-suggests > nth-child(1)
to make use of index to locate A, B, C
Here is the Reference Link

XPath using classname and contains text

I was looking for an answer to how to find an element that has a class and contains text.
I've got two answers.
//div[#class='credit_summary_item' and contains(text(),'Professor']:
as in HTML XPath Searching by class and text
//div[contains(#class, 'credit_summary_item') and contains(., 'Professor')]:
as in XPath to match #class value and element value?
For me, only 2nd answer worked.
Can anyone pls explain the difference for 'contains text' part.? As both answers don't mention it.
For a demonstration consider the following HTML:
<div class="credit_summary_item">Professor</div>
There is:
Only one value of the class attribute i.e. credit_summary_item.
And the innerText i.e. Professor contains no leading and trailing spaces.
So, to locate this element you can use either of the following solutions:
Using text():
//div[#class='credit_summary_item' and text()='Professor']
Using contains():
//div[#class='credit_summary_item' and contains(., 'Professor')]
This usecase
But in your usecase it seems contains(#class, 'credit_summary_item') worked which implies the element have multiple classes. So apart from credit_summary_item there are some other values present as class attributes.
In my case the html looked like this :
<ki5-tab text="Super Boal" ki5-tab="" slot="default-1" selected="true"></ki5-tab>
xpath :
//ki5-tabcontainer/ki5-tab[contains(#text,'Boal')]

How to handle multiple xpath for same locator using Selenium?

How to handle multiple xpath for same locator using Selenium, i.e if one is failed use another locator for same field before failing script.
To start with each WebElement within the DOM Tree can be uniquely identified using any of the available Locator Strategies.
However, you can construct multiple xpath for the same element using permutation and combination of the available attributes and their values. As an example, for the element below:
<div class="_2S1VP copyable-text selectable-text" data-tab="1" dir="ltr" spellcheck="true" contenteditable="true"></div>
You can construct multiple xpaths as follows:
"//div[contains(#class, 'copyable-text')]"
"//div[contains(#class, 'copyable-text') and #data-tab='1']"
"//div[contains(#class, 'copyable-text') and #data-tab='1'][#dir='ltr']
"//div[contains(#class, 'copyable-text') and #data-tab='1'][#dir='ltr' and #spellcheck='true']"
"//div[contains(#class, 'copyable-text') and #data-tab='1'][#contenteditable='true']"
All these xpaths would identify the same element. But what matters most is the xpath should be able to identify the desired element uniquely. The responsibility of constructing the optimized xpath is solely on the test creator.
Use OR expression for the same. You can pass multiple attribute of the same WebElement.
For example:
Xpath=//*[#type='submit' or #name='btnReset']

Is it okay to use such xpath to find web elements?

Consider this xpath which should always return one element.
//div[#id='MyDiv123']/div[contains(#class, 'super')]
Assume that we won't add anymore divs whose class is super. Given that info, I don't think that it is a good idea to use /div[contains(#class, 'super')]because the xpath will break if div[contains(#class, 'super')] is placed inside another element.
Shouldn't we be using //div[contains(#class, 'super')] instead ?
I don't like using XPaths for locators that can be written as a CSS selector. I think it's much simpler as
#MyDiv123 > div.super
or just
div.super
if it's unique on the page.
XPath contains() is a string match. All the elements below will match your XPath locator but none of them will match the CSS selectors above.
<div class="super-duper" ...>
<div class="superior" ...>
<div class="abcsuperdef" ...>
... you get the idea...
There is no defined Best Practices while writing xpaths. It all boils down to how effective xpath can be written.
I don't see any issue with the xpath as :
//div[#id='MyDiv123']/div[contains(#class, 'super')]
Of-coarse there ca be some improvements as follows :
As an enduser you won't be sure how the class attribute super impacts the HTML or which elements have this attribute. So in that case to identify the WebElement uniquely it would be wise to include the ancestor <div> tag with id as MyDiv123.
But it doesn't looks like the classname super can be dynamic. Hence you can avoid the keyword contains within the xpath and rewrite it as :
//div[#id='MyDiv123']/div[#class='super']

Selenium WebDriver Xpath current element attribute reports invalid Xpath exception

I have html like
<html>
<body>
<div class='open'>
<h1>Title</h1>
<div>Opened</div>
</div>
</body>
</html>
And in my Selenium WebDriver 3 tests I am trying to select the div.open element using the following xpath:
//h1/../.[contains(#class, 'open')]
In the following command in c#:
driver.FindElement(By.XPath("//h1/../.[contains(#class, 'open')]"));
Which results in
OpenQA.Selenium.InvalidSelectorException : invalid selector:
Unable to locate an element with the xpath expression //h1/../.[contains(#class, 'open')] because of the following error::
Failed to execute 'evaluate' on 'Document': The string '//h1/../.[contains(#class, 'open')]' is not a valid XPath expression.
Searching by the same Xpath in Firefox console successfully locates the element.
Any ideas why WebDriver considers this xpath invalid?
Note: my example is of course simplified
I would say that's probably down to the specification doesn't explicitly state whether predicate after abbreviated step i.e . and .. should be allowed, neither the specification ever mention an example involving predicate after abbreviated step.
So some XPath implementations just don't support it. One of these, I noticed, is XPath implementation in .NET framework. Other implementations explicitly disallow it, and even provide useful suggestion for the user to replace . or .. in their XPath with the equivalent unabbreviated expression, in order, self::node() or parent::node(), for example, the implementation used by xpathtester. But as you noticed, other implementations might support it.
As of workaround, there are many alternatives XPath which equivalent or close enough to you original attempted XPath. The easiest is to expand the . to its unabbreviated expression, as I mentioned above. Personally, I prefer not to go down the tree and then back up to return a parent element, if possible. Instead, just stop at the parent element which we want to return, and check for the child in predicate :
//*[contains(#class, 'open')][h1]
If you want to reference "div" with respect to "h1" tag then you can change the xpath to following:
//h1/ancestor::div[contains(#class, 'open')]
Instead of //h1/../.[contains(#class, 'open')] xpath can you try //div[#class='open'] xpath