Devise/Capybara Ambiguous match - ruby-on-rails

I am using devise to create a sign up wizard,
but capybara(2.0.2) raises
Feature: Signing up
In order to be attributed for my work
As a user
I want to be able to sign u
Scenario: Signing up
Given I am on the homepage
When I follow "Sign up"
And I fill in "Email" with "user#ticketee.com"
And I fill in "Password" with "password"
Ambiguous match, found 2 elements matching field "Password" (Capybara::Ambiguous)
./features/step_definitions/web_steps.rb:10:in `/^(?:|I )fill in "([^"]*)" with "([^"]*)"$/'
features/signing_up.feature:10:in `And I fill in "Password" with "password"'
And I fill in "Password confirmation" with "password"
And I press "Sign up"
Then I should see "You have signed up successfully."
step definition is
When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
fill_in(field, :with => value)
end

With Capybara 2.1, this works:
fill_in("Password", with: '123456', :match => :prefer_exact)
fill_in("Password confirmation", with: '123456', :match => :prefer_exact)
From here :prefer_exact is the behaviour present in Capybara 1.x. If multiple matches are found, some of which are exact, and some of which are not, then the first eaxctly matching element is returned.

In version 2.0 Capybara's find method raises a Capybara::Ambiguous exception when several elements matching specified locator where found. Capybara doesn't want to make an ambiguous choice for you.
The proper solution is to use another locator (e.g. find('#id').set('password') or fill_in('field_name', with: 'password')
Read "Ambiguous Matches" section of Capybara 2.0 Upgrade guide for a bit longer explanation of the same.

Related

How to properly evaluate if a page has certain content with capybara?

I am currently trying to evaluate the result of clicking the "Pay" button on my site with Capybara if the form has invalid params. It seems like the test is doing everything correctly, filling out all fields correctly, clicking the "Pay" button, but then it is not "seeing" my .alert selector with the text "Email can't be blank, Email is invalid". I am not sure why it is not "seeing" this. Its as if its not even there. My controller is setting flash and then rendering the page with the flash method. Is Capybara trying to evaluate if the current page has the flash? The code for my current test is below. Any help is appreciated. Ive been going through the documentation but must be missing something about expect().
feature "order features", type: :feature do
feature "making a new order" do
before(:each) do
visit "/orders/new"
end
scenario "with invalid params", js: true do
fill_in "Your Name *", with: "Benedict Cumberbatch"
select("CAM Academy", from: 'order_school_name')
fill_in "Street Address *", with: "221b Baker St."
fill_in "City", with: "Vancouver"
select("WA", from: 'order_state')
fill_in "Zip code *", with: "98671"
fill_in "groupNumberBooks", with: 1
fill_stripe_elements(card: '4242 4242 4242 4242', selector: "#card-element-1 > div > iframe")
click_button "Pay"
expect(page).to have_selector('.alert', text: "Email can't be blank, Email is invalid")
end
end
end
On a side note, I have also tried expect(page).to have_content("Email can't be blank"). This also does not work. What am I missing here?
edit: the exact error message I am getting is:
Failures:
1) order features making a new order with invalid params
Failure/Error: expect(page).to have_selector('alert', text: "Email can't be blank, Email is invalid")
expected to find css "alert" but there were no matches
# ./spec/features/order_features_spec.rb:19:in `block (3 levels) in <top (required)>'
I have gone to the specified path and have manually filled out these fields and have evaluated what is being presented on the page when the render is called and the flash[:alert] is set.
It sounds like your test is probably hitting the real stripe (sandbox instance) which means the failure is going to take longer than most of your tests. This means that Capybara.default_max_wait_time isn't long enough for the page to actually update to show the error message. You could increase Capybara.default_max_wait_time or you can tell individual expectations to allow longer wait times for a match using the wait parameter
expect(page).to have_selector('.alert', text: "Email can't be blank, Email is invalid", wait: 20)
which will wait up to 20 seconds for the expected element to appear on the page

Testing ruby with rails, Element not found

I get the error:
Capybara::ElementNotFound:
Unable to find field "user_email"
And this is the test code:
feature 'User' do
given!(:user) { User.new(email: 'testuserid#example.com', encrypted_password: 'test') }
scenario 'opens sign_up page' do
visit new_user_session_path
expect(page).to have_content 'unique text on the page'
end
scenario 'signs in with invalid email' do
visit new_user_session_path
fill_in('user_email',with: 'ssd')
expect(page).to have_content 'unique text on the page'
end
end
My HTML file consists of this code literally:
unique text on the page
<br>
<input type="text" id="user_email">
So this proves that the path is correct because my first scenario runs correctly. It is visiting the right page. But still I get this error for second scenario in fill_in.
I have also tried element = page.find("user_email"), it gives same error.
What am I possibly doing wrong?
I have been scratching my head like hell.
Usually the reason for this is that the input isn't actually visible on the page. You can verify this by doing
fill_in('user_email', with: 'ssd', visible: false)
If that succeeds in finding the element, then you need to change your test to first perform whatever actions make the field visible before attempting to fill it in.
Your code seems right. Maybe you are visiting wrong url or you have used user_email id once more. But you can give a try with alternative syntax like following :
find("input[id$='user_email']").set "ssd"

Find and fill in input field by data attribute

