Capybara ambiguous match errors - capybara

The project is a reboot of the original, and I am trying to get to grips with the various gems - the rails composer does a brilliant job of everything but me gaining understanding of what is going on.
I am getting this Capybara error that is tied to the signup code (I have several specs using the call, but have only included one of them here as they all call the same helper).
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching field "Password"
I have looked on StackOverflow (several times), and used the power of Messr Google - who mainly suggests I look here.
At first I thought it was to do with having a mismatch of
fill_in 'Email', with: email
and
fill_in 'Email', :with => email
but neither seems to make a difference.
From reading the documentation (I know: hangs head in shame) at rubydoc.info capybara fill-in , the fill-in takes a locator and an options array.
My understanding is that fill_in 'Password', with: password would match the Password locator, which is the id="user_password" part of the html. I don't see any others on the page, so am now reaching out for help.
All my code is on the SO001 branch of my projectGithub project branch to match this question.
sign_up_spec.rb
feature 'Sign Up', :devise do
# Scenario: Visitor can sign up with valid email address and password
# Given I am not signed in
# When I sign up with a valid email address and password
# Then I see a successful sign up message
scenario 'visitor can sign up with valid email address and password' do
sign_up_with('test#example.com', 'please123', 'please123')
txts = [I18n.t( 'devise.registrations.signed_up'), I18n.t( 'devise.registrations.signed_up_but_unconfirmed')]
expect(page).to have_content(/.*#{txts[0]}.*|.*#{txts[1]}.*/)
end
# ... others snipped for space
end
spec/support/helpers/session_helpers.rb
module Features
module SessionHelpers
def sign_up_with(email, password, confirmation)
visit new_user_registration_path
save_and_open_page
fill_in 'Email', with: email
fill_in 'Password', with: password
fill_in 'Password confirmation', :with => confirmation
click_button 'Sign up'
end
def signin(email, password)
visit new_user_session_path
fill_in 'Email', with: email
fill_in 'Password', with: password
click_button 'Sign in'
end
end
end
The devise new.html.erb code is
Sign up
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :email, required: true, autofocus: true %>
<%= f.input :password, required: true, hint: ("#{#minimum_password_length} characters minimum" if #minimum_password_length) %>
<%= f.input :password_confirmation, required: true %>
</div>
<div class="form-actions">
<%= f.button :submit, "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
The form part of the html has
<form novalidate="novalidate" class="simple_form new_user" id="new_user" action="/users" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" />
<div class="form-inputs">
<div class="form-group email required user_email">
<label class="control-label email required" for="user_email">
<abbr title="required">*</abbr> Email
</label>
<input class="form-control string email required" autofocus="autofocus" required="required" aria-required="true" type="email" value="" name="user[email]" id="user_email" />
</div>
<div class="form-group password required user_password">
<label class="control-label password required" for="user_password">
<abbr title="required">*</abbr>
Password</label>
<input class="form-control password required" required="required" aria-required="true" type="password" name="user[password]" id="user_password" />
<p class="help-block">6 characters minimum</p>
</div>
<div class="form-group password required user_password_confirmation">
<label class="control-label password required" for="user_password_confirmation">
<abbr title="required">*</abbr>
Password confirmation
</label>
<input class="form-control password required" required="required" aria-required="true" type="password" name="user[password_confirmation]" id="user_password_confirmation" />
</div>
</div>
<div class="form-actions">
<input type="submit" name="commit" value="Sign up" class="btn btn-default" data-disable-with="Sign up" />
</div>
</form>

As documented, Capybara's fill_in will match an element on either its id or name attributes, or on the text of an associated label element. The attribute matches need to be exact and the text of an associated label element may be an exact or substring match depending on the setting of a few options.
In the current versions of Capybara, Capybara.exact defaults to :smart which when looking for an associated label text match will first attempt for an exact match, and if none is found will attempt for a substring match. In your case the label text of two of your fields are *Password and *Password confirmation which when checked for an exact match with Password fail, and when checked with a substring match both match - hence the "Ambiguous match" error since more that one element matched.
To fix this (while still using fill_in) you could do any of the following
fill_in '*Password', with: password # will match exactly on one elements label text
find('div.user_password').fill_in 'Password' with: password # scope fill_in so substring match is unique
fill_in 'user_password', with: password # match on id attribute
fill_in 'user[password]', with: password # match on name attribute

Related

Rails 5, simple_form_for, RSpec & Capybara - click_button does not work with my form

The following Capybara/RSpec test is failing on my Rails app and I can't figure out why. I'm using the simple_form_for Gem to create the form and submit button. The update method seems to be working properly, as when I change
expect(#coin.currency_name).to eq('Updated Name')
to
expect(page).to have_text('Updated Name')
the test passes and the updated name is shown on the new page. However #coin.currency_name doesn't seem to be updated when I use the previously described expect method. When I manually update the Coin model (on the page, not using RSpec) it works fine and the currency_name is updated.
What am I doing wrong on this test?
spec/features/coins/coin_spec
require 'rails_helper'
RSpec.feature 'Coins' do
before(:each) do
#user = FactoryBot.create(:user)
end
context 'update coin' do
scenario 'should succesfully edit name if user=admin' do
#user.update(admin: true)
login_as(#user, :scope => :user)
#coin = Coin.create!(currency_name: "TestName", user_id: #user.id)
visit edit_coin_path(#coin)
fill_in 'Currency Name', with: 'Updated Name'
click_button 'Submit'
expect(#coin.currency_name).to eq('Updated Name')
end
end
end
app/views/coins/edit.html.erb
<div class='form-container'>
<%= simple_form_for #coin, url: coin_path do |f| %>
<h2>Edit Coin</h2>
<div class="form-container__section">
<%= f.input :currency_name, label: "Currency Name", class: 'form-control' %>
<%= f.input :link_name, placeholder: "Link Name", label: false, class: 'form-control' %>
...
<%= f.button :submit, value: "Submit", class: "btn primary-small", style: "margin-top: 20px;" %>
<% end %>
</div>
and the HTML
<div class="form-container">
...
<h2>Edit Coin</h2>
<div class="form-container__section">
<div class="form-group string required coin_currency_name"><label class="control-label string required" for="coin_currency_name"><abbr title="required">*</abbr> Currency Name</label><input class="form-control string required" type="text" value="OldName" name="coin[currency_name]" id="coin_currency_name"></div>
...
<input type="submit" name="commit" value="Submit" class="btn btn-default primary-small" style="margin-top: 20px;" data-disable-with="Update Coin">
</form>
After change of model, use reload method:
click_button 'Submit'
#coin.reload
expect(#coin.currency_name).to eq('Updated Name')

Capybara is unable to find a field

I have the following form
<%= form_for(#route, :html => {:class => "form-horizontal", :role => 'form'}) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="form-group">
<input type="text" name="route_origin_input" id="route_origin_input" placeholder="From" onFocus="geolocate(); autocompleteLocation(this,route_origin)" class="form-control" />
<%= f.hidden_field :origin, class: "form-control" %>
</div>
<div class="form-group">
<input type="text" name="route_destination_input" id="route_destination_input" placeholder="Destination" onFocus="geolocate(); autocompleteLocation(this,route_destination)" class="form-control" />
<%= f.hidden_field :destination, class: "form-control" %>
</div>
<div class="form-group">
<% options = options_from_collection_for_select(#vehicleList, 'vehicle', 'vehicle') %>
<%= f.select :vehicle, options, class: "form-control" %>
</div>
<div class="form-group">
<%= f.date_field :departure_date, class: "form-control" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary", id: "route_post" %>
<% end %>
and the following test
require 'spec_helper'
describe "Route pages" do
subject { page }
let(:user) { FactoryGirl.create(:user) }
before { sign_in user }
describe "route creation" do
before {
visit terminal_path
}
describe "with invalid information" do
it "should not create a route" do
expect { click_button "route_post" }.not_to change(Route, :count)
end
describe "error messages" do
before { click_button "route_post" }
it { should have_danger_message('Not Submitted') }
end
end
describe "with valid information", :js => true do
before do
fill_in 'route_origin_input', with: "Kimmage, Dublin, Ireland"
fill_in 'route_destination_input', with: "Kimmage, Dublin, Ireland"
fill_in 'route_departure_date', with: Time.now
select 'Car', :from => "route_vehicle"
end
it "should create a route" do
expect { click_button "route_post" }.to change(Route, :count).by(1)
end
end
end
end
For some reason the two text fields that I have added with html cannot be found by capybara
The error I receive is
Failure/Error: fill_in 'route_origin_input', with: "Kimmage, Dublin,
Ireland"
Capybara::ElementNotFound:
Unable to find field "route_origin_input"
# /Users/jeff/.rvm/gems/ruby-2.0.0-p594/gems/capybara-2.4.4/lib/capybara/node/finders.rb:41:in
block in find'
# /Users/jeff/.rvm/gems/ruby-2.0.0-p594/gems/capybara-2.4.4/lib/capybara/node/base.rb:84:in
synchronize'
# /Users/jeff/.rvm/gems/ruby-2.0.0-p594/gems/capybara-2.4.4/lib/capybara/node/finders.rb:30:in
find'
# /Users/jeff/.rvm/gems/ruby-2.0.0-p594/gems/capybara-2.4.4/lib/capybara/node/actions.rb:56:in
fill_in'
# /Users/jeff/.rvm/gems/ruby-2.0.0-p594/gems/capybara-2.4.4/lib/capybara/session.rb:676:in
block (2 levels) in <class:Session>'
# /Users/jeff/.rvm/gems/ruby-2.0.0-p594/gems/capybara-2.4.4/lib/capybara/dsl.rb:51:in
block (2 levels) in '
# ./spec/requests/route_pages_spec.rb:30:in `block (4 levels) in '
I've used fill_in in other tests and the only difference I can see is that I have used standard html code in the form. These fields are used to process the info the user enters and then add the result to a hidden field that is part of my model, basically getting the result from Google maps api.
EDIT:
Here is the HTML
<div class="form-group">
<input type="text" name="route_origin_input" id="route_origin_input" placeholder="From" onFocus="geolocate(); autocompleteLocation(this,route_origin)" class="form-control" />
<input class="form-control" id="route_origin" name="route[origin]" type="hidden" />
</div>
The HTML and spec look OK. Here's some things I would try:
Are you sure it is failing on route_origin_input and not another field?
What if you run it without :js => true? Perhaps there is some Javascript that is preventing the form from rendering or redirecting.
Use save_and_open_page and/or get a screenshot to see what is going on.
Which driver are you using? Different drivers behave differently. Try a different driver.
Try selenium-webdriver. It runs a browser and you can see what is going on. Use sleep 10 before the fill_in. The spec will pause and you can inspect the form or manually fill it in.
Usually I use within '.form' do. That may make a difference. See Capybara docs for how to use within.

RSpec does not recognise my internationalised labels

In my Rails 4 application I have specified my locales as follows:
config/locales/views/show_user/en.yml
with content:
en:
activerecord:
attributes:
user:
first_name: First name
last_name: Last name
...
layouts:
header:
home: "Home"
help: "Help"
...
footer:
about: "About"
contact: "Contact"
users:
new:
signup: "Sign up!"
create_my_account: "Create my account"
When I run my application in my localhost, all my labels display correctly. However, my RSpec test,
describe "with valid information" do
before do
fill_in "First name", with: "Jenny"
fill_in "Last name", with: "Johnson"
fails when I make the following change to my partial, /views/users/_fields.html.erb
<%= f.label t :first_name, scope: [:activerecord, :attributes, :user] %>
<% # f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
My RSpec test displays this failure:
1) User pages signup with valid information should create a user
Failure/Error: fill_in "First name", with: "Jenny"
Capybara::ElementNotFound:
Unable to find field "First name"
# ./spec/requests/user_pages_spec.rb:88:in `block (4 levels) in <top (required)>'
I have noticed that the generated html of my form displays a different id for first_name, but I do not know if this is important:
<label for="user_First name">First name</label>
<input id="user_first_name" name="user[first_name]" type="text" />
<label for="user_last_name">Last name</label>
<input id="user_last_name" name="user[last_name]" type="text" />
My question is therefore: Why can Capybara not find my label, :first_name, while my page displays correctly in my browser?
It is important. Capybara uses the locator argument to fill_in in one several ways:
if there is a text field (or similar input) with that id then it will use that field
if there is a label that contains the text, then it will use the field that label for.
You're in the second case, so capybara uses the for attribute on the label to find the text field. This doesn't work because the label's for is user_First name but the id on the text field is user_first_name.
By default rails derives both the label text and the for attribute of the label from the value you pass as the first argument, so you should pass the attribute name as the first argument and then the human readable translation as the second argument
f.label :first_name, t(:first_name, scope: [:activerecord, :attributes, :user])
My reading of the documentation is that looking for this transaction would actually be the default - you may not need to specify it at all.

Sending POST request in Rails

How would I go about sending an HTTP POST request in the following format in a Rails setup:
curl https://api.venmo.com/v1/payments -d access_token=4e4sw1111111111t8an8dektggtcbb45 -d email="someemail#gmail.com" -d amount=5 -d note="Delivery."
I'd like to have users input the email/amount/note parameters into a form on a webpage, then pass that data (when the user clicks the submit button) into a Controller where the access_token parameter is stored to then fire off the POST request.
So far I've tried setting up the Controller with this method (and calling it from the view html.erb):
def make_payment
if !params[:access_token].nil?
form_data = {
"email" => #email_to,
"amount" => #amount_to,
"note" => #note_to,
"access_token" => :access_token
}
url = "https://api.venmo.com/v1/payments"
request = Net::HTTP::Post.new(url, form_data)
response = http.request(request)
end
end
Here's my current view setup:
<div class="box box-warning">
<div class="box-header">
<h3 class="box-title">Pay Bill using Venmo Account</h3>
<div id="payment_note_input">
<input id="payment_note" type="text" class="form-control" required placeholder="add a note to your payment">
</div>
<div id="payment_target_input" >
<input id="payment_target" type="text" class="form-control" required placeholder="pay an email address">
</div>
<div id="payment_amount_input">
<input id="payment_amount" type="text" class="form-control" required placeholder="enter the payment amount! ">
</div>
</br>
<button class="btn btn-danger" type="button" onClick= <%=make_payment%> > Make payment</button>
</div>
I feel like I'm close to a solution here...
You can use httpparty gem to achieve that, its easy to use in only 1 line:
response = HTTParty.post("https://example.com?param1=value1",
:body => {:text => data}.to_json,
:headers => {'Content-Type' => 'application/json'}
)
You can remove Body and Header if you don't have specific Body or Header,
and if you want to achieve Get request its even easier:
responce = HTTParty.get('http://example.com.json')
json=JSON.parse(responce.body) # in case you expect json responce
You need to use a form in order to generate a POST request from the web page. Rails provides you with Form helpers that would help you achieve this.
<div class="box box-warning">
<div class="box-header">
<%= form_tag make_payment_path do %>
<%= hidden_field_tag "access_token", #access_token %>
<h3 class="box-title">Pay Bill using Venmo Account</h3>
<div id="payment_note_input">
<%= text_field_tag "payment_note", nil, class: "form-control", placeholder: "add a note to your payment" %>
</div>
<div id="payment_target_input" >
<%= text_field_tag "payment_target", nil, class: "form-control", placeholder: "pay an email address" %>
</div>
<div id="payment_amount_input">
<%= text_field_tag "payment_amount",nil, class:"form-control", placeholder: "enter the payment amount! ">
</div>
</br>
<%= sumbit_tag "Make payment", class:"btn btn-danger" %>
<% end %>
</div>
And then you can access the form variables in your controller by...
def make_payment
access_token = params[:access_token]
note = params[:payment_note]
target = params[:payment_target]
...
end

rails form helper password security issue

This is the font-code.
<%= form_for(:something, :html=> {:id => 'login'}, :url => 'login/create') do |form| %>
<h1>Log In</h1>
<fieldset id="inputs">
<%= form.text_field :username, :placeholder => 'Username', :autofocus=>true%>
<%= password_field_tag(:password, :placeholder => 'Password') %>
<%= password_field_tag :userpass1, placeholder: 'Password'%>
<%= password_field_tag :userpass, params[:userpass], placeholder: 'Password', class: 'input-small'%>
</fieldset>
<fieldset id="actions">
<input type="submit" id="submit" value="Log in">
Register
</fieldset>
<%end%>
and if i submit, it sends like this,
Processing by LoginController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2RQKkdOZf2HL+dsY2peWYUIaY78WBZBvNveKKBvZv60=", "something"=>{"username
"=>"test"}, "password"=>"[FILTERED]", "userpass1"=>"test2", "userpass"=>"est3"}
in here, the first password field is sending with filtered, but the other two are just showing what the user typed.
what is the problem?
and the most big problem is, after i send this and check the request header,
it shows all the parameter, even the data what have been filetered like this,
Any good solution about this?!
You probably have line
config.filter_parameters += [:password]
in your config/application.rb. Modify it to:
config.filter_parameters += [:password, :userpass, :userpass1, :userpass2]
That causes filtering additional parameters in logs.
The second "problem" is not a problem at all. How do you suppose server to know what your parameters are if you want to filter them in request?

Resources