I need some help here,
I'm using the date_select helper from Rails in a form to get a date in the format %d/%m/%Y. The helper seems to work fine in development but it doesn't in test mode.
In test mode, I got failure/error when a failed POST is made and the test needs to re-render the view. I got the follow failure/error message:
Failure/Error: <%= f.date_select :birthdate,
ActionView::Template::Error:
undefined method `year' for "{3=>15, 2=>6, 1=>2001}":String
or, depending on how I format the date
ActionView::Template::Error:
undefined method `year' for "15/06/2001":String
It only happens with failed posts (when is needed to re-render the form), both for rspec request and feature tests. In development mode, it works fine to re-render de form.
My tests
# feature test
it "sign up with false credential" do
users = FactoryBot.attributes_for(:users)
visit 'users/sign_up'
fill_in 'user_name', :with => "test"
find_by_id('user_birthdate_3i').find("option[value='15']").select_option
find_by_id('user_birthdate_2i').find("option[value='6']").select_option
find_by_id('user_birthdate_1i').find("option[value='2001']").select_option
click_button('Save')
expect(page).to have_selector "#error_explanation"
end
And
# request test
it "signs usuario_candidato up with wrong attributes" do
users = FactoryBot.attributes_for(:users)
expect{ post user_registration_path,
:params => {
"user" => {
"name" => user[:name],
"birthdate" => "2018/12/31"
}
}
}.to change{ User.count }.by(0)
end
The form
<%= f.date_select :birthdate,
options = {
start_year: Date.current.year,
end_year: 1950,
order: [:day, :month, :year],
prompt: { day: 'day', month: 'month', year: 'year' }
}
%>
Some observations:
The same test format works for feature and request when the is a
successful post.
I'm using locale PT-BR, with default format to %d/%m/Y%
I'm using Postgres, with birthdate as Date type.
I'm using Devise controller to this form, and I didn't override the controller's actions.
As I already said, development mode don't reproduce this error.
I already try different date format (e.g %m/%d%/%Y or year/month/day) before posting, it always return the same error. Because of that, my suggestion to the problem is: after the post fails, the return is a String when it should be a Date class.
Anyone already face this problem?
Any additional information, just ask on comments.
Thanks in advance!
UPDATE
After 6 days of posting, I was trying a suggestion someone gave on comments, and it happens that the tests passed, no error was raised. I didn't make the suggestion, I just change from a text_field to the date_field (as I wasn't able to use date_field, I changed to text_field and moved on to the next problem).
I found a certain inconsistency at how Rails works with date. Before the first raise of the problem, it was passing the tests normally. Then the error happened, now it's not happen anymore.
I have similar problem with a Regex that I use in my model for validate dates:
Model
# dd/mm/yyyy
VALID_DATE = /\A[0-9]{2}\/[0-9]{2}\/[0-9]{4}\z/
validates :birthdate, presence: true, format: { with: VALID_DATE, message: "must be in the format DD/MM/YYYY" }
When I create the validation, it works fine. Values in the format yyyy-mm-dd used not pass in model tests. In the next day, Rspec started to raise errors saying that yyyy-mm-dd and others should be valid (therefore, ignoring the regex validation)
Model test error
User birthdate should be invalid
Failure/Error: expect(user).to_not be_valid
expected #<User id: nil, name: "Sra. Margarida Sophie Santana", birthdate: "2000-12-29", created_at: nil, updated_at: nil> not to be valid
The only thing I have extra for date is a date_formats.rb in config/initializer with the follow value:
Date::DATE_FORMATS[:default] = "%d/%m/%Y"
I have it for a long time.
Apparently Rails changes when it decides to transform string values in date values (when it was passing the regex validation test, it was passing the date as string, when the problem raise up the date was been transformed from string to date before the validation).
Related
I am using the bootstrap-datepicker-rails gem for my app and have created a form where users can pick a start date and an end date.
When the user edits the form I want them to be able to see the dates that they had previously chosen. (e.g. Start: October 31, 2015, End: November 15, 2015).
I am trying to write a test to make sure my app does this, but I'm stuck on the syntax.
user_updates_project_spec.rb
require "rails_helper"
feature "User updates project" do
before (:each) do
#user = create(:user)
login_as(#user, scope: :user)
#project = create(:project, creator: #user,
name: "Building a robot",
start_at: "2015-10-31",
end_at: "2015-11-15", )
end
scenario "and sees correct dates on form" do
visit edit_user_project_path(#user, #project)
expect(".datepicker").to have_content("October 31, 2015")
end
end
The error I get is:
Failure/Error: expect(".datepicker").to have_content("October 31, 2015")
expected to find text "October 31, 2015" in ".datepicker"
This is what my form looks like:
<div class="col-sm-6 pl0">
<%= f.input :start_at, label: "Start Date", as: :string,
input_html: {value:#project.set_start_date_for_form.to_s(:long),
class: "datepicker start-date"} %>
</div>
<div class="col-sm-6 pr0">
<%= f.input :end_at, label: "End Date", as: :string,
input_html: {value: #project.set_end_date_for_form.to_s(:long),
class: "datepicker end-date"} %>
</div>
Any ideas on why it's not working?
The way you've written your expects, what you're really saying is expect the string ".datepicker" to have the content "October 31, 2015" which is never going to be true. expect needs to take an element that the have_content matcher can be run against rather than a string. You could rewrite it like this
expect(page.find(:css, '.datepicker.start-date')).to have_content("October 31, 2015")
although a better method would probably be to use
expect(page).to have_css('.datepicker', text: 'October 31, 2015)
since that will match if the content is changing or being loaded via ajax.
One other thing to note is that since you're using a datepicker widget you might actually need to look at the html that is produced by the JS in a real browser, instead of the html in your view tempalte, to determine exactly which element you need to be checking for the content inside.
I guess there may be a confusing for .start-date and .end-date also you can use .text attribute to check them:
expect(page.find(:css, '.datepicker.start-date').text).to eq('October 31, 2015')
expect(page.find(:css, '.datepicker.end-date').text).to eq('November 15, 2015')
If it fails, you can print the value to debug it:
puts page.find(:css, '.datepicker.start-date').text
It turns out I wasn't checking inside the correct element when testing.
What solved it was:
expect(page).to have_css("#project_start_at[value='October 31, 2015']")
expect(page).to have_css("#project_end_at[value='November 15, 2015']")
The element id's that datepicker set were project_start_at and project_end_at.
I want to validate European date formats like "10.02.2012" or "10-02-2012" in my model.
Therefore I am using this line:
validates_format_of :matchdate, :with => /\A\d{2}(\.|-)\d{2}(\.|-)\d{4}\Z/i, :message => "Date incorrect"
The regex must be right, but whatever I type in, it always returns "Date incorrect".
I get the input from a form where I use
<%= f.text_field :matchdate, :size => '10' %>
together with the Datepicker UI, that outputs the right format. (I want to use both "." and "-" seperators, cause I collect the date in another scope, where both versions should be allowed.)
The params look correct as well, there it says: "...matchdate"=>"29.09.2012"
What am I doing wrong?
When you initiate new instance of your model from params, Rails casts your date into Date object (example using my own model):
i = Invoice.new due_date: "29.09.2012"
i.due_date #=> Sat, 29 Sep 2012
i.due_date.class #=> Date
But you can't validate Date with a Regex. What you actually need is
validates :matchdate, presence: true
because it will be nil if Rails can't parse it, and maybe add some custom validators if needed.
I have a number of standard rails validations within my model:
validates_presence_of :url_string
validates_uniqueness_of :url_string
validates_presence_of :stream_source
validates_presence_of :width
validates_presence_of :height
validates_presence_of :name
validates_uniqueness_of :name
validates_presence_of :customer_name
validates_presence_of :iframe_background_color
If I don't fill out one of these fields within my form then I am taken back to the form as expected but the odd thing is no error messages are displayed. I am using the code below to display the error messages:
<% #camera.errors.full_messages.each do |error| %>
<p><%= error %></p>
<% end %
I also attempted to print out the #camera.errors object and this is what is shown:
#<ActiveModel::Errors:0x12db19bc #base=#<Camera id: 1, stream_source: "test", width: 640, height: 360, active: true, name: "test", url_string: "CAYD19Vp", customer_name: "test", iframe_background_color: "#FFFFFF", online: true, created_at: "2011-08-30 15:54:16", updated_at: "2011-09-06 15:52:48", audio: true, iframe_text_color: "#FF00FF", iframe_link_color: "#FF0000", notes: "Some notes!", offline_image_file_name: "Cake.jpg", offline_image_content_type: "image/jpeg", offline_image_file_size: 196591, offline_image_updated_at: "2011-09-06 12:12:38", pull_stream_url: "test", bitrate: "300-500", show_branding: false>, #messages={}>
#
As you can see the messages hash is empty. I tried setting the validation error message manually by doing the following:
validates_presence_of :name, :message => "No name present"
but it did not populate the messages hash either.
Controller update action is shown below:
def update
#camera = Camera.find(params[:id])
if #camera.update_attributes(params[:camera])
flash[:notice] = "Camera updated"
redirect_to nwcadmin_camera_path
else
redirect_to :action => :edit
end
end
I am using Ruby version ruby 1.9.2p290 and Rails version 3.1.0.
Any assistance would be great!
Thanks
Just a heads up that you'll get a Validation failed (ActiveRecord::RecordInvalid) error with an empty error message (if there are no other errors) when you have before_validation declarations and any of them returns false.
Note that before_validation callbacks must not return false (nil is okay) and this can happen by accident, e.g., if you are assigning false to a boolean attribute in the last line inside that callback method. Explicitly write return true in your callback methods to make this work (or just true at the end if your callback is a block (as noted here)).
UPDATE: This will no longer be an issue starting Rails 5.0, as return false will no longer halt the callback chain (throw :abort will now halt the callback chain).
UPDATE: You might also receive ActiveRecord::RecordNotSaved: Failed to save the record if a callback returns false.
I managed to get to the bottom of my problem. In the controller I was using:
redirect_to :action => :edit
I should have been using:
render :action => :edit
By using redirect_to I was hitting the edit action within the controller which was then getting a new camera object from the database rather than preserving the current camera object from the update action.
Unless you call #camera.save or #camera.valid?, the errors hash will not be populated with the validation errors. Please check your controller code.
You can use flash[:message] or flash[:notice] in controller code to store the error message, which can be used in view to display the errors.Link Have a look in the link,it's clearly explained, how to append the error messages and use them to display.The instance variable doestnot contains any errors as no validation runs in update.
You can use #camera_errors = #camera.save to collect the errors and then
<% #camera_errors.errors.full_messages.each do |error| %>
<p><%= error %></p>
<% end %>
I'm not sure if this is something that you might be interested in or not, but you can use this official Rails gem: dynamic_form
This gem provides you two helper methods: error_messages and error_messages_for
Refer to the following Rails guide for more: http://guides.rubyonrails.org/active_record_validations_callbacks.html#displaying-validation-errors-in-the-view
I'm writing a spec for a controller in Rails 3 project using RSpec and Capybara, and I want to select current date from a select box. I tried:
select Date.today, :from => 'Date of birth'
but the spec fails and I get error:
Failure/Error: select Date.today, :from => 'Date of birth'
NoMethodError:
undefined method `to_xpath' for Mon, 18 Jul 2011:Date
How to fix it?
P.S. In view file I use simple_form_for tag and the select box is generated by code:
f.input :date_of_birth
Had the same problem. I googled at lot and solved it this way:
Wrote date select macros into /spec/request_macros.rb
The select_by_id method is necessary for me, because the month is dependent on the translation
module RequestMacros
def select_by_id(id, options = {})
field = options[:from]
option_xpath = "//*[#id='#{field}']/option[#{id}]"
option_text = find(:xpath, option_xpath).text
select option_text, :from => field
end
def select_date(date, options = {})
field = options[:from]
select date.year.to_s, :from => "#{field}_1i"
select_by_id date.month, :from => "#{field}_2i"
select date.day.to_s, :from => "#{field}_3i"
end
end
Added them to my /spec/spec_helper.rb
config.include RequestMacros, :type => :request
Now in my integration tests in spec/requests i can use
select_date attr[:birthday], :from => "user_birthday"
Thanks to http://jasonneylon.wordpress.com/2011/02/16/selecting-from-a-dropdown-generically-with-capybara/ and https://gist.github.com/558786 :)
You need to specify the exact value as it's in the select menu in html. So if your select has values like "2011/01/01" then you need to write:
select '2011/01/01', :from => 'Date of birth'
Your code fails because you pass a date object.
with credit to Markus Hartmair for an excellent solution, I prefer to use labels as selectors because of improved readability. So my version of his helper module is:
module SelectDateHelper
def select_date(date, options = {})
field = options[:from]
base_id = find(:xpath, ".//label[contains(.,'#{field}')]")[:for]
year, month, day = date.split(',')
select year, :from => "#{base_id}_1i"
select month, :from => "#{base_id}_2i"
select day, :from => "#{base_id}_3i"
end
end
call it like this:
select_date "2012,Jan,1", :from => "From date"
I found a clean solution for rspec and capybara to test using date and time select methods, where in your HTML you use a datetime select or date select. This works with Rails 4, RSpec 3.1 and Capybara 2.4.4.
Say in your HTML form you have the following:
<%= f.datetime_select(:start_date, {default: DateTime.now, prompt: {day: 'Choose day', month: "Choose month", year: "Choose year"}}, {class: "date-select"}) %>
the DateTime Select View helper will create 5 select fields with ids such as id="modelname_start_date_1i", where the id the is appended with 1i, 2i, 3i, 4i, 5i. By default Year, Month, Day, Hour, Minute. If you change the order of the fields, make sure to change the feature helper below.
1) Create a Feature Helper for dates and times helpers
spec/support/helpers/date_time_select_helpers.rb
module Features
module DateTimeSelectHelpers
def select_date_and_time(date, options = {})
field = options[:from]
select date.strftime('%Y'), :from => "#{field}_1i" #year
select date.strftime('%B'), :from => "#{field}_2i" #month
select date.strftime('%-d'), :from => "#{field}_3i" #day
select date.strftime('%H'), :from => "#{field}_4i" #hour
select date.strftime('%M'), :from => "#{field}_5i" #minute
end
def select_date(date, options = {})
field = options[:from]
select date.strftime('%Y'), :from => "#{field}_1i" #year
select date.strftime('%B'), :from => "#{field}_2i" #month
select date.strftime('%-d'), :from => "#{field}_3i" #day
end
end
end
Note that for the day I use %-d that gives you a non-padded numeric value (i.e. 4) instead of %d that has a zero-padded numeric value (i.e. 04). Check the date formats with strftime
2) You then need to include your date and time helpers methods in spec/support/helpers.rb so you can use them in any spec file.
require 'support/helpers/date_time_select_helpers'
RSpec.configure do |config|
config.include Features::DateTimeSelectHelpers, type: :feature
end
3) In your Spec file you can call your helper. For example:
feature 'New Post' do
scenario 'Add a post' do
visit new_post_path
fill_in "post[name]", with: "My post"
select_date_and_time(2.days.from_now, from:"post_start_date")
click_button "Submit"
expect(page).to have_content "Your post was successfully saved"
end
end
A slight adaption of Markus's answer:
def select_date(date, options = {})
raise ArgumentError, 'from is a required option' if options[:from].blank?
field = options[:from].to_s
select date.year.to_s, :from => "#{field}_1i"
select Date::MONTHNAMES[date.month], :from => "#{field}_2i"
select date.day.to_s, :from => "#{field}_3i"
end
Thanks to Dylan for pointing it out, but in case anyone is looking for the cucumber version, you can use this:
select_date("Date of birth", :with => "1/1/2011")
For more information, see select_date.
Given the following Formtastic code renders Rails default date selector:
= f.input :born_on, end_year: Time.now.year, start_year: 60.years.ago.year
In your spec, break the date into separate calls to each individual select tag:
select '1956', from: 'person_born_on_1i'
select 'July', from: 'person_born_on_2i'
select '9', from: 'person_born_on_3i'
I don't like that this code is so aware of the HTML, but it does work with the versions of gems at this time.
Gems:
Capybara 2.1.0
Formtastic 2.2.1
Rails 3.2.13
RSpec 2.13.0
In my particular situation, I'm adding potentially multiple date select fields to the page with accepts_nested_attributes_for functionality. This means, I'm not sure what the full id or name of the fields are going to be.
Here's the solution I came up with in case it helps anyone else Googling this:
I'm wrapping the date select field in a container div with a class:
<div class='date-of-birth-container'>
<%= f.date_select :date_of_birth %>
</div>
Then in my feature spec:
within '.date-of-birth-container' do
find("option[value='1']", text: 'January').select_option
find("option[value='1']", text: '1').select_option
find("option[value='1955']").select_option
end
Here's a helper method I wrote for it:
def select_date_within_css_selector(date, css_selector)
month_name = Date::MONTHNAMES.fetch(date.month)
within css_selector do
find("option[value='#{date.month}']", text: month_name).select_option
find("option[value='#{date.day}']", text: date.day.to_s).select_option
find("option[value='#{date.year}']").select_option
end
end
Then using the helper:
select_date_within_css_selector(Date.new(1955, 1, 1), '.date-of-birth-container')
The following worked for me, using a date_field:
fill_in "Date", with: DateTime.now.strftime('%m/%d/%Y')
For Rails 4, in case somebody gets to this question without being limited to Rails 3.
select '2020', from: 'field_name_{}_1i'
select 'January', from: 'field_name_{}_2i'
select '1', from: 'field_name_{}_3i'
You can of course extract this to a helper and make it dynamic.
It looks like this one has been sufficiently covered, but see Capybara's docs for an official answer. You can select by name, id, or label text.
My simple date validation regex is not working correctly...
validates_format_of :dob, :with => /\d{2}\/\d{2}\/\d{4}/, :message => "^Date must be in the following format: mm/dd/yyyy"
What am I missing here? I'm trying to validate that a date is in the following format: mm/dd/yyyy - When I enter what should be valid data, I still get the error message.
Thanks for the help so far. Here's a snippet of code from my form that is passing the dob value in:
<tr>
<td>
<%= f.label :dob, "Date of Birth: " %>
</td>
<td>
<%= calendar_date_select_tag "user[dob]", "", :format => :american, :year_range => 50.years.ago..0.years.ago %>
</td>
</tr>
I think it may have something to do with my use of this js calendar plugin. A related problem is that my dob value is not maintained in the field if the post fails validation - the previously entered date value clears out...
Thanks!
Tom
You are probably using a string field to store a date. Consequently, any helpers that expect a DateTime or Time value will not work properly. You will need to investigate multiparameter assignments in Rails to figure out the proper way to do what you want to do (multiparemeter assignments is the magic behind sending 4 fields to Rails and get them converted to a DateTime or Time object).
In other words: if you are using a true DateTime field (as you should for this case) the validates_format_of will not have any effect (or will have adverse effects)
Found an excellent gem / plugin for all your date / time validations.
validates_timeliness
http://github.com/adzap/validates_timeliness
I've not tested your code within a Rails app, but your regular expression looks good to me. Try this test program:
#!/usr/bin/ruby
str = '08/24/2009'
regex = /\d{2}\/\d{2}\/\d{4}/
if str =~ regex
print 'matched', "\n"
else
print 'did not match', "\n"
end
Your regular expression matches. That suggests the problem is elsewhere.
You also might think about trying more general solutions for parsing a date in Ruby, such as the Date.parse method of the Date class. This does a little bit more validation than your regular expression, and it also provides some useful methods for converting dates between different formats.
Ahhh! Forcing date formats on the end-user when Rails implicitly converts them is a bad thing for usability, along with being more work for you (as you've seen).
If you've got a date/time attribute in your model, Rails will do its best to convert via Date.Parse (http://www.ruby-doc.org/core/classes/Date.html#M000644), e.g.
u = Somemodel.find :first
u.created_at
=> Tue Nov 20 15:44:18 -0500 2007
u.created_at = '2008/07/03'
=> "2008/07/03"
u.created_at
=> Thu Jul 03 00:00:00 -0400 2008
u.created_at = '05/10/1980'
=> "05/10/1980"
u.created_at
=> Sat May 10 00:00:00 -0400 1980
The regex looks correct to me. A useful resource is http://www.rubyxp.com/, which will test your regular expression against a given string. Indeed, the regex you have matches a date I typed into rubyxp.
Perhaps there's an issue in getting the entered data -- any chance the field is really called da?
Another item you may find useful: validates_timeliness, a rails plugin to validate dates and times. Why just validate the date format when you can check if it's a real date -- after all, 99/99/9999 will validate against your regex, but you may not really want to accept that.
You can try using current date to convert it into a format something like his:
def validate_date_format(value)
current_date = DateTime.now
begin
DateTime.strptime(current_date, value)
rescue
raise 'Invalid Format'
end
end
You might also want to look into the Chronic Gem (http://chronic.rubyforge.org/) which does natural date parsing. So you can enter in :
Next tuesday at 9pm
And it will interpret it correctly.