How to iterate with more than one element on the page - ruby

I have several buttons to click on the same page. How do I iterate and click on each of them?
def btnConectar()
elements = all("button[data-control-name='srp_profile_actions']").count
puts elements
first("button[data-control-name='srp_profile_actions']").click
find("section[class=modal]")
find("button[class='button-primary-large ml1']").click
end

all returns an Array like Capybara::Result object. You can iterate through that using the standard ruby enumerable methods.
all("button[data-control-name='srp_profile_actions']").each do |el|
el.click
find("section[class=modal]") # Not sure what this is for - if it's an expectation/assertion it should be written as such
click_button(class: %w(button-primary-large ml1)
end
That will work as long as clicking on the button doesn't cause the browser to move to another page.
If clicking does cause the browser to move to another page then all the rest of the elements in the Capybara::Result object will become stale (resulting in a stale element reference error on the next iteration) and you won't be able to iterate any more. If that is your case then details on what exactly you're doing will be necessary. Questions like does the original button still exist on the page after clicking the button-primary-large button, or can you iterate by just clicking the first matching button over and over? If it does still exist is it changed in any way to indicate it's already been clicked, or is the number/order of buttons on the page guaranteed to be stable? It would probably help to understand if you posted a fragment of the HTML for the first and second iteration.

def btnConectar()
page.all("button[data-control-name='srp_profile_actions']").each do |el|
while page.has_css?("button[data-control-name='srp_profile_actions']")
el.click #Click the button
find("section[class=modal]") #Modal mapping
click_button(class: %w(button-primary-large ml1)) #Click the button
sleep 3
end
end
end

Related

watir element click intercepted:

I'm trying to click on a button that contains the words "Add To Cart" using ruby/watir.
If I use
cart = browser.link(text: 'Add To Cart')
if cart.present?
cart.click
It works (item gets added to cart) but I get a element click intercepted: Element ... is not clickable at point (441, 20). Other element would receive the click: (Selenium::WebDriver::Error::ElementClickInterceptedError
I'm new to ruby/watir so any help would be greatly appreciated.
Try this code whether it works. If it's not working then as you have stated, even after the error, it works, So I assume we have to issue the click on the element which is overlaying the target element. So Paste the entire error message. The error message must contain the element which is actually obscuring.
require 'watir'
b = Watir::Browser.new
cart = browser.link(text: 'Add To Cart')
if cart.present?
b.wait_while { cart.obscured? }
cart.click
end
Update
Okay, there are other two ways you have to try.
One is to use JavaScript.
cart.click!
Or click the element which is overlaying your element.From your error the element is division with class attribute 'row'. If you have more detail about that element, you can locate that element by yourself. But now, with the given detail, I form the locator.
b.div(class: 'row').click
there are few ways you can figure what is going on
try looking around the element
check if watir sees more than one link, this should produce 1
browser.as(text: 'Add To Cart').count
then try to click every element if it has more than 1
browser.as(text: 'Add To Cart')[0].click
browser.as(text: 'Add To Cart')[1].click
or try to click its parent, link can be behind overlay
browser.a(text: 'Add To Cart').parent.click
then check if same can be acomplished by clicking parent element directly
browser.div(class: 'wishlist_cart_button').click
or you can try with different approach, find element by some other attribute
browser.a(href: /setCurrentId/).click
also maybe there is a parent element with id so you can find the cart button easier
browser.element(id: "asdf").a(text: "Add To Cart").click
browser.element(id: "asdf").as.last.click
if everything above fails, then click it with selenium, since watir is worried about click being intercepted
browser.driver.action.move_to(browser.a(text: 'Add To Cart').wd).click.perform
hope something helps :D

How can I iterate through an array of links and check information on the page to which each refers?

I have a page that lists job openings at my company. I want to use Capybara tests to click on each of the links and determine if the target page contains certain content.
I have the step below. The next step in the process checks for the content on the new page.
When("I click on each job title") do
page.all('.job-box').each do |item|
within(item) do
find('a').click
end
end
end
Once the first link is clicked the browser goes to a different page. At that point, I would like to check the content on that page, then return to the previous step to check the next link. What actually happens is that the code clicks the first link, and then attempts to click the second link. Since the first link sent it to another page, the second link is no longer present, so it returns a stale element error.
Obviously, I need a way to deal with multiple pages before continuing with the next step in the iteration, but I haven't found any documentation that addresses that problem.
You probably want to use page.go_back (or page.driver.go_back depending on the driver you're using). Something like this:
When("I click on each job title") do
page.all('.job-box').each do |item|
within(item) do
find('a').click
# do stuff
end
# roll back (granting you went but one click away)
page.go_back
end
end
As you've recognized the issue here is that the elements are stale when you return to the previous page. Depending on what limitations you're willing to accept there are multiple ways to deal with this. The bigger issue you have is that you're doing this in Cucumber which really doesn't have support for scoping/looping in its test structure. If you're willing for this to only work on a specific browser/platform you can use the respective key modifiers on click to have the browser open a new tab for each job entry and then your next step could run through all the open tabs checking those
When("I click on each job title") do
page.all('.job-box').each do |item|
item.find('a').click(:alt, :cmd) # alt,cmd click in Chrome MacOS opens in new tab
end
end
But really you're going to be better off doing something like
When("I click on each job title and verify info") do
page.all('.job-box').each do |item|
window = page.window_opened_by do
item.find('a').click(:alt, :cmd)
end
page.within_window(window) do
# verify whatever needs to be verified
end
window.close
end
end
Thomas Walpole's answer got me close enough to figure it out. Here is what ended up working:
When("I click on each job title and verify info") do
page.all('.job-box').each do |item|
new_window = window_opened_by do
url = item.find('a')[:href]
within_window open_new_window do
visit url
end
end
page.within_window(new_window) do
# do stuff
end
end
end
It doesn't close each new tab after it checks it, which could be a problem if you are working with a lot of links. I only have about 15, so it wasn't an issue for me. They all closed when the browser closed after the test finished.

Handling Element is not clickable at point (x, y). Other element would receive the click: With Capybara, ruby, Webdriver

I am writing a UI test that goes to a list of articles and selects the load_more button until this is no longer available (because all articles have loaded). The problem is the button has been hidden at this point rather than removed and is returning this error
Selenium::WebDriver::Error::UnknownError: unknown error: Element <div
class="">...</div> is not clickable at point (x, y). Other element
would receive the click: <div class="" id="" data-module-name="">...
</div>
(Session info: chrome=65.0.3325.181)
(Driver info: chromedriver=2.36.540469
ruby-2.5.1/gems/selenium-webdriver
3.11.0/lib/selenium/webdriver/remote/response.rb:69:in `assert_ok'
I have tried a few loop types but for this, if we assume I'm using
while world.page.has_load_more?
world.page.load_more.click
break if world.page.has_load_more? == false
end
The question is how do I tell Selenium this is ok and not to error, so I can continue with the next step in my test case(cucumber). I realise that the choice of loop type may also be incorrect, so feel free to suggest changes there as well.
Firstly, as Rajagopalan has pointed out, the error that you're getting is not that your element is not visible (as in hidden by CSS) but that it is overlapped by another element. You can call save_and_open_screenshot to see what element is overlapping the button (possibly a fixed footer, etc?). Make sure you've set the window size large enough so you don't have elements unintentionally overlapping and make sure you're running the latest version of Capybara since it attempts to deal with overlapped elements in selenium by scrolling them into view if possible.
Secondly, the loop in your question doesn't make a lot of sense having both the while and break since they check the same thing. Also, assuming the "load more" button is loading and appending to the page via AJAX, you probably need to wait for the new elements to load before checking if the button exists (click does not wait for actions to complete since it has no idea what actions could/would be triggered). That would then become a structure something like
while page.has_button?('Load More')
# Get the current number of visible articles
article_count = page.all('div.article').count # Use whatever selector would match an article in the list
# click the button
page.click_button('Load More')
# ensure more articles have loaded
expect(page).to have_css('div.article', minimum: article_count + 1)
end
Note: You'll probably want to change that to use whatever page object methods you are using (has_load_more?, etc) but the general logic should be correct

Ruby: Selenium: Webpage has strange page behavior for list, want to loop through each link in list

So I have the following code:
list = [#array of xpaths pointing to each of the 41 links]
begin
list.each do |entry|
wait.until {browser.find_element(xpath: entry)}
browser.find_element(xpath: entry).click
... #do what needs to be done inside link
browser.find_element(xpath: #location of back button) #goes back to list
end
wait.until{browser.find_element(css: ".next>a")}
browser.find_element(css: ".next>a").click
# ^ clicks the next button to get to next page
sleep 2
end while browser.find_element(class_name: "next").displayed?
# ^ end when there is no next button because were on the last page.
I am looping through each link on the page, doing what I need to do with it, and returning to the list. When all 41 links have been hit on the page, I tell it to load the next page. The issue is that for some reason going back to the list always goes back to the first page. So if I am on the third page and I click the link, do what I have to do, and return to the list I am back on the first page of the list. Anyone have an ideas on how to deal with this?

Iterate over links' array with Capybara

I'm trying to take a screenshot of the last three links found on a menu of a page.
all('.menu-left a').last(3).each do |e|
txt = e.text
puts txt
e.click
puts "clicked!"
page.save_screenshot(txt + ".png")
page.evaluate_script('window.history.back()')
end
Unfortunately I get:
/usr/local/lib/ruby/gems/2.0.0/gems/capybara-webkit-1.0.0/lib/capybara/webkit/node.rb:132:in `invoke': Capybara::Webkit::NodeNotAttachedError (Capybara::Webkit::NodeNotAttachedError)
from /usr/local/lib/ruby/gems/2.0.0/gems/capybara-webkit-1.0.0/lib/capybara/webkit/node.rb:4:in `visible_text'
from /usr/local/lib/ruby/gems/2.0.0/gems/capybara-2.2.0/lib/capybara/node/element.rb:61:in `block in text'
from /usr/local/lib/ruby/gems/2.0.0/gems/capybara-2.2.0/lib/capybara/node/base.rb:81:in `synchronize'
from /usr/local/lib/ruby/gems/2.0.0/gems/capybara-2.2.0/lib/capybara/node/element.rb:57:in `text'
How can I iterate over the links' array, click on it, take the screenshot, go back and repeat?
By clicking a link (causing page change), the references to the elements become invalid.
Save the href attributes somewhere and visit them instead of clicking elements. (If the elements are a tags).
If the clicking elements cause javascript code executed, you need to re-evaluate the css selectors to get elements again.

Resources