Capybara will not click button for Stripe SCA authentication - ruby

I cannot get Capybara to click on the SCA/3DS ‘Complete authentication’ button when running RSpec tests. Similar tests which do not trigger SCA pass just fine, and if I run VNC to view what Firefox is doing, the button is visible and I can click it myself in the browser.
My problem seems very similar to what’s discussed in the comments here, but the solutions do not work: I have tried changing the browser used, and flattening the iframe traversal.
Test code:
scenario "SCA required" do
create_payment_method(account, payment_method: "stripe", last_four: "1234")
visit "/billing"
click_on "Enter Card Payment"
within "#main-content" do
within_frame(find("iframe")) do # Stripe payment form is in an iframe.
find("input#Field-numberInput").set("4000002760003184") # SCA-required test card.
find("input#Field-expiryInput").set("1234")
find("input#Field-cvcInput").set("123")
find("input#Field-postalCodeInput").set("12345")
end
end
find("button#submit").click
# Stripe nests the popup in several layers of iframes.
stripe_frame = find("body > div > iframe") # Popup is prepended to the body element.
switch_to_frame(stripe_frame)
challenge_frame = find("iframe#challengeFrame")
switch_to_frame(challenge_frame)
fullscreen_frame = find("iframe.FullscreenFrame")
switch_to_frame(fullscreen_frame)
click_on "Complete authentication"
switch_to_frame(:top)
expect(page).to have_content "ends in 3184"
end
Is there some way to debug what Selenium is doing under the hood here? I don’t see any movement on the page when running click_on "Complete authentication", but if I click on the button myself in the Firefox instance being controlled by Selenium it does work.
Running click_on "Complete authentication" returns the element clicked, which appears to be the expected element when I drop into Pry and call native.dom_attribute("id").
I can see an error of some kind in the browser container’s logs:
1654078084345 Marionette WARN TimedPromise timed out after 500 ms: stacktrace:
TimedPromise/<#chrome://remote/content/marionette/sync.js:239:19
TimedPromise#chrome://remote/content/marionette/sync.js:224:10
interaction.flushEventLoop#chrome://remote/content/marionette/interaction.js:431:10
webdriverClickElement#chrome://remote/content/marionette/interaction.js:179:31
It’s a bit odd because it mentions #chrome but this is a headless Firefox instance.

Assuming no error is returned by the click_on call then I'm guessing the button is being clicked before it's ready to be clicked. You can test that by sleeping for a few seconds before calling 'click_on'/navigating through the frames. If that fixes it then you'd need to look at what changes on the button to indicate that the page has finished whatever work it's doing and the button is ready to be clicked.

I have solved this by clicking on the button directly with JavaScript:
execute_script(%Q{ document.querySelector("button#test-source-authorize-3ds").click() })
However, this does not in any way explain why click_on is not working, and if anything makes it more strange that it is not. If anyone has a better solution or a way to dig into why Capybara/Selenium are failing then that would be welcome.

Related

Capybara Element is not clickable at point

