having trouble testing upload file with Capybara attach_file method - ruby-on-rails

I'm having trouble trying to test whether image upload works with paperclip using Capybara's attach_file method.
rspec returns these errors below:
Failures:
1) Upload Background Image cannot upload a background image
Failure/Error: page.attach_file('#artist_background_image', Rails.root + 'spec/Fixtures/Snow.jpg')
Capybara::ElementNotFound:
Unable to find file field "#artist_background_image"
# ./spec/models/artist_spec.rb:65:in `block (2 levels) in <top (required)>'
my test is listed below. I assumed the choose file button is the selector for attach_file so I used the id of the selector '#artist_background_page'.
describe 'Upload Background Image', js: true do
before :each do
p '================='
pp Artist.all
#artist = Artist.first || Artist.create(artist_name:'Double Stuff Oreos', email:'i#love.oreos', password:'letmein')
visit "/artists/" + #artist.id.to_s
pp #artist.id
pp #artist.artist_name
p #artist.errors.full_messages
click_button 'Log in'
fill_in 'Email', with: 'i#love.oreos'
fill_in 'Password', with: 'letmein'
page.find('#loginButtonModal').click
page.find('#addBackgroundImagePrompt').click
page.attach_file('#artist_background_image', Rails.root + 'spec/Fixtures/Snow.jpg')
p Rails.root + 'spec/Fixtures/Snow.jpg'
click_button 'upload'
end
it "cannot upload a background image", js: true do
backgroundURL = #artist.background_image.url.include?('Snow.jpg')
p #artist.background_image.url
expect(backgroundURL).to be_truthy
end
end
However when I change the selector to just 'artist_background_page' without the id '#' symbol. rspec gives me different error:
...."================="
[]
"is_active_session is called"
#<Artist id: 1, artist_name: "Double Stuff Oreos", route_name: "double-stuff-oreos", created_at: "2014-07-20 17:20:48", updated_at: "2014-07-20 17:20:48", password_digest: "$2a$04$vgozdgieklXPjJ9Ri4Cv1e1d/hme0ybNnSEGXrmob5z...", remember_token: nil, email: "i#love.oreos", description: nil, youtube: nil, twitter: nil, facebook: nil, instagram: nil, hometown: nil, confirmation_code: nil, is_confirmed: nil, background_image_file_name: nil, background_image_content_type: nil, background_image_file_size: nil, background_image_updated_at: nil>
1
"Double Stuff Oreos"
[]
"is_active_session is called"
#<Pathname:/Users/bob/rails_projects/audience/spec/Fixtures/Snow.jpg>
"/background_images/original/missing.png"
"Hello from update"
"Logged in!"
An error occurred in an after hook
ActiveRecord::RecordNotFound: Couldn't find Artist with 'id'=1
occurred at /Users/shuo/.rvm/gems/ruby-2.1.2/gems/activerecord- 4.1.4/lib/active_record/relation/finder_methods.rb:320:in `raise_record_not_found_exception!'
F....
Failures:
1) Upload Background Image cannot upload a background image
Failure/Error: expect(backgroundURL).to be_truthy
expected: truthy value
got: false
# ./spec/models/artist_spec.rb:74:in `block (2 levels) in <top (required)>'
In this case, the model was created but the errors says that it cannot find it for some reason...
Is there any other method I can use to attach files in capybara and test successfully uploads?
Possibilities for failure:
Wrong path for file upload
Wrong method to use or invalid use of method
something wrong with server

This error:
Capybara::ElementNotFound:
Unable to find file field "#artist_background_image"
shows that capybara can't find a field named #artist_background_image. What you need to do is to reference the file upload field by its name.
Suppose you have
<input id="artist_background_image" name="file_upload" type="file">
Then you can reference the field like:
page.attach_file('file_upload', Rails.root + 'spec/Fixtures/Snow.jpg')
With that, capybara will know what field to upload through.
I know this is a late response but I think it will help other people facing this issue.