I want to find elements by a "test" data attribute and fill them in like the following:
When(/^I sign in with valid credentials$/) do
email = page.find("data-test='email'")
password = page.find("data-test='password'")
fill_in email, with: #user.email
fill_in password, with: #user.password
submit = page.find("[data-test='submit']")
submit.click
end
The button works fine, but the inputs throw the following error:
Unable to find field #<Capybara::Element tag="input"> (Capybara::ElementNotFound)
Is there a way to do this in capybara? Thanks in advance for the help.
fill_in method accepts element's id, name or label text, so it might not work with the result the find method returns. In your case try the set method:
find("input[data-test='email']").set(#user.email)

Check the content of a field populated by javascript with Capybara before submit

I have a field whcih I am auto populating based on a dropdown on the page.
i.e. I select task_type and the task_name has task_type populated.
I can't work out how to test this. I basically just want to test what the current content of the field is.
Everything I try seems to be able to find the field but not check the content of it.
Then(/^"(.*?)" should contain "(.*?)"$/) do |field, text|
page.should have_field(field, :text => value)
end
I'm assuming I need to do some js trickery to get the info from the browser, but can't seem to work it out.
And I should see "Annual Accounts" in "task[name]"
Have tried task_name aswell and both fail with
find(field).should have_text(text)
With message
Unable to find css "task[name]"
if I use
page.should have_field(field, :text => text)
Then they fail with:
expected to find field "task_name" with text "Annual Accounts" but there were no matches. Also found "",
You can use the have_text helper
find(field).should have_text(text)
Right, both of these work:
Not sure what the difference between them is.
I also think this does an exact match. If anyone could let me know how to do a "contains" type match that would be really handy.
Then(/^I should see "(.*?)" in "(.*?)"$/) do |text, field|
# page.has_field?(field, :with => text)
page.should have_field(field, :with => text)
end

Capybara matcher for presence of button or link

Users on web page don't distinguish between "button" and "link styled as button".
Is there a way to add check whether a "button or link" is present on page?
For example Capybara has step:
page.should have_button('Click me')
which does not find links styled as buttons.
Updated answer (should matcher is deprecated in RSpec 3.0+):
expect(page).to have_selector(:link_or_button, 'Click me')
Before:
page.should have_selector(:link_or_button, 'Click me')
Followed from click_link_or_button which is defined here: https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/actions.rb#L12
def click_link_or_button(locator)
find(:link_or_button, locator).click
end
alias_method :click_on, :click_link_or_button
It calls a selector :link_or_button. This selector is defined here: https://github.com/jnicklas/capybara/blob/master/lib/capybara/selector.rb#L143
Capybara.add_selector(:link_or_button) do
label "link or button"
xpath { |locator| XPath::HTML.link_or_button(locator) }
end
It calls this method: http://rdoc.info/github/jnicklas/xpath/XPath/HTML#link_or_button-instance_method
# File 'lib/xpath/html.rb', line 33
def link_or_button(locator)
link(locator) + button(locator)
end
So i tried to check the presence of the selector and it worked:
page.should have_selector(:link_or_button, 'Click me')
Using the expect syntax
expect(page).to have_selector(:link_or_button, 'Click me')
This works without needing to define a custom matcher.
You can also use a custom matcher
RSpec::Matchers::define :have_link_or_button do |text|
match do |page|
Capybara.string(page.body).has_selector?(:link_or_button, text: text)
end
end
Then do
expect(page).to have_link_or_button('Login')
Personally I would give your button or link an id and look for that using
page.should have_css('#foo')
This way you can refer to the link or button without worrying about its implementation.
I always find this useful: https://gist.github.com/428105
I think you can use the find button instance method:
(Capybara::Element) find_button(locator)
Using id, name, value.
Or if you want a link
(Capybara::Element) find_link(locator)
From: http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Finders#find_button-instance_method
I had an odd case where some smoke tests marched across various customer-centric login pages that had slight variations on doing the login submit button... Driven by a Cucumber table of user, org, etc.
# A bit of a hack, org_name is normally a subdomain, but sometimes it is the complete domain
def login(user, org_name)
# Use the below to automatically hit each user's org's server
if org_name.include? '.com'
Capybara.app_host = "http://#{org_name}"
else
Capybara.app_host = "http://#{org_name}.mydomain.com"
end
visit '/'
fill_in 'username', :with => user
fill_in 'userpwd', :with => '***'
begin
page.find(:link_or_button, 'submit')
click_on 'submit'
rescue Capybara::ElementNotFound
page.find(:link_or_button, 'Log In')
click_on 'Log In'
rescue Capybara::ElementNotFound
pending "Need to determine how to invoke the Login button for #{org_name} near Line ##{__LINE__} of #{__method__} in #{__FILE__} "
end
# -----------------------
# Work-around for modal popup saying SSL is mismatched if you are using actual production URLs
# The rescue is for cases that do not exhibit the modal pop-up
page.driver.browser.switch_to.alert.accept rescue Selenium::WebDriver::Error::NoAlertPresentError
# Ensure that login was successful
page.should_not have_content 'Login failed'
end
if html:
<a class="top-menu__item">text123
<span class="label">
<span class="label">86</span>
</span>
</a>
not work:
assert page.has_selector?(:link_or_button, text: 'text123')
assert page.should have_selector(:link_or_button, text: 'text123')

Resources