How to click a span in capybara tests? - ruby

I'm trying to click on a checkbox, but when I run the action, it doesn't work.
I'm using the same way to click buttons and other elements.
I'm using a data-testid to find the checkbox in the view.
I need to click on the checkbox, and after clicking execute the actions of the next step.
This checkbox is a react component that will have a span in the view.
context 'when the user agrees to charges by clicking a checkbox' do
before do
# sleep 1000
wait_before_clicking
page.find("span[data-testid='prepay-enrollment-modal-agreement-checkbox']").click
end
it 'renders new content for the user to review payment details' do
expect(page).to have_selector("[data-testid='review-step-container']")
expect(page).to have_content("Your Plan")
expect(page).to have_content("100 pages/ month")
expect(page).to have_content("Plan cost for 12 months, before discount")
expect(page).to have_content("Prepay discount (10%)")
expect(page).to have_content("Total Due:")
end
end

The data-testid attribute is on the input element not on the span, so find("span[data-testid='prepay-enrollment-modal-agreement-checkbox']") won't work. If you put the data-testid attribute on the span rather than the input then it would work, however there's a better way. Since the checkbox has an associated label element you can just tell Capybara it's allowed to click on the label element if necessary. First, since you're using test ids, set
Capybara.test_id = 'data-testid'
in your global config, and then in your test just do
page.check('prepay-enrollment-modal-agreement-checkbox', allow_label_click: true)
Note: rather than expect(page).to have_selector("[data-testid='review-step-container']") you should use expect(page).to have_css("[data-testid='review-step-container']") to make it clearer that you know you're passing CSS. Also for test speed reasons you may want to combine all of the have_content calls into one using a regex

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 click on a button that can only be identified by a custom attribute?

I'm just getting into automated testing and I've been writing some code for a fake site that a friend set up for me for testing. The test I'm writing is meant to click on an "Add to Cart" button, however each of the button have the same exact class and the only way to really identify them is through the custom attributes.
This is the code for the button i'm trying to click
<a class="btn btn-success btn-block addItem" data-id="1" data-name="Chocolate Cups" data-price="1.00">Add to Cart</a>
(I'm trying to click on the button by using the data-id attribute)
Each of the buttons have the same class so they can't be identified that way otherwise an error pops up saying that the result is "Ambiguous" and thus can't do it.
I used the code below and managed to identify a specific button however I'm not sure how I would now click the button.
Given("I'm on the sweets list page") do
visit("https://sweetshop.netlify.com/sweets.html")
end
When("I click {string} button") do |string|
page.should have_css("*[data-id='1']")
end
Then("add the item to cart") do
end
You can try something like:
Then("add the item to cart") do
find(:css, "*[data-id='1']").click
end
or
Then("add the item to cart") do
find(:xpath, "//*[#data-id='1']").click
end
Similar post: Cucumber/Capybara Selecting Button from Specific Class?

Click on buttons on the page that has got the same class using capybara with siteprism

There are 20 different buttons to expect and needs to be clicked through to expect and verify the urls inside the code. I have tried different ways to implement my tests but they are failing.
I'm trying something like:
page.all(:class => 'action red').each do |button|
c = button.find(:class => 'action view red')
c.click
page.driver.browser.switch_to.window(#new_window)
expect('some element on those 20 different browsers sessions before closing them')
page.driver.browser.close
end
end
I'm getting this error:
ArgumentError: invalid keys :class, should be one of :count, :minimum,
:maximum, :between, :text, :visible, :exact, :match, :wait
Any can help me in the code how to perform get the elements of all the 20 buttons, store them and click them to expect the url each of them before closing it
Your "buttons" aren't buttons - since they are <a> elements they are actually links, styled to look like buttons.
Assuming that clicking each of these links actually opens a new window (since you're attempting to switch to a new window) then the code would be something like
page.all(:link, class: ['action', 'red']).each do |link|
win = page.window_opened_by { link.click }
page.within_window(win) do
expect(page).to ... # whatever you need to expect
end
win.close()
end
Note this doesn't use any driver specific (.driver.browser...) methods - you should stay away from them whenever possible since they are generally a sign you're doing something wrong. Additionally, the :class option wasn't universally available on all of Capybaras builtin selector types until v2.10, so you will need to be using a newer version of Capybara for that.

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

Testing a 'stale' element with Selenium and rspec

Still a newbie to RSpec and Ruby, so apologies for any obvious errors, omissions, etc in my question.
What I'm looking to test using RSpec (with Selenium Webdriver) is the following;
1) I've (randomly) selected a make of car from a drop down menu
2) From this selected make, I then (randomly) selected a corresponding model of car from a drop down menu
3) Then pressed "Search" button and a list of, for example, BMW 3-Series cars were correctly displayed.
What I now need to do using RSpec is test that the (randomly selected) make and model names are displayed in a page heading and in the URL.
For example, selecting BMW 3-Series will result in the URL ending in /bmw/3-series and a heading on the page will read "BMW 3-Series Cars For Sale".
However, the problem I'm having is that a 'Stale Element' error message is being displayed when I run the test.
I know why the error is displayed (because the elements used to select the make and model are 'lost' when the Search button is pressed, as a new page opens which does not contain these elements), but I don't know how to get around this problem.
Any help would be greatly appreciated.
# This generates a random manufacturer
manuselect = #driver.find_element(:id, 'ctl00_contentHolder_topFullWidthContent_ctlCarsForSaleSearch_ctlUsedCarsSearchTab_ddlManufacturer_Control')
manufacturer = manuselect.find_elements(:tag_name => 'value').sample
#puts manufacturer.text
manufacturer.click
sleep 7
# This generates a random model
modelselect = #driver.find_element(:id, 'ctl00_contentHolder_topFullWidthContent_ctlCarsForSaleSearch_ctlUsedCarsSearchTab_ddlRange_Control')
carmodelrange = modelselect.find_elements(:tag_name => 'value').sample
puts carmodelrange.text
carmodelrange.click
sleep 5
# Click on the Search button
#driver.find_element(:id, 'ctl00_contentHolder_topFullWidthContent_ctlCarsForSaleSearch_ctlUsedCarsSearchTab_btnSearch').click
You just need to store the text you're going to look for into variables before the elements disappear from the page
manufacturer_name = manufacturer.text
modelrange_name = carmodelrange.text
#click the search button...
# the following expectations require Capybaras matchers, use whatever expecations are equivalent with whatever you're using
expect(page).to have_current_path(/#{Regexp.quote(manufacturer_name)}\/#{Regexp.quote(modelrange_name)}$/)
expect(page).to have_text("#{manufacturer_name} #{modelrange_name}")

Resources