This might be the case if you're using simple_form gem.
Note that it does automatically generate ids for your form elements, hence manually id-ing elements yourself is futile!
You can make sure of this by manually id-ing a form element then view the source on you browser. You'll defiantly see that your manually-assigned ids aren't anywhere in the page source.
There is; however, a naming convention that simple_form follows to id form elements.
object_attribute
The example bellow might clarify this even more (HAML exmaple):
= simple_form_for #user do |f|
= f.input :image, as: :file
simple_form will then automatically generate an id for the form element image as so (object_attribute):
id="user_image"
Now you can simply make use of this naming convention to target ids in your test. Hope this helps.

Related

Rails Rspec allow multiple method call in one line

desc 'Remove credential state users who no longer request for confirm otp within 10 minutes'
task failed_user_cleaner: :environment do
puts "Daily UserRecord Cleaning CronJob started - #{Time.now}"
#user = User.with_state("credentials").with_last_otp_at(Time.now - 10.minutes)
Users::Delete.new(#user).destroy_all
puts "Daily UserRecord Cleaning CronJob ended - #{Time.now}"
end
Above is crop job rake file code.
then I've tried in many times and found in many times.
But I couldn't find the way to write unit test case for above job.
Help me to write test case correctly.
here is my spec code
require 'rails_helper'
describe 'users rake tasks' do
before do
Rake.application.rake_require 'tasks/users'
Rake::Task.define_task(:environment)
end
context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
let(:user) { create(:user, last_otp_at: Time.now - 11.minutes, state: "credentials") }
let (:run_users_rake_task) do
Rake.application.invoke_task 'users:failed_user_cleaner'
end
it 'calls right service method' do
#users = Users::Delete.new([user])
expect(#users).to receive(:destroy_all)
run_users_rake_task
end
end
end
here is the error log
Failures:
1) users rake tasks when remove credential state users who no longer request for confirm otp within 10 minutes calls right service method
Failure/Error: expect(#users).to receive(:destroy_all)
(#<Users::Delete:0x0000556dfcca3a40 #user=[#<User id: 181, uuid: nil, phone: "+66969597538", otp_secret: nil, last_otp_at: "2021-09-30 09:32:24.961548000 +0700", created_at: "2021-09-30 09:43:24.973818000 +0700", updated_at: "2021-09-30 09:43:24.973818000 +0700", email: nil, avatar: "https://dummyimage.com/300x300/f04720/153572.png?t...", refresh_token: "eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MzI5Njk4MDQsImV4c...", first_name_en: "Jenise", first_name_th: "Damion", last_name_en: "McCullough", last_name_th: "Beatty", nationality: "TH", thai_national_id: nil, thai_laser_code: nil, company_id: 200, role: nil, state: "credentials", date_of_birth: "2020-10-30 00:00:00.000000000 +0700", deleted_at: nil, password_digest: "$2a$04$jfR9X9ci06602tlAyLOoRewTK1lZ12vJ2cZ9Dc2ov4F...", username: "zreejme238", shopname: nil, access_token: nil, locked_at: nil, login_attempts: 0, locale: "th", scorm_completed: false>]>).destroy_all(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/tasks/users_spec.rb:19:in `block (3 levels) in <top (required)>'
You are creating two instances of Users::Delete when running this test, one within the test and one within the task. Since the instance within the test is not used, it is incorrect to expect it to receive a message.
Rspec has an expectation, expect_any_instance_of, that will fix this however consider reading the full page since it can create fragile or flaky tests. If you wanted to use this method, your test would look something like:
it 'calls right service method' do
expect_any_instance_of(Users::Delete).to receive(:destroy_all)
run_users_rake_task
end
Personally I'd instead check that the expected users were deleted with something like:
it 'removes the user' do
expect { run_users_rake_task }.to change { User.exists?(id: #user.id) }.to(false)
end
Unless you want to use any_instance_of (which is a code smell) you need to stub the Users::Delete method so that it returns a double and put the expectation on the double:
require 'rails_helper'
describe 'users rake tasks' do
before do
Rake.application.rake_require 'tasks/users'
Rake::Task.define_task(:environment)
end
context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
let(:user) { create(:user, last_otp_at: Time.now - 11.minutes, state: "credentials") }
let(:run_users_rake_task) do
Rake.application.invoke_task 'users:failed_user_cleaner'
end
let(:double) do
instance_double('Users::Delete')
end
before do
allow(Users::Delete).to receive(:new).and_return(double)
end
it 'calls right service method' do
expect(double).to receive(:destroy_all)
run_users_rake_task
end
end
end
However this really just tells us that the API of the service object is clunky and that you should write a class method which both instanciates and performs:
module Users
class Delete
# ...
def self.destroy_all(users)
new(users).destroy_all
end
end
end
desc 'Remove credential state users who no longer request for confirm otp within 10 minutes'
#...
Users::Delete.destroy_all(#user)
# ...
end
require 'rails_helper'
describe 'users rake tasks' do
# ...
context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
# ...
it 'calls right service method' do
expect(Users::Delete).to receive(:destroy_all)
run_users_rake_task
end
end
end

Capybara can't find file

I'm getting this error when running RSpec:
Failures:
1) Creating a new movie saves the movie and shows the new movie's details
Failure/Error: attach_file "Poster image", with: "#{Rails.root}/app/assets/images/interstellar.jpg"
Capybara::FileNotFound:
cannot attach file, [:with, "/Users/Ian/code/courses/ruby_on_rails_1/flix-tdd/app/assets/images/interstellar.jpg"]does not exist
However, I check and it looks like the file is there:
File.exist?("#{Rails.root}/app/assets/images/interstellar.jpg")
=> true
File.exist?("/Users/Ian/code/courses/ruby_on_rails_1/flix-tdd/app/assets/images/interstellar.jpg")
=> true
What am I missing here?
Why not?
attach_file "Poster image", "#{Rails.root}/app/assets/images/interstellar.jpg"
http://www.rubydoc.info/github/jnicklas/capybara/Capybara/Node/Actions#attach_file-instance_method

Rails, Capybara - click_link on remote links doesnt work

I'm using Capybara to test my project. But i have a problem.
I have some remote forms on my project. They add records via ajax. When i'm testing with capybara it works well on development environment. It visits the page, fills in the form and submits. Booom, record has been added and test didnt fail.
But when i run rspec with test environments i'm getting unknown format exception.
1) add new address user adds new address
Failure/Error: find("input[value='Adres Ekle']").click
ActionController::UnknownFormat:
Account::AddressesController#create is missing a template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
# ./spec/features/user_add_new_address_spec.rb:28:in `block (2 levels) in <top (required)>'
I've also tried to respond via js from controller like;
def create
request.format = :js
end
Then it returns;
1) add new address user adds new address
Failure/Error: find("input[value='Adres Ekle']").click
ActionController::UnknownFormat:
Account::AddressesController#create is missing a template for this request format and variant.
request.formats: ["text/javascript"]
request.variant: []
# ./spec/features/user_add_new_address_spec.rb:28:in `block (2 levels) in <top (required)>'
And my scenario if u want more info;
scenario 'user adds new address' do
expect(page).to have_content 'Kayıtlı Adreslerim'
find("a[title='Adres Ekle']").click
expect(page).to have_content 'Yeni Adres Ekle'
expect(page).to have_content 'Adres Başlığı'
fill_in 'address[name]', with:'Izmir Ofisi'
select('Izmir', :from => 'address[city_id]')
fill_in 'address[address]', with: 'Lorem ipsum dolor sit amet.'
find("input[value='Adres Ekle']").click # It submits remote: true form.
expect(page).to have_content 'Success!'
end
PS: my create action doesnt render something like that.
its like;
def create
#new_address = Address.new
#address = #current_account.addresses.new(address_params)
if #address.save
#check = true
else
#check = false
end
end
it renders: create.js.erb
<% if #check %>
if($('.addresses').length) {
$('.addresses').append('<%= j(render('account/addresses/address', address: #address)) %>');
}
if($('#did-addresses').length){
$('#did-addresses').append("<%= "<option selected='true' value='#{#address.id}'>#{#address.name}</option>".html_safe %>").selectpicker('refresh');
}
$('#new-address').html('<%= j(render('account/addresses/form', new_address: #new_address)) %>');
swal({
type: 'success',
title: "<%= t('response.success') %>",
text: "<%= t('flash.actions.create.notice', resource_name: Address.model_name.human) %>",
timer: 2000
});
quickview.close('#new-address');
<% else %>
<% #address.errors.each do |error| %>
<% end %>
<% end %>
$('.preloader').fadeOut();
I was facing the same case in rails 6 but I fixed it be adding js: true to the scenario and it automatically worked well.
scenario 'Should delete the feature', js: true do
# Your logic
# Your expectations
end
Since copying your development config over your test config fixed your issue, it sounds like you probably an error in one of your JS files. Normally in the test and production environment all of your JS assets get concatenated into one file which means an error in any one of them can prevent the code in the others from being executed. In the development environment each JS file is loaded separately which means an error in any file can only affect the rest of the code in that file. Check your the console in your browser for any JS errors when going to the page in question and fix them.

factory_girl does not set model attribute

factory_girl is not setting the url attribute on the model correctly. I looked to see if there are reserved words, but I found nothing.
factory :attachment do
association :attachable, factory: :upload
url "some/path"
description "Some important file"
end
I get this failure. Notice url: nil
1) Attachment should be valid
Failure/Error: it { should be_valid }
expected #<Attachment id: 1048, attachable_id: 1047, attachable_type: "Upload", name: nil, url: nil, created_at: nil, updated_at: nil, description: "Some important file"> to be valid, but got errors: Url can't be empty
# ./spec/models/attachment_spec.rb:14:in `block (2 levels) in <top (required)>'
I ran factory_girl in the console and got the same result with FactorGirl.attributes_for :attachment
Running Rails 4 with FactoryGirl 4.2.1.
I was having this same issue and after hours of banging my head on the desk I decided to stop and restart my Spring server. It started working like magic.
Before restarting my Spring server I was able to set attributes using create :object, new_field: true but just using create :object wouldn't work, even if I had new_field true in my factories.rb file.
Late to the party but I had the same problem. I had added all my attributes after the initial migration, and it turned out I was using attr_accessor instead of attr_accessible in my model.

created data did not appear when testing

hye, I'm new to BDD testing and testing particularly. I'm using rspec, factory_girl_rails, selenium and capybara gem. I want to test for editing a data and save it like so :
it "edit consultant" do
# c = FactoryGirl.create(:consultant)
c = Consultant.create(
orgorcom: "xyz",
year_id: 8,
bidang: "IT & Networking",
project: "E-Sw",
professional_fee: "1000000",
role: "Database Architect",
user_id: 19,
nopkj: "075899 "
)
visit "/consultants?year=#{Time.now.year}"
string = "#consultant#{c.id}"
page.find(string).click_link('Edit')
fill_in "Organization / Company" , :with => c.orgorcom + "aaa"
fill_in "Field", :with => c.bidang + "aaa"
fill_in "Project" , :with => c.project + "aaa"
fill_in "Role", :with => c.role + "aaa"
fill_in "Professional fee", :with => c.professional_fee + 111
click_button "Save"
expect(page).to have_content "Data updated."
sleep 10
# c.destroy
# find('a[href="/consultants/6144/edit?year=2013"]').click
end
But the data I created did not appear & I get this message
1) the consultation edit consultant
Failure/Error: page.find(string).click_link('Edit')
Capybara::ElementNotFound:
Unable to find css "#consultant6157"
when I tried click on existing data like below, it passed.
page.find("#consultant6144").click_link('Edit')
I am able to print out the consultant id but still the record mysteriously did not appear before the test ends (which the db will rollback).
This is a known issue if you're using Selenium for browser testing, with transactional fixtures configured in your spec_helper. Selenium will run in a different thread and with a different database connection than the one being used by Rspec to create your database objects inside transactions, therefore the Selenium thread won't be able to see them.
For these kind of specs, you will need to not use transactional fixtures and use something like database_cleaner to create objects at the start of your specs and truncate/delete them afterwards.
Your problem doesn't seem related to FactoryGirl. The error message thrown by Capybara indicates that the object's id was properly concatenated into the string, but the corresponding CSS wasn't found in the page.
I would recommend you install the launchy gem by inserting this into your Gemfile:
group :development, :test do
gem 'launchy'
end
That will enable you to use the save_and_open_page method in your feature specs. Then insert this method right before the line that's provoking the error, like so:
# ...
save_and_open_page
page.find(string).click_link('Edit')
# ...
When save_and_open_page is called, your default browser will open at the same page. Then you can manually inspect the page's HTML and find out if it does, in fact, have the element you're looking for and, if it doesn't, why that happens.

Resources