xpath on the site is constantly changing - selenium

Peace. I registered a test on the amazon site. Doing a search of 11 iphone and then coming to a page of full products i choose first but its xpath
// span [contains (text (), 'Apple iPhone 11 (64GB) - Black')]
The problem is that I can use this xpath but tomorrow the xpath will be renamed because the first product is changed for example:
// span [contains (text (), 'Apple iPhone 11 Pro (64GB) - Space Gray')]
But I always choose the first product among all iphones even when the product changes?
Thanks.
This is the page
https://www.amazon.co.uk/s?k=iphone+11&crid=3GCCCW0Q2Z1MQ&sprefix=iph%2Caps%2C220&ref=nb_sb_noss_2

Use index and following xpath to get the first element.
(//a[#class='a-link-normal a-text-normal']/span)[1]

Always try to find something on the page that is very unlikely to change. If the element that you're looking for doesn't have such properties, look at it's ancestors.
For example, in this case, you can see that one of this span's ancestors have cel_widget_id="MAIN-SEARCH_RESULTS" which'll most likely remain constant. So, the following xpath:
//span[#cel_widget_id="MAIN-SEARCH_RESULTS"]//h2/a/span
will give you all such titles. You can get the first index as
(//span[#cel_widget_id="MAIN-SEARCH_RESULTS"]//h2/a/span)[1]

You could use the class of the search item span:
//span[#class="a-size-medium a-color-base a-text-normal"]
Then if you can do:
first_iphone = driver.find_element_by_xpath('//span[#class="a-size-medium a-color-base a-text-normal"]')
Although all search items are all the same class, (in this case a-size-medium a-color-base a-text-normal) the find_element_by_xpath method will only look for the first one.

Related

Identifying an element from a group, span[i] is the differentiating factor

I have added the screenshot I have a group of elements that have the exact same xpath except the span tag.I want to identify the individual input fields, but unable to.
I have tried using contains, with class but unable to attach span to the xpath
Here is what the HTML looks like:
/html/body/div[#id='app']/div/div[#class='LayoutModify_LayoutModify_1Akxb']/main[#class='LayoutModify_main_5aBy3']/section[#class='sub-detail inner ProductDetail_productdetail_bJWN2']/div[#class='ProductDetail_productsphere_kgNGm']/div[#class='ProductDetail_threecol_2zA1n ProductDetail_productsphereleft_2pLZT']/span[4]/div[#class='el-input el-input--medium ProductDetail_productsphereinput_3eVZg']/input[#class='el-input__inner']
/html/body/div[#id='app']/div/div[#class='LayoutModify_LayoutModify_1Akxb']/main[#class='LayoutModify_main_5aBy3']/section[#class='sub-detail inner ProductDetail_productdetail_bJWN2']/div[#class='ProductDetail_productsphere_kgNGm']/div[#class='ProductDetail_threecol_2zA1n ProductDetail_productsphereright_3BrqC']/span[4]/div[#class='el-input el-input--medium ProductDetail_productsphereinput_3eVZg']/input[#class='el-input__inner']
Notice the span[4] and span[15] are the only differences
Quick question:
do either of these locators:
locator A
//span[4]/div/input[#class='el-input__inner']
locator B
//span[15]/div/input[#class='el-input__inner']
find any input on Your page?
If not - could You please post here the whole HTML page code here please?
Here is the xpath that worked:
//div[contains(#class,'ProductDetail_productsphereright')]/span[4]/div/input

Not able to locate webelement

I am not able locate a webelement, this web application opens in Internet explorer only and I have used all the possible ways to click but no luck.
Xpath locators that I have tried :
"//form[#id='Form1']//a[contains(text(),'Age Range')]"
and
"//form[#id='Form1']//a[#id='rptTables1_ctl07_hlTablename1']"
also I have tried click on the element using action class and javascript as well.
Attached DOM in the URL, please have a look here
In the node a, id value is not static so you can't locate that element using the id value but you can use partial id value for example, looks like rptTables1_ is unique in id value and the remaining part is changing so applying contains() on this may works.
Try the below xpath if there is only match :
//a[contains(#id, 'rptTables1_')]
Try the below xpath by providing the matching index if there are multiple xpath matches :
(//a[contains(#id, 'rptTables1_')])[Matching index number]
for example if the matching index is 3 then you can write like this (//a[contains(#id, 'rptTables1_')])[3].
Or you can use the Advanced Performance Parameters Panel Topics text to identify that element.
//a[contains(text(), 'Advanced Performance Parameters Panel Topics')]
Again if there are multiple matches then try to use indexing method as mentioned above.
Or you can try the below modified your xpaths :
//form[#id='Form1']//a[contains(#id, 'rptTables1_')]
or
(//form[#id='Form1']//a[contains(#id, 'rptTables1_')])[Matching index number]
or
//form[#id='Form1']//a[contains(text(), 'Advanced Performance Parameters Panel Topics')]
I hope it helps...
I haven't tried any automation in IE at all but in firefox, sometimes I encounter those issues and my work around other than the .click() function is send_keys(Keys.RETURN). Also, i'm using time.sleep(x) before clicking or sending keys to make sure the element has been loaded.

Concise Xpath to simulate finding an element regardless of page structure? (selenium)

If you're visually looking at a webpage and there is something clickable and unique on the page, you'll just click it. Without thinking about the page structure.
I'm interested to see what the most concise xpath is that could be constructed to simulate this in a versatile manner.
For example, target the "I'm feeling Lucky" button on the Google homepage:
//*[contains(#*, 'Lucky')]
The above works. But would fail in the element contained Lucky as inner text, or if the wrong case was specified. As such, our xpath needs to cater for any sensitivity and also look for the given string matching inner-text as well.
How could the above xpath be expressed in the most concise yet encompassing structure?
There is nothing thats very generic and executing such xpaths could be costly also at times.
"//*[contains(#*, 'Lucky')] | //*[contains(text(), 'Lucky')]"
Above is one xpath you can combine to get some results. You start specifying which nodes you don't to examine or ones which you want to examine
"//*[contains(#*, 'Lucky')] | //*[contains(text(), 'Lucky')][not(self::script|self::td)]"
And you can keep improving it
It's not possible to create a versatile XPath to accurately/reliability locate an element by text.
Why?
Because the text evaluated by an XPath is not necessary rendered in the page.
Because there's a hight chance to end-up with multiple matches since each ancestor also contains the expected text.
But mainly because there's too many rules/specific cases to consider.
But if I had to create one, then I'd start with this one:
"(html/body//*[not(self::script or self::style)][contains(concat(#value, normalize-space()), 'MyText')])[last()]"
Get all the descendants of the <body>
html/body//*
except <script> and <style>
[not(self::script or self::style)]
where the value attribute or normalize html contains 'MyText'
[contains(concat(#value, normalize-space()), 'MyText')]
then returns the last and deepest match
[last()]

How to click an element with reference to another web element in Selenium WebDriver(Java)?

There are many span tags as mentioned in the image below and each has its own a-tag with unique id as "chooseitem". I need to choose particular a tag using names in the span tags.
Need to click the a-tag button using the text Mayo Chicken from the above HTML snippet in the image.
I have tried the below Selenium script
WebElement select = driver.findElement(By.xpath("//*[contains(text(),'Mayo Chicken (Single)')]"));
WebElement add = select.findElement(By.id("chooseitem"));
It doesn't work for me.
driver.findElement(By.id("chooseitem"));
The above code chooses the first item in the page by default as its id is also 'chooseitem', but need to define what to be chosen.
Can anybody help me out?
We need to get the common parent(ancestor) element of the chicked and the clickable 'a' tag, then we can navigate to the tag 'a'. Below xpath should ideally work.
"//span[contains(text(),'Mayo chicken')]/ancestor::div[4]//a"
Note: Here i have used div[4] because fourth parent is the common ancestor for 'Mayo chicken' and tag 'a'.
For more details about different xpath axis refer this->https://www.w3schools.com/xml/xpath_axes.asp
Hope this helps you. thanks.
You can do that by using the xpath position, press F12 for developer tools click on "Select element button", click the element that interests you on the page, as in your picture you will see one or more lines highlighted, right click the line -> Copy -> Copy xpath. You will have something like the line below:
//*[#id="comment-76500216"]/td[2]/div/span[1]
The xpath position will be:
//td[2]/div/span[1]
You can use that when you have multiple elements that share the name or id or so on.
And you will have:
WebElement select = driver.findElement(By.xpath("//td[2]/div/span[1]"));
PS: I used google chrome

Selenium is not identifying the 'iframe'

Below in my code
driver.switchTo().frame(driver.findElement(By.id("file")));
Above line is not running
There is various way to switch to frame. Please share the HTML code if you need a exact code to switch to your respective application. you can try with below method. Better use index if your frame do not any name etc
driver.switchTo().frame(index)
replace index with 0 first and if it not work then try with 1 and then 2 etc one by one
More details
driver.switchTo().frame() has multiple overloads.
driver.switchTo().frame(name or id)
Here your iframe doesn't have id or name, so not for you.
driver.switchTo().frame(index)
This is the last option to choose, because using index is not stable enough as you could imagine. If this is your only iframe in the page, try
driver.switchTo().frame(0)
driver.switchTo().frame(iframe_element)
The most common one. You locate your iframe like other elements, then pass it into the method.
Here locating it by title attributes seems to be the best.
driver.switchTo().frame(driver.findElement(By.cssSelector("iframe[title='Fill Quote']")));
// driver.switchTo().frame(driver.findElement(By.xpath(".//iframe[#title='Fill Quote']")));
most probably, your iframe is not visible or your window is not active.
in Python:
driver.switch_to_default_content()
wait.until(EC.frame_to_be_available_and_switch_to_it("yourFrame"))
in Java:
driver.switchTo().defaultContent();
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.tagName("yourFrame")));