Using an OR condition in Xpath to identify the same element - selenium

I have this logic which gets the current page's title first clicks on next button, fetches the title again and if both the titles are the same, meaning navigation has not moved to the next page, it clicks on Next again.
However, my problem is that the title element's Xpath differs - the same title element has two Xpaths. One is some pages the other in some other pages.
It is either this,
(.//span[#class='g-title'])[2]
OR
.//span[#class='g-title']
So, how can I handle this?

If the element has two xpath, then you can write two xpaths like below
xpath1 | xpath2
Eg: //input[#name="username"] | //input[#id="wm_login-username"]
It will choose any one xpath

You can use or operator like
(.//span[#class='g-title'])[2] or .//span[#class='g-title']
For more details : Two conditions using OR in XPATH

You can use or, like below:
//tr[#class='alternateRow' or #class='itemRow']

I had an interesting insight I had using this method. In my python code I was clicking a cart button and the or "|" ONLY works with separate xpath statements like so ...
WebDriverWait(webdriver,20).until(EC.visibility_of_element_located((By.XPATH,"//*[#class='buttoncount-1'] | //button[contains(text(), 'Add to Cart')]")))
or
btn = webdriver.find_element_by_xpath("//*[#class='buttoncount-1'] | //button[contains(text(), 'Add to Cart')]")
I found that "or" ONLY works when they share the same bracket []
WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.XPATH,"//button[contains(text(), 'Add to Cart') or contains(text(),'Buy')]"))).click()
And since you're here. If you're curious about "and" statements this worked for me...
WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.XPATH,"//button[contains(text(), 'Continue To Order Rev')][#data-attr='continueToOrderReviewBtn']"))).click()
Simply pairing two separate statements was sufficient. No "and" necessary.
Note: This was all in python. I do not know how this will transfer over to java. Hope this was somewhat useful. It took me a few migraines to narrow this down.

Related

Selenium find element next to another one

I need to find an element that is located next to another one depending to an if condition.
For example, I'm trying to retrieve the bottom with the word “Log In & Pay” only if I found the words ‘DANA’ before.
I can find the first element with text DANA in this way, but how can I find then the next botton element with the text “Log In & Pay” ?
driver.findElement(By.xpath ("//*[contains(text(), 'DANA')]"));
Below the Html page:
Get the span with the desired text, find the closest ancestor div which contains both els, find the el you want from there. i.e.
//span[contains(text(), 'DANA')]
//ancestor::div[#class='web-pay-wallet-inside-wrap']
//div[#class='action']
/div[contains(text()='Log In & Pay')]
try using nested predicates
//div[span[contains(text(), 'DANA')]]/following-sibling::div[#class='action']/div
Explanation
//div[span[contains(text(), 'DANA')]] finds the div which contains span with text DANA
following-sibling::div finds the following div at the same level
Selenium 4 introduces relative locators which allow to look up elements in relative position to others. Like:
above
below
right of
left of
and even "near"
You can find examples here.

Can t find the xpath for Following button instagram for selenium

I try to get the xpath for the following button on instagram making an automate unfollowing soft. enter image description here
I found it just like this:
driver.find_element_by_xpath('//div[#class="qF0y9 Igw0E rBNOH YBx95 ybXk5 _4EzTm soMvl "]').click()
But i want to itterate over all ,,Following" Buttons , but like this is stuck at the first one!
This is my Code:
fBody = driver.find_element_by_xpath("//div[#class='isgrP']")
for i in range(1, 1500):
driver.find_element_by_xpath('//div[#class=" qF0y9 Igw0E rBNOH YBx95 ybXk5 _4EzTm soMvl "]').click()
#driver.find_element_by_xpath("//*[text()='Following']").click()
print("Am apasat follow")
sleep(5)
driver.find_element_by_xpath('//button[#class="aOOlW -Cab_ "]').click()
sleep(5)
driver.execute_script('arguments[0].scrollTop = arguments[0].scrollTop + arguments[0].offsetHeight;', fBody)
print("Ma bag la somn 1 min")
sleep(2)
print("salut")
Selenium does Not "like" empty or white spaces in the attributes.
I suggest using a CSS selector and using *= in order to find text contains:
driver.find_element_by_CSS('//div[class*="qF0y9"][class*="Igw0E"]').click();
Avoid using white or empty spaces and, underscores (_) and hyphens (-) for the element's attributes.
I think the classes on the elements change as yours do not match with mine. Here is a more generic XPath that matches the "following" button.
//div//button[div[text()='Following']
When using this in a test I found it instantly failing unless I surrounded it with an explicit wait condition.
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div//button[div[text()='Following']"))).click();
Ill post my example when Instagram stops giving me connectivity issues.

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()]

Capybara, selecting 1st option from dropdown?

I've done a search and most of the related google results have returned just in general selecting an element from a dropdown. However the ID's in this case for the elements in the dropdown are dynamically generated unfortunately.
This is for a base test case, so I basically just need to select for example the first one. The text is also the same for the elements in the dropdown (not sure if that helps).
Is there such an example of this?
Im using cucumber with caybara(using selenium driver) integration
You can find the first option element and then use the select_option method to select it.
For example, if the select list has an id "select_id", you can do:
first('#select_id option').select_option
As #TomWalpole mentions, this will not wait for the element to appear. It would be safer to do one of the following:
first('#select_id option', minimum: 1).select_option
or
find('#select_id option:first-of-type').select_option
Alternatively you can get the first element text then select it by select function:
first_element = find("#id_of_dropdown > option:nth-child(1)").text
select(first_element, :from => "id_of_dropdown")
After two days of searching and reading, this article was amongst one of a few that was helpful. Hopefully, this can help someone else!
I created a few methods like so, excuse the naming..I changed it.
def some_dropdown(id, text)
dropdown = find(id).click
dropdown.first('option', text: text).select_option
end
def select_form
within 'content#id' do
some_dropdown('#id', text)
click_link_or_button 'Submit'
end
end
I also referenced this.
I've tried to select an option from a modal dropdown. After trying all listed methods, and many other from other threads - I totally gave up and instead of using clicks or select_option just used keyboard keys
find(:select, "funding").send_keys :enter, :down, :enter
In case it still complains - try:
find(:select, "funding", visible: false).send_keys :enter, :down, :enter
Worked like a charm, selecting first option from a dropdown.

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")));