How to test a file attach with rspec in a rails project? - ruby-on-rails

I put a file upload tag in my view source:
<%= form_tag(confirm_path, method: :post, multipart: true) do %>
<%= file_field_tag 'my_file' %>
<button type='submit'>Submit</button>
<% end %>
I want to test if user select a file and click submit button, then redirect to next page. Here is my spec source:
require 'rails_helper'
feature 'MyTest', type: :feature do
describe 'File test' do
before do
visit index_path
end
it 'If select a file, then go to next page' do
attach_file 'my_file', my_real_file_path # I am sure this file exists
click_button 'Submit'
expect(page).to have_content('Confirm Page')
end
end
end
But after I run this spec test, it said:
Failure/Error: attach_file 'my_file', my_real_file_path
Capybara::ElementNotFound:
Unable to find file field 'my_file'

This is usually because the actual file field is being hidden and overlayed with a "button" or some JavaScript driven interface so it can be styled to look the same across multiple browsers. If this is the case here you generally need to execute some JavaScript, using execute_script, to override whatever css is hiding the file field so it becomes visible on the screen and then call attach_file

Related

RSpec/Capybara can't find link to image

I want to use RSpec and Capybara to simulate clicking on an image, but I cannot do it.
My error is this:
Failure/Error: click_on "icon_red_heart.png"
Capybara::ElementNotFound:
Unable to find visible css "#image-button"
spec/features/posts_spec.rb
scenario "push an image" do
visit posts_path
expect(page).to have_current_path(posts_path)
find('#image-button').click
end
likes/_likes.html.erb
<%= button_to post_like_path(post, like), id: "image-button",method: :delete, remote: true do %>
<%= image_tag("icon_red_heart.png")%>
I don't know how to how to specify the image.
Please teach me.
As #jonrsharpe indicated, you are not referencing the button properly. The most resilient way to reference an element is to give it an id and reference it by that id.
Also, the button created by button_to may have no content, in which case you'll need to tell Capybara that the button is not visible.
Change the button_to line to this:
<%= button_to post_like_path(post, like), id: "image-button", remote: true do %>
Then change your test to read as follows:
scenario "push an image" do
visit posts_path # Use the name of your path here
find('#image-button', visible: false).click
end
Incidentally, using method: :delete in your button_to link doesn't do what you would expect. The method is automatically set to POST, which is presumably what you want.

Capybara failed to find position for element

I'm using Capybara with a webkit driver and when I'm running tests with js: true it raises error listed below. When I do same things in other tests without js: true everything works fine.
PS. There is no need for js :true in this test. This code is actually inside a helper but I put it here like test , so it will be easier to understand.I'm using js: true in the other test that invokes this helper method.
Code below raises Capybara::Webkit::ClickFailed:
Failed to find position for element /html/body/div/div/div/div[2]/a[12]
scenario "adding logged days", js: true do
visit '/logged_days'
find(:xpath, "//a[contains(.,'12')]").click
# click_link("12") raises same error
expect(current_path).to eq("/logged_days/new")
fill_in "Опис виконаної роботи", with: "Some description"
fill_in "Кількість відпрацьованих годин", with: 40
click_button "Додати"
expect(current_path).to eq("/logged_days")
expect(page).to have_content("40")
end
/logged_days:
<div class="page-header">
<h2>Logged Days <small>March</small></h2>
</div>
<div class="conteiner-fluid logged_days_container">
<% for i in 1..31 %>
<%= link_to new_logged_day_path(:cal_date => "#{i}"), method: :get do %>
<div class="calendar_cell">
<p class="cell_date"><%= i %></p>
<p class= "cell_text"></p>
</div>
<% end %>
<% end %>
</div>
I think I had this error exactly once in all my years and it was about an element being covered or otherwise not being clickable.
A thorough investigation with your browser's webinspector is necessary here: You have to figure what your js does to that click target.
When using js:true your page not only runs JS but also has CSS processed. This means you can end up with elements that are are non-visible, overlapped, or moving. You need to look at what is done to the element in a real browser and make sure the element is actually clickable, or what other actions a user would have to do first to make it clickable.
Secondly, don't use .eq with current_path - it'll lead to flaky tests as you use js capable drivers. Instead use the has_current_path matcher
expect(page).to have_current_path('/logged_days/new')

What causes the failure to click buttons that are not in forms when using capybara?

I have searched this for some good time in SO, and on Google. I am a newbie(only 7 months in development), but I'd like an explanation as to why capybara's click_button does not respond to buttons that are not in forms
take and example of this
<div id="btn-div">
<%= button_tag('Attach new file', id: "attach-file-btn", class: "btn btn-info") %>
</div>
in this case when you are writing tests you get an error of the type
undefined method `node_name' for nil:NilClass
however when you put this in a form like this it works
<form id="btn-form">
<%= button_tag('Attach new file', id: "attach-file-btn", class: "btn btn-info") %>
</form>
if you may, please provide an explanation as to why this happens.
and take the following into consideration
the button is not submitting anything, it is only causing JavaScript to show a form
when I try to find the button by using xpath or find it is available, however click_button does not work for it
update
code for click button is similar to this:
describe "Getting to the deployable tools page" do
before do
#tool = FactoryGirl.create(:tool)
#tool.set_status(1)
end
before { visit assigned_tools_path }
before { click_button('attach-file-btn') }
it { should have_content('select tools to add') }
it { should have_css('#non-successful-tool-adding') }
end
Try using a capybara finder, and then invoke click on it, like so:
page.find('#attach-file-btn').click
If you just want to click the file show at broswer with text Attach new file, use:
click_button 'Attach new file'

Capybara choose("radio button") not working

A snapshot of my view:
<%= form_for #request do |f| %>
<div class="form-group">
<%= f.radio_button(:item, "Snow/waterproof shell (upper)") %>
<%= f.label(:item, "Snow/waterproof shell (upper)") %>
</br>
<%= f.radio_button(:item, "Headlamp") %>
<%= f.label(:item, "Headlamp") %>
</div>
Yet on my Rspec integration test file (spec/requests/requests_spec.rb), when I write (note the choose radio button is a part of the form where the user requests an item from a list, and the test is for the resulting page after submission, which should indicate the item that the user requested). I'm using gem 'rspec-rails', '2.13.1'
describe "Requests" do
subject { page }
describe "new request" do
before { visit root_path }
describe "with valid information" do
before do
choose("Snow/waterproof shell (upper)")
click_button submit
end
it { should have_content("Snow/waterproof shell (upper)")
end
end
end
I always get the error:
←[31mFailure/Error:←[0m ←[31mchoose("Snow/waterproof shell (upper)")←[0m
←[31mCapybara::ElementNotFound←[0m:
←[31mUnable to find radio button "Snow/waterproof shell (upper)"←[0m
←[36m # ./spec/requests/requests_spec.rb:24:in `block (4 levels) in <top (required)>'←[0m
Same if I try with choose("Headlamp") or any other option. Any thoughts smart people? This seemed like something that would be so easy...
I've had this issue a number of times. If you choose form elements based on their ID in the dom it's far more reliable:
before do
choose 'request_item_headlamp'
click_button submit
end
I can't tell without looking what ID rails would come up with for the other radio button. Just right click it in chrome, inspect element, and cut and paste the element ID into your test.
I suspect sometimes when choose doesn't reliably check a radio button, it may be because an animation is in progress.
If you suspect this is causing your choose calls to be unreliable, try disabling animations, say by setting their execution time to 0 seconds in your test environment. Alternatively build in a wait period in your test for the animation to finish.

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.

Resources