I am following Michael Hartl's Ruby on Rails Tutorial. When I use rspec/capybara, the fill_in method makes me confused. I have the following view code:
<%= f.label :name %>
<%= f.text_field :name %>
This is my testing code:
fill_in "Name", with: "Example User"
It seems that label and text_field are both required for fill_in to locate the input field. If I either take off f.label or change <%= f.text_field :name %> to be <%= f.text_field :another_name %>, the test will give me ElementNotFound error. Can anyone explain how the fill_in works here? Are input field and label both required for fill_in method?
It is stated that fill_in looks for field name, id or label text. According to ActionView::Helpers::FormHelper section of rails guides, the view code which you ask about should be translated to the following html code:
# I assume that you made a form for a #user object
<label for="user_name">
Name
</label>
<input id="user_name" name="user[name]" type="text" />
As you see, label produced the "Name" text, which you ask for inside of your fill_in expression. But id and name properties of input field are slightly different, so you should have been using id based selector to achieve the same result:
fill_in "user_name", with: 'Example User'
So, to sum up, label field is not required, but you should watch for your html code carefully and select the appropriate parameters for fill_in expression.
Just to add to what twonegatives posted. Here's what the capybara docs say for the fill_in() method:
The field can be found via its name [attribute], id [attribute,] or label text
http://rubydoc.info/github/jnicklas/capybara/Capybara/Node/Actions:fill_in
When I delete the 'Name' label, I can use any of the following and the tests still pass:
fill_in 'user_name', with: "Example User" #locate text field by id attribute
fill_in :user_name, with: "Example User" #locate text field by id attribute
fill_in 'user[name]', with: "Example User" #locate text field by name attribute
fill_in :'user[name]' with: "Example User" #locate text field by name attribute
In ruby, some characters cannot be used in a symbol name unless the whole symbol is quoted.
Capybara must be retrieving all the text fields (or text areas) from the page, then getting the values of the id and name attributes(easily done with something like Nokogiri) then checking if either value is equal to the first argument to fill_in() (after converting the first argument to a String via to_s()).
I provided my 2 cents here: Capybara not finding form elements.
If you call the Rails text_field helper with underscore (:first_name), the DOM gets rendered as "First name" and its what Capybara will need. No need for an id attribute.
View:
<%= form.text_field :first_name %>
Test:
fill_in "First name", with: 'Elaine'
Related
I'm trying to fill in an ActionText rich_text field form input, but can't figure out how to select it. I'm using Rails 6 and ActionText.
With
class Activity
has_rich_text :description
end
and _form.rb
= f.label :description
= f.rich_text_area :description, class: 'form-control'
Test using:
fill_in "Description", with: "Some description.."
I'll get the error
Capybara::ElementNotFound: Unable to find field "Description" that is not disabled
I suspect the problem is with how the Trix editor dynamically fills in this field as you type. I'm just not sure how to do the input, replicating how the user would be entering text.
You can do so by find the trix editor and entering your details manually:
find(".trix-content").set("New value")
Since Rails 6.1, you can use the fill_in_rich_text_area system test helper. Added in b2b6341374.
So, I'm basically trying to show Devise's confirm_within time in the error message that comes when clicking the confirmation email link after the required time.
I am using YAML translation files.
The interpolation variable is called devise_confirm_within
My YAML is like this:
en:
activerecord:
errors:
models:
user:
attributes:
email:
confirmation_period_expired: "some text %{devise_confirm_within}"
Normally, I would find the appropriate view, and then pass the interpolation variable as a parameter to the translate or t method. Like this:
<p><%= t("devise.mailer.confirmation_instructions.please_click_the_below_link_to_confirm_your_new_email", :devise_confirm_within => distance_of_time_in_words(0, User.confirm_within, :locale => I18n.locale)) %></p>
However, there is no view that has the confirmation_period_expired translation key, so I can't do it in this same manner.
I have a view with:
<div class="form-inputs">
<%= f.input :email, :required => true %>
</div>
And a user model with:
validates :email, uniqueness: true
I want to keep all the translation text in the YAML file, so adding :message to the validates field won't do.
So how can I get the devise_confirm_within time into that confirmation_period_expired error message?
PS: I am using the simple_form gem for forms, and also the dotiw gem for overriding the default distance_of_time_in_words method (this doesn't influence this problem I'm having).
The problem was circumvented when I added this to devise.en.yml and similar to the other devise..yml files:
en:
errors:
messages:
confirmation_period_expired: "some text %{period}"
I however wasn't able to pass a translation interpolation variable to a failed form validation (i.e. add a variable to a devise message), of which I am still curious.
I have checked several posts here and none of them seemed to work for me. I don't know what I am missing.
The ruby documentation here http://apidock.com/rails/ActionView/Helpers/FormHelper/label, seems to have the answer, but it is not clear how to interpret some text. For eg.,
helpers:
label:
post:
body: "Write your entire text here"
Is this the text one should put in the en.yml file as-is (replacing the post with my own model name and body with my own field name etc?) I tried that and it didn't work. Then I tried to put this text in the app/helpers/"modelname"_helper.rb file. Didn't work there either.
helpers:
label:
representative:
fname: "First Name"
After these edits, in my view I have the code as follows:
<%= f.label :fieldname %>
In my case:
<%= f.label :fname %>
At this point when I run my app, I am expecting my custom label that to be shown on the form. It doesn't. It just shows Fname
When I see similar posts on stackoverflow even there I see the same convention being used. Looks like I have two issues going on. 1) Understanding this convention and how to interpret it and 2) The solution to my actual issue itself.
I know I can use <%= f.label :fname, 'First Name' %>, but because I am validating for fname in the model for presence, the error message says "Fname is required". I would like it to say "First Name" is required.
How else can I do this?
Please help.
If you want to translate the names of your model attributes, put something like the following in config/i18n/en.yml:
activerecord:
attribute:
representative:
fname: "First name"
body: "Write something here"
Error messages for specific attributes and specific errors can be overriden too:
activerecord:
errors:
models:
representative:
attributes:
body:
blank: "Body cannot be blank"
But I think your best option is to create a new translatable for the custom body label. In your form:
= f.label :body, t(:write_text)
In your en.yml:
write_text: "Write something here"
While I was writing cucumber test code,
I get:
Unable to find field "username" (Capybara::ElementNotFound)
But I have the following on the page itself.
<%= f.text_field :username, :placeholder => "Username" %>
I've checked that it lands on the correct page using
save_and_open_page
fill_in "username", :with => "TESTUSER"
isn't the tag :username? What am I supposed to write instead?
Capybara will match fields based on their id, name or label text. See here for more details.
Since you are using the default text_field helper, id and name will default to include the model (e.g. user_username for the id, or user[username] for the name). You can change these defaults by simply using id: <id> or name: <name> on your text_field call but you might run into other problems later - so probably best to stay with the defaults.
Change your capybara test to fill_in "user_username" or fill_in "user[username]" to make it match. Alternatively, if you have a label_for on the field, you can match on the text of the label which can make your tests more readable.
How do you test for a text form field that has no value such as in the following case:
<input class="string required" id="student_name" name="student[name]"
size="50" type="text" />
I know if a value is present you would have:
<input class="string required" id="student_name" name="student[name]"
size="50" type="text" value="John" />
and you can to test to make sure the form field is pre-populated with the name "John" with the following:
it "should display an enmpty field for the student name" do
rendered.should have_selector("input", :type => "text",
:name => "student[name]",
:value => "John")
end
I tried specifying ":value => nil" but that didn't work.
Documentation on have_selector() is pretty slim so haven't been able to find anything there and have not been able to find any examples via searches (likely searching with the wrong terms).
As per suggestion I also tried :value => '' but get the following error message:
expected following output to contain a <input type='text' name='vendor[name]' value=''/>
When I tried :value => nil I got the following error message:
NoMethodError:
undefined method `include?' for nil:NilClass
Answer
The solution was to use either :value => "" or :value => "".
The reason it did not initially work in my case was that I had included both the webrat and capybara gems in my Gemfile. After removing webrat it worked using both solutions.
try specifying the value as ""
it "should display an enmpty field for the student name" do
rendered.should have_selector("input", :type => "text",
:name => "student[name]",
:value => "")
end
What #Unixmonkey said in the comment should work. You can also do:
page.first(:css, "#student_name")[:value].should be_nil
Edit Oh I guess #Unimonkey's comment didn't work according to your update, but the above will work - I've tested it out.
Edit 2 Re: Comments Try this instead since you're in a view spec:
selector = css_select("#student_name").first
selector["value"].should be_nil