Rails project, testing with capybara a hidden field: Selenium::WebDriver::Error::ElementNotVisibleError: element not visible - capybara

(the other stackoverflow answers do not answer my question)
I have a form with a hidden_field_tag:
<form action="/score" method="post">
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
<%= hidden_field_tag :letters, #letters, id: "idgrid" %>
<input type="text" name="word" placeholder="type world">
<input type="submit" value="send">
</form>
I want to run following test with capybara (minitest)
require "application_system_test_case"
class GamesTest < ApplicationSystemTestCase
test "word in the grid but not english" do
visit new_url
fill_in "word", with: "car"
find('#idgrid', visible: false).set("N e w t e x t")
click_on "send"
assert_text "Sorry, can't build from grid"
end
end
I get following error
Error:
GamesTest#test_word_in_the_grid_but_not_english:
Selenium::WebDriver::Error::ElementNotVisibleError: element not visible
(Session info: headless chrome=68.0.3440.106)
(Driver info: chromedriver=2.41.578706 (5f725d1b4f0a4acbf5259df887244095596231db),platform=Mac OS X 10.13.6 x86_64)
test/system/games_test.rb:22:in `block in <class:GamesTest>'
I have read the other stackoverflow questions related to the topic, and not find the right answer. Also github issues. I understand that the element is not visible, but I have read that capybara can set the value, if I use the visible: false.
Could someone please help me?
Thanks

With Capybara you can find and inspect non-visible elements but you can't directly set their values because a normal user can't and therefore it doesn't really make a lot of sense to be directly changing them in feature/system tests. If you really need to change a hidden fields value you will need to do so via JS using something like
find('#idgrid', visible: false).execute_script("this.value = "N e w t e x t")
Since this doesn't replicate behavior a user would normally do, it's generally considered bad practice in feature/system tests and you really should think about whether there's better way of setting up those fields values that actually replicates a user (or if it makes more sense as a different type of test).

Related

Capybara and dropzone.js (Rails)

I am trying to follow the answers that I found elsewhere but for some reason, while there aren't any basic code errors, the following test fails anyway. if I look at the screenshot taken during the test, I can see that there is no "book cover" attached. So it looks like that the actual attachment of the jpg is not happening...? I am not quite sure how to go from here?
Test:
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'creating a book', type: :system do
scenario 'displays a cover image if there is one', js: true do
visit new_book_path
fill_in 'Book Title', with: 'Catcher in the Rye'
el = find('#book_cover', visible: false)
el.attach_file('db/sample/images/cover-1.jpg')
click_on 'Create Book'
expect(page).to have_css('div.cover-large img')
end
end
The expected code that I have on my webpage - confirmed in a dev environment is something like
<div class="cover-large">
<img src="...">
</div>
(and if there is no real image, the img tag is replaced with an svg tag that renders a dummy image).
EDIT: Here is the HTML source of the form in question around the #book_cover element:
<div
class="form-col col-wide dropzone dropzone-default dz-clickable"
data-controller="coverdrop"
data-coverdrop-max-file-size="3"
data-coverdrop-max-files="1"
>
<input data-coverdrop-target="input" data-direct-upload-url="http://127.0.0.1:3000/rails/active_storage/direct_uploads" type="file" name="book[cover]" id="book_cover" />
<div class="dropzone-msg dz-message">
<h3 class="dropzone-msg-title heading4 color-primary-dark">Drag cover image here or click to browse</h3>
<span class="dropzone-msg-desc small color-primary-dark">3 MB file size maximum. Allowed are only image file types.</span>
</div>
</div>
The corresponding CSS sets display=none for the input element with type=file.
I was able to get the upload to complete on dropzone's official website here: https://www.dropzone.dev/js/
image = './spec/fixtures/images/space_sloth.jpg'
page.attach_file(image) do
page.find('.dropzone').click
end
That works for me on the Dropzone site. Is there something in the DOM that you can click that invokes the Windows Explorer / MacOS Finder file chooser? You should use that element in the code block.
If you are working with a newer version of capabraya you can try:
find(".dropzone").drop(Rails.root.join("path/to/image"))

Rails 5 - Create Multiple Objects of 1 Model

I think what I would like to achieve is rather straightforward. I would like to have a form with 5 fields, and a person can create any number of objects from that form (i.e. If you fill in only 1 field, you create 1 object, if you field 2 fields, you create 2 objects etc...)
Interestingly, nothing much comes up when I attempt to google this topic. The only "proper" example I came across was this. Which leads me to where I'm currently at (cause it doesn't work).
Here's my foo_controller.rb
def new
#foo = []
5.times do
#foo << Foo.new
end
end
def create
if params.has_key?("foos")
Foo.create(foo_params(params["foos"]))
else
params["foos"].each do |f|
Foo.create(foo_params(f))
end
end
end
private
def foo_params(my_params)
my_params.permit(:item_one, :item_two)
end
And here's the new.html.erb
<%= form_tag foos_path do %>
<% #foo.each do |f| %>
<% fields_for "foos[]", f do |ff| %>
#all the form stuff, labels, buttons etc
<% end %>
<% end %>
<% end %>
Now before I even hit submit, I already "know" that there's one problem. My form fields all have the same ID, which means even "if" I could submit without any problems, only the last entry would end up hitting the database. So...
Problem #1
- How do I have unique form IDs?
The next issue I face is when I hit submit, I get this error Rack::QueryParser::ParameterTypeError (expected Array (got Rack::QueryParser::Params), so basically the params is not receiving the right thing. And that's my next problem...
Problem 2
- What's the proper way to be passing an Array of params?
But that is all assuming that THIS guide's solution works. I followed pretty much every step but came up empty. So i guess my big question/problem is:
How do i create One or Multiple objects of a single model?
Looks like you are headed in the right direction.
Problem 1: The IDs
You don't need IDs if you are creating new records. If you want to be able to edit the records with the same form it may be better to generate IDs, but that is more of a decision on how you want to handle create vs. update.
The way this works is Rails looks at the Name of the form elements. If you have empty brackets in the name Rails will render the params into an array:
<input type="text" name="foo[bar][][baz]" value="one" />
<input type="text" name="foo[bar][][baz]" value="two" />
<input type="text" name="foo[bar][][baz]" value="three" />
params => { foo: { bar: ["one", "two", "three"]}}
But, if you have IDs, Rails will render params into a hash:
<input type="text" name="foo[bar][1][baz]" value="one" />
<input type="text" name="foo[bar][2][baz]" value="two" />
<input type="text" name="foo[bar][3][baz]" value="three" />
params => { foo: { bar: {1: "one", 2: "two", 3: "three"}}}
Problem 3: Creating the models
Now the trick is how you handle the creation of the models. If you want to use nested attributes you can do that by setting up #accepts_nested_attributes_for and nesting everything under bar_attributes. This will let you just pass the params hash to the model and have it work some magic.
Alternatively you might want to iterate over the params hash and make each model right in your controller.
Or create a form object to contain all that logic. Up to you.
I would start with the manual iterate-over-params approach and get things working. Then you can refactor and clean things up.
This might look something like this in your controller:
array_of_bars = foo_params[:bar].map do |bar_params|
Bar.new(baz: bar_params[:baz])
end
Take a look at RailCast's Dynamic Forms and Nested Forms for more details. I revisit those guides numerous times over the last few years and have been happy with the resulting code every time.
Nested forms is always tricky but there are gems that can do that for you. One of my personal favourites would be this: https://github.com/nathanvda/cocoon
Here's a tutorial using the gem https://www.sitepoint.com/better-nested-attributes-in-rails-with-the-cocoon-gem/
I've been dealing with the same error Rack::QueryParser::ParameterTypeError (expected Array (got Rack::QueryParser::Params). This answer helped me to fix it.
The error is caused by the fact that the records with an ID render params into a hash. The solution: you don't need the fields_for. Removing the fields_for block and just using form tag helpers instead should work, such as:
text_field_tag "foos[][name]", f["name"]
With this approach, there will be no IDs that mess with your params. If you submit and inspect the params, you will see a nice array of hashes.

getting the input from radio buttons on rails 4 view

I am working on a new view for the app we're using (Rails 4.1.6 on Ruby 2.0.0). The model has an integer attribute, max_operations. This is a limit we set and then use.
It can be either a natural number or -9, which we we to represent no limit. On the _form.html.erb, I want it to look something like this:
[...]
max operations
⃝ unlimited
⃝ limit: □ (tiny square to represent a text box)
[...]
If the unlimited option is selected, the value passed for the max_operations would be -9 and if the limit option is selected, the number typed in the adjacent text box would be passed.
I also want the field to be properly shown when editing the record (selecting the proper radio, value shown inside the text box if needed).
I'm not sure where to begin with. Trying to imitate some code examples were a complete failure.
I did manage to produce this, which is probably the ugliest piece of code I've written in the last 15 years:
<input type="radio" id="operation_date_max_operations_unlimited" name="operation_date[max_operations]" <%= #operation_date.max_operations==-9?"checked=checked":"" %> value="-9"> <%=t("unlimited")%>
<br>
<input type="radio" id="operation_date_max_operations_limited" name="operation_date[max_operations]" <%= #operation_date.max_operations==-9?"":"checked=checked" %> value=<%= #operation_date.max_operations%> ><%=t("max_operations")%>:
<input type="text" id="max_operations_limit" name="operation_date[max_operations]" value=<%= #operation_date.max_operations==-9?"":#operation_date.max_operations %> >
it does show the things well but changing values does not work when saving, so I'd be glad to re-write the whole thing in a neat way.
I would like to keep it as much as I can within the erb code (<%=f.radio_button etc.), the less js the better. Any ideas?
First of all, having a special number to represent something is generally not a good idea.
That said, in order to stick with this approach but also make it more "neat", you can have a method in your OperationDate model that tells you if there is a limit or not:
def limited_operations?
(max_operations == -9)? true : false
end
Then, in your view, you can start bringing the radio buttons to your needs with the following:
<%= f.radio_button :max_operations, -9, { :checked => #operation_date.limited_operations? } %>
<%= f.radio_button :max_operations, #operation_date.max_operations, { :checked => #operation_date.limited_operations? } %>
<%= f.text_field :max_operations, :value => #operation_date.limited_operations? ? #operation_date.max_operations : "" %>
You can already see a danger here: The text field allows for any kind of values to be given..

Why isn't Capybara filling in these form elements?

I have been trying to set up Capybara to test a form but I keep getting the error
cannot fill in, no text field, text area or password field with id, name, or label 'Name' found
Here is what I have in my view:
<%= form_for(#user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
...
<%= f.submit "Create Account", class: "btn btn-large btn-primary" %>
<% end %>
which renders the following html:
<label for="user_name">Name</label>
<input id="user_name" name="user[name]" size="30" type="text" />
So it seems like it should be finding the field based on the label.
Here is what my user_pages_test.rb file has (I am using Test::Unit and shoulda-context):
context "sign up page" do
should "add user to database when fields are filled in" do
fill_in "Name", with: "Bubbles"
...
click_button "Create Account"
end
end
Here is what I've tried so far:
1) changing the call to fill_in to match the id with fill_in "user_name", with: "Bubbles"
2) changing the call to fill_in to page.fill_in "Name", with: "Bubbles" to match the example in the documentation
3) changing the view to manually add the id "Name" with <%= f.text_field :name, id: "Name" %> (this answer)
4) changing the call to get sign_up_path to get "/sign_up" (in case it was an issue with the routing)
All these still give me the same error, which makes me think that the page isn't being loaded correctly for some reason. However, I have a different (passing) test in the same context that asserts the page has the correct title, so I know the page does get loaded correctly (in the setup).
Based on this (and according to this answer), it seems like the problem might just be that the fill_in method isn't waiting for the page to load before trying to access the fields. According to this suggestion, I added the line puts page.body in my test to see that the HTML was being loaded completely before it was trying to fill in the fields, and got the following output:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
So, I was pretty sure that fill_in just wasn't waiting for the page to load. I then tried
5) changing the Capybara.default_wait_time according to this answer, but this had no effect. I tried setting it in the ActionDispatch::IntegrationTest class in the test_helper file (where Capybara is included), and also in the test itself.
Then I tried adding puts page.body in the passing test (both before and after it asserts the correct title), and I got the same output. THEN, I found this answer, and finally got the console to print out the page's HTML. I tried one more thing to get Capybara to fill in the fields:
6) changed the call to fill_in to say #response.fill_in, since #response seems to do what I thought the page variable was supposed to do.
So, I have two questions about this:
1) What does the page variable actually refer to? The doctype declaration for my app is just <!DOCTYPE html>, so I have no idea where it gets the old one from.
2) Why isn't Capybara able to find/fill_in these fields?
You need to use the visit method to get the page object setup properly. The capybara documentation indicates that '/' is visited by default and if you want to visit some other page you need to do an explicit call. It may also be helpful to read a bit about the difference between visit and get.

In rails, whether to use form helpers or not to?

In rails, is it recommended to use form helpers? Internally, everything boils down to plain html then why not write the html directly? Performance will obviously be better in writing direct html than using helpers. Is using form helpers like a convention or something that rails developers must follow?
Define performance. Your performance or the applications? Say you have the same rhtml snippet spread out across your views. Say you have it in thousands of places. Maybe you even haven't gotten it exactly the same in all places. Now your customer wants to change this (maybe different order of presentation or some such). It'll take you a while to do this in all the views, right? And chances are you won't get it right the first time. Chances are in fact that you'll keep getting bug reports for years to come on places you've missed to change.
The customer will end up paying a lot for that gained "performance". Maybe hundreds of working hours. Maybe tens of thousands if you avoid the DRY principle on principle. Think of all the servers and all the RAM she could buy for those work hours instead. If she spent it all on hardware her application might run hundred-folds faster. Think of all the fun things you could be working with instead of monkeying around changing html snippets.
I think that form helpers is a reflection of the DRY (don't repeat yourself) principle. Rather than writing the same code over to do similar tasks, creating a form helper that allows you to reuse that code is the way to go. That way if you need to make a change or fix, you only need to do it in one place. It also helps to make your code more compact and readable to abstract a complex action into a form helper. The same is true of partial views, though partial views tend to encapsulate more complex mark up than a form helper.
The form helpers are especially useful to let rails handle creating forms based on your model. To cite the example of the API documentation:
The following code
<% form_for :person, #person, :url => { :action => "create" } do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= submit_tag 'Create' %>
<% end %>
generates this html
<form action="/persons/create" method="post">
<input id="person_first_name" name="person[first_name]" size="30" type="text" />
<input id="person_last_name" name="person[last_name]" size="30" type="text" />
<input name="commit" type="submit" value="Create" />
</form>
You could write the html by yourself, but by using the form helpers you have to type less and make the form creation less dependent on the rails implementation. You always get a form that writes data into your model when you hit the submit button. If the rails developers ever change the implementation of this, you automatically get the correct html output from your helpers. If you had written the html manually, you would have to update all of it to reflect the changes of the inner workings of rails.
Its seems good when a developer having same name for class,id and no value for a input field if he needs different name id and also giving value then he have to write <%= text_field_tag " name", :value=>"value", :id=>"id" ,:class=>""class %> and for same html can be < input type ="text" value ="value" class="class" name ="name" id="id"/>
now think overhead 1. evaluate first helper into html 2. now also consider length of that in helper we also have to write : , =>
3. sometimes you forget to use : or , by mistake
so i think we prefer html in that case
And one thing if your server getting lots of request than it will got too busy and the response time will be increased because <%= %> should have to execute
This is a very old question but I am a newbee on rails. I would argue that instead of writing this
<label class="input-group__label" for="email">Email</label>
<input class="input-group__field" id="email" type="email" name="email" placeholder="e.g. me#something.com">
writing the same thing with rails helpers like this
<%= label_tag 'email', nil, class: 'input-group__label' %>
<%= email_field_tag "email", nil, placeholder: "e.g. me#something.com", class: "input-group__field" %>
in a form_tag is a very DRY way of doing things.
Readability is better in the first one.
Everybody can understand it. No rails knowledge is required.
Rails specific tags are not evaluated and converted (slight performance increase).
Amount of code is the same.
If that was inside a form_for which creates form from a model, I understand the use of rails specific tags and helpers. It makes sense. But I don't see a real advantage of using rails helpers for simple html as in the example above.

Resources