Reusing a cucumber step definition on a single page Angular app - ruby

I'm using watir-webdriver with the page-object gem to drive cucumber tests for a single web page app built in angular js.
The site uses a multiple stage registration process, filling a series of details each time then clicking a 'next' button. (which is enabled when all details are completed for that page)
The issue occurs when I'm attempting to reuse the following step definition:
And(/^I click next$/) do
#registration = RegistrationPage.new(#browser)
#registration.click_next
end
Which calls the page-object:
class RegistrationPage
include PageObject
button(:next, :value => "Next")
def click_next
#browser.wait_while { next_element.disabled? }
next_element.click
end
end
For the first section, the Next button is found correctly, however on the second call to the step definition, the button cannot be found, despite the button being verifiably enabled and otherwise identical.

I see that you've answered your question but in case it helps to explain it to anyone else:
The issue is likely that to watir it isn't the same next button on each page and all of the next buttons are in scope all of the time. Therefore to click the correct one you would have to use the index property of the button you want i.e. index: 1 for the second page and index 2 for the the button on the third page.

Related

Capybara will not click button for Stripe SCA authentication

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.

How to iterate with more than one element on the page

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

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

links opening in new window - Cheezy page-object ruby gem with selenium

I'm new to page-object and selenium, but so far it's all been going ok. The last elements I'm testing for this page are social media links.
To check that the links are correct i usually click them and then use selenium's driver.current_url to check that the url of the window is what we'd expect for that link, but these social media buttons are opening links in a new window (this is just to their static facebook page rather than a login etc) which causes the test to fail
is there a way of either:
1-forcing the link to open in the current window, or
2-extracting the target url from the link_element?
this is what the tests look like atm:
def test_facebook_button
assert #testpage.facebook_icon?, 'Facebook button not found'
#testpage.facebook_icon_link
sleep 5
assert #browser.current_url == '<facebookurlforsite>', 'Facebook link does not open expected URL'
end
if not i'm guessing i'd just use the actual selenium methods as some sort of exception to manually grab the properties of the link, i'd rather not some elements be in the page model and some out in the test itself but i can see how it's probably necessary
If you are just checking that the link navigates to the right URL, checking the link's href attribute will be the quickest. The PageObject::Elements::Link elements have an href method for this.
Assuming that facebook_icon_link is your link defined by an accessor method, you would call:
facebook_icon_link_element.href
In other words, the assertion could be written as:
assert facebook_icon_link_element.href == '<facebookurlforsite>'

How a verify a page in transition using selenium

I clicked on a button on some page which redirects me to some other page.but between these pages there is one page which comes only for 1 or 2 seconds. I have to verify that page.I am using selenium.
Any suggestions?
What you could do here, is do a simple waitForElement subroutine.
Ideally, when your test framework cannot find an element to operate with, it will fail.
So we can assume, that when your test gets past waitForElement(theInterimElement) then your interim element has appeared, and we can continue.
Check the url of the page is the url of the desired page and wait while this is not true.
while (!webDriver.Url.Contains(desiredUrl.ToString()))
Thread.Sleep(50);

Resources