I was trying to use capybara to help me upload vocabulary to memrise.com, but I encounter some problems in its login page.
Here is what I've written.
def sign_in
self.visit 'https://www.memrise.com/login/'
find(".inpt-large[name='username']").set 'my-username' # Step 1
find(".inpt-large[name='password']").set 'my-password' # Step 2
find('input.btn-success.btn-large').click # Step 3
end
It can finish the step1 and setp2 but fail at step3 sometimes.
And below is the error message.
gems/selenium-webdriver-2.53.4/lib/selenium/webdriver/remote/response.rb:70:in `assert_ok': Element is not clickable at point (592.5, 23). Other element would receive the click: <span class="nav-item-btn-text"></span> (Selenium::WebDriver::Error::UnknownError)
Since the only items on the page I can find matching <span class="nav-item-btn-text"></span> are in the fixed header, I'm guessing you're running your tests with too small of a window size, so the actual Login button you want to hit is off the page when the test is run. This means when the test goes to click the button, it needs to scroll the item into view and it does that by scrolling it to the top of the page. That leaves the button behind the fixed header and unable to be clicked. To fix that you can either
increase your window size so the form doesn't need to be scrolled
set the elementScrollBehavior capability to 1 in your driver registration which will cause elements to be scrolled until they're visible at the bottom of the page rather than top.
scroll the page yourself before clicking the button
Additionally is there are reason you're using find(...).set vs just using fill_in for this form?
def sign_in
visit 'https://www.memrise.com/login/'
within('form#login') do
fill_in('username', with: 'my-username')
fill_in('password', with: 'my-password')
click_button('Login')
end
assert_text('You are now logged in') # whatever messagge is shown once login complete
end

Watir-webdriver throws 'not clickable' error even when element is visible, present

I am trying to automate tests in Ruby using the latest Watir-Webdriver 0.9.1, Selenium-Webdriver 2.53.0 and Chrome extension 2.21. However the website that I am testing has static headers at the top or sometimes static footers at the bottom. Hence since Watir auto-scrolls an element into view before clicking, the elements get hidden under the static header or the static footer. I do not want to set desired_capabitlites (ElementScrollBehavior) to 1 or 0 as the websites I am testing can have both - static header or static footer or both.
Hence the question are:
1) Why does Watir throw an exception Element not clickable even when the element is visible and present? See ruby code ( I have picked a random company website for an example) and the results below.
2) How can I resolve this without resorting to ElementScrollBehaviour?
Ruby code:
require 'watir-webdriver'
browser = Watir::Browser.new :chrome
begin
# Step 1
browser.goto "shop.coles.com.au/online/mobile/national"
# Step 2 - click on 'Full Website' link at the bottom
link = browser.link(text: "Full website")
#check if link exists, present and visible?
puts link.exists?
puts link.present?
puts link.visible?
#click on link
link.click
rescue => e
puts e.inspect
ensure
sleep 5
end
puts browser.url
browser.close
Result:
$ ruby link_not_clickable.rb
true
true
true
Selenium::WebDriver::Error::UnknownError: unknown error: Element is not clickable at point (460, 1295). Other element would receive the click: div class="shoppingFooter"...div
(Session info: chrome=50.0.2661.75)
(Driver info: chromedriver=2.21.371459 (36d3d07f660ff2bc1bf28a75d1cdabed0983e7c4),platform=Mac OS X 10.10.5 x86_64)>
http://shop.coles.com.au/online/mobile/national
thanks!
You can do a click at any element without getting it visible. Check this out:
link.fire_event('click')
BUT It is very very very not good decision as far as it will click the element even if it is not actually visible or in case when it is just impossible to click it (because of broken sticky footer for example).
That's why much better to wait the fooler, scroll the page and then click like:
browser.div(id: "footerMessageArea").wait_until_present
browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
link.click
The sticky footer is blocking webdriver from performing the click, hence the message that says 'other element would receive the click'.
There are several different ways you can get around this.
Scroll down to the bottom of the page before the click
Hide/Delete the sticky footer before any/all link clicks
Focus on an element below the element you want to click before you perform the click
I Guess your element is visible in the screen.
Before clicking on the element first you have to scroll the webpage so that element is visible then perform the click. Hope it should work.
I had similar issue,
I just used following javascript code with watir:
link = browser.link(text: "Full website")
#browser.execute_script("arguments[0].focus(); arguments[0].click();", link)
Sometimes I have to use .click! which i believe is the fire_event equivalent. Basically something is layered weird, and you just have to go around the front end mess.

Verify for the text to be present in an overlay using watir-webdriver

I have an overlay form where i create an user for our application. After giving the details in the text fields i click on save and try to capture the Saved Successfully Text which appears for about a second on the overlay. But i am unable to do so as i get an error saying "Element is no longer attached to the DOM (Selenium::WebDriver::Error::StaleElementReferenceError)".I have used the below code:
if($browser.div(:class=>"validation-summary-valid").exists?)
message=$browser.div(:class=>"validation-summary-valid").li.text
if(message=="Saved Sucessfully")
puts("Save action complete")
else
fail("fail")
end
end
in capybara i would scope the code using ( within ) to the message element in the Dom then use have_content
within('#Browser div')do
page.should have_content('Saved Successfully')
end
hope this will help to try something similar in watir
What I understood from the situation is, the moment you click on save a transient message appears on the UI and a check needs to be performed.
The below approach should work fine in this case,
# the browser waits for 20 s until the element is present(exists+visible) on the UI
$browser.div(:class=>"validation-summary-valid").wait_until_present(20).li.text

intercepting the onload event fired by the browser in watir

I have a unique situation over here. I have a button on a form which produces a popup if there are some errors in the form. [I know this is not good practice, but the developers of the product would not be changing that behavior!] The form navigates to a different page if all the required fields are correctly populated. Now, I need to write a script in order to click the "Submit" button on the form which either might produce a popup or navigate to the next page.
I have the used the click_no_wait on the "Submit" button and handled the popup using AutoIt as per Javascript Popups in Watir. Now, if all the information is valid and the form navigates to the next page, I use a delay in the script by following some of the techniques described in How to wait with Watir. I am using a Watir::wait_until() to wait in the script.
Now sometimes because of some network issues, it takes time to go to the next page (report-generation) page when the form is submitted and thus the script fails because of the timeout value specified in the wait_until.
I was wondering whether there is a way to intercept the onload event of the HTML page in Watir, since the onload event isn't fired until the entire page is loaded. By that way I could have an accurate estimate of the timeout value and not experiment with it. Thus, my script will pass 100% rather than say 98% right now.
Thanks for any help on this topic.
You could try setting up a rescue for the time out, then looping a reasonable amount of times (2 or 3?) if it encounters a timeout.
E.g.
# All your button clicking and autoit stuff here
i = 0
begin
b.wait_until{ # the thing you're waiting to happen }
rescue TheSpecificTimeOutException
# Sorry I can't remember it, the command prompt will tell you exactly
# which one
if i < 3
i += 1
retry
else
raise
end
end
I'm sure i'll have messed something up in the above, or there'll be more concise ways of doing it, but you get the idea. When it times out, give it another few tries before giving up.

How can I know what element is clicked in IE?

I'm automating IE with watir, and I want to know what html element(s) are clicked (selected). Can this be done using watir? win32ole? In a last chance, without ruby?
Something like:
Click a button -> button with id=213 and class=btn_class was clicked.
Click a text field -> text field with id=123 and value=my_text was clicked.
Try one of the recorders, Selenium IDE for example.
I'm not sure if I completely understand either, but would a custom function like this work?
def click(item, how, what)
#browser.item(how, what).click
puts "#{item} with #{how}->#{what} was clicked"
end
click("button", ":id", "awesome")
Edit: If you're attempting to identify page elements so that you can then use them in a Watir script, the Developer Toolbar is perfect for this application, much like Firebug for Firefox.
http://www.microsoft.com/download/en/details.aspx?id=18359
Your comment to me & your response to Zeljko appear to contradict each other. If you want to use WATIR for this task, the code above will execute a mouse click and post the information to console. The only other way to get information is to locate the object in WATIR and fish for more details:
object = #browser.button(:name, /myButton/)
object.id
object.title
object.status
object.height, etc.
or
#browser.divs.each do |div|
puts div.id, div.title
end
I'd recommend a strong look at the capabilities of the various developer tools, such as are provided with Chrome, Firefox, and IE. those are generally the best means to get this kind of information.
Watir is really about driving the browser, and getting info out of the DOM. it's not really setup to report on manual interactions with the browser.

Resources