ruby_dev
ruby_dev

Reputation: 136

Capybara upload images issue

I need to test file uploads page with Capybara and Selenium.

I wrote this test:

require 'rails_helper'

describe 'Images', type: :feature do
  before(:each) do
    @user = create(:user)
    visit '/users/sign_in'
    fill_in 'sing-in-email-input', with: @user.email
    fill_in 'sign-in-password-input', with: @user.password
    click_button 'btn-sign-in'
    visit '/categories'
    click_on 'btn-new-category'
    expect(current_path) == new_category_path
    fill_in 'category_name', with: 'Test'
    click_button 'btn-create category'
    visit '/categories'
    first('.fa', :visible => false).click
    expect(current_path) == category_path(id: Category.last.slug.to_s)
  end

  it 'should allow a registered user to create image and go to it page', js: true do
    click_on 'btn-upload-images'
    expect(current_path) == new_image_path(id: Category.last.slug.to_s)
    attach_file('image[image]',
                File.join(Rails.root, '/spec/fixtures/solnce-kosmos-merkuriy.jpg'), :visible => false)
    click_on('btn-upload-img')
  end
end

I am not able to continue write this test and check was this image successfully uploaded because on this line:

click_on('btn-upload-img')

I got this error:

Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
     
     ActionController::RoutingError:
       No route matches [GET] "/home/Anton-S/RubymineProjects/rails_projects/gallery/spec/support/uploads/image/image/8790/solnce-kosmos-merkuriy.jpg"
     # /var/lib/gems/2.3.0/gems/railties-5.1.4/lib/rails/rack/logger.rb:36:in `call_app'
     # /var/lib/gems/2.3.0/gems/railties-5.1.4/lib/rails/rack/logger.rb:24:in `block in call'
     # /var/lib/gems/2.3.0/gems/railties-5.1.4/lib/rails/rack/logger.rb:24:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/method_override.rb:22:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/runtime.rb:22:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/sendfile.rb:111:in `call'
     # /var/lib/gems/2.3.0/gems/railties-5.1.4/lib/rails/engine.rb:522:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/urlmap.rb:68:in `block in call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/urlmap.rb:53:in `each'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/urlmap.rb:53:in `call'
     # /var/lib/gems/2.3.0/gems/capybara-2.16.1/lib/capybara/server.rb:44:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/handler/webrick.rb:86:in `service'
     # ------------------
     # --- Caused by: ---
     # Capybara::CapybaraError:
     #   Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true
     #   /var/lib/gems/2.3.0/gems/capybara-2.16.1/lib/capybara/session.rb:145:in `raise_server_error!'

Finished in 17.15 seconds (files took 2.55 seconds to load)
133 examples, 1 failure

Failed examples:

rspec ./spec/features/images_spec.rb:20 # Images should allow a registered user to create image and go to it page

I have checked that I realy have this file

/spec/fixtures/solnce-kosmos-merkuriy.jpg

on the right place.

Here is my /config/initializers/carrierwave.rb

if Rails.env.test? || Rails.env.cucumber?
  CarrierWave.configure do |config|
    config.storage = :file
    config.enable_processing = false
  end

  # make sure our uploader is auto-loaded
  ImageUploader

  # use different dirs when testing
  CarrierWave::Uploader::Base.descendants.each do |klass|
    next if klass.anonymous?
    klass.class_eval do
      def cache_dir
        "#{Rails.root}/spec/support/uploads/tmp"
      end

      def store_dir
        "#{Rails.root}/spec/support/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
      end
    end
  end
end

my rails_helper.rb

require 'support/factory_bot'
require 'spec_helper'
require 'shoulda/matchers'
require 'capybara/rspec'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'

ActiveRecord::Migration.maintain_test_schema!


module DeviseRequestSpecHelpers

  include Warden::Test::Helpers

  def sign_in(resource_or_scope, resource = nil)
    resource ||= resource_or_scope
    scope = Devise::Mapping.find_scope!(resource_or_scope)
    login_as(resource, scope: scope)
  end

  def sign_out(resource_or_scope)
    scope = Devise::Mapping.find_scope!(resource_or_scope)
    logout(scope)
  end

end

RSpec.configure do |config|
  Capybara.ignore_hidden_elements = false

  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  config.use_transactional_fixtures = true

  config.infer_spec_type_from_file_location!

  config.filter_rails_from_backtrace!
  config.include Devise::Test::ControllerHelpers, type: :controller
  config.include DeviseRequestSpecHelpers, type: :request
end

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec

    with.library :rails
  end
end

My spec_helper.rb

RSpec.configure do |config|
  config.expect_with :rspec do |expectations|
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end

  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  config.shared_context_metadata_behavior = :apply_to_host_groups
end

My view template for this images upload page:

.row.main-new-image
  = form_for @image, url: create_image_path, html: {multipart: true, method: :post, remote: true } do |form|
    #new_image_error_explanation
    .field
      .p.text-center
        %span.btn.btn-new-image{id: 'new-image-button'}
          %p#p-new-image-button Choose image
          \#{form.file_field :image, id: 'new-image-id'}
    .field
      = form.hidden_field :user_id, value: current_user.id
    .actions
      .p.text-center
        = form.submit 'Upload', class: 'btn button-upload-img', id: 'btn-upload-img'

All my other Capybara and RSpec tests works fine.

Please help me to understand what is the reason of this issue and how to solve it and prevent in the future.

I can provide more information if it can help to solve this issue.

UPDATE 1

After I read comment of Thomas Walpole I decide to update my code and try some other features. I include new gem

gem 'database_cleaner'

Also I have added new code to clear test uploaded code. Here is updated rails_helper.rb

require 'support/factory_bot'
require 'spec_helper'
require 'shoulda/matchers'
require 'capybara/rspec'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'

ActiveRecord::Migration.maintain_test_schema!
module DeviseRequestSpecHelpers

  include Warden::Test::Helpers

  def sign_in(resource_or_scope, resource = nil)
    resource ||= resource_or_scope
    scope = Devise::Mapping.find_scope!(resource_or_scope)
    login_as(resource, scope: scope)
  end

  def sign_out(resource_or_scope)
    scope = Devise::Mapping.find_scope!(resource_or_scope)
    logout(scope)
  end
end

RSpec.configure do |config|
  Capybara.javascript_driver = :selenium
  Capybara.ignore_hidden_elements = true
  Capybara.register_driver :selenium do |app|
    Capybara::Selenium::Driver.new(app, browser: :firefox)
  end

  config.include Capybara::DSL
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  config.use_transactional_fixtures = true
  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end
  
  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end
  
  config.after(:all) do
    if Rails.env.test?
      FileUtils.rm_rf(Dir["#{Rails.root}/spec/support/uploads"])
    end
  end
  config.infer_spec_type_from_file_location!
  config.filter_rails_from_backtrace!
  config.include Devise::Test::ControllerHelpers, type: :controller
  config.include DeviseRequestSpecHelpers, type: :request
end

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec

    with.library :rails
  end
end

Here is my updated test

require 'rails_helper'

describe 'Images', type: :feature do
  before(:each) do
    @user = create(:user)
    visit '/users/sign_in'
    fill_in 'sing-in-email-input', with: @user.email
    fill_in 'sign-in-password-input', with: @user.password
    click_button 'btn-sign-in'
    visit '/categories'
    click_on 'btn-new-category'
    have_current_path(new_category_path)
    fill_in 'category_name', with: 'Test'
    click_button 'btn-create category'
    visit '/categories'
    first('.fa', :visible => false).click
    have_current_path(category_path(id: Category.last.slug.to_s))
  end

  #ISSUE WITH
  it 'should allow a registered user to create image and go to it page', :driver => :selenium do
    click_on 'btn-upload-images'
    have_current_path(new_image_path(id: Category.last.slug.to_s))
    attach_file('image[image]',
                File.join(Rails.root, '/spec/fixtures/solnce-kosmos-merkuriy.jpg'), :visible => false)
    click_on('btn-upload-img')
  end
end

As Thomas Walpole told me, I checked test logs and saw next (about current test):

Started GET "/en/categories/test/new_image" for 127.0.0.1 at 2018-01-30 14:33:26 +0200
Processing by ImagesController#new as HTML
  Parameters: {"locale"=>"en", "id"=>"test"}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 22], ["LIMIT", 1]]
   (0.2ms)  SAVEPOINT active_record_2
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 22], ["LIMIT", 1]]
  SQL (0.3ms)  INSERT INTO "user_actions" ("user_id", "action_type", "url", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["user_id", 22], ["action_type", "navigation"], ["url", "http://127.0.0.1:38226/en/categories/test/new_image"], ["created_at", "2018-01-30 12:33:26.645909"], ["updated_at", "2018-01-30 12:33:26.645909"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_2
  Rendering images/new.html.haml within layouts/application
  Rendered images/_form.html.haml (5.2ms)
  Rendered images/new.html.haml within layouts/application (7.5ms)
  Category Load (1.1ms)  SELECT  DISTINCT categories.*, COUNT(images.*) AS images_count, categories.*, COUNT(likes.*) AS likes_count, categories.*, COUNT(comments.*) AS comments_count FROM "categories" LEFT OUTER JOIN "images" ON "images"."category_id" = "categories"."id" LEFT OUTER JOIN "images" "images_categories_join" ON "images_categories_join"."category_id" = "categories"."id" LEFT OUTER JOIN "likes" ON "likes"."image_id" = "images_categories_join"."id" LEFT OUTER JOIN "images" "images_categories_join_2" ON "images_categories_join_2"."category_id" = "categories"."id" LEFT OUTER JOIN "comments" ON "comments"."image_id" = "images_categories_join_2"."id" GROUP BY categories.id ORDER BY images_count DESC, likes_count DESC, comments_count DESC LIMIT $1  [["LIMIT", 5]]
  Rendered layouts/_header.html.haml (4.3ms)
  Rendered layouts/_footer.haml (0.0ms)
Completed 200 OK in 21ms (Views: 12.6ms | ActiveRecord: 2.1ms)
  Category Load (0.6ms)  SELECT  "categories".* FROM "categories" ORDER BY "categories"."id" DESC LIMIT $1  [["LIMIT", 1]]
Started POST "/en/categories/test/create_image" for 127.0.0.1 at 2018-01-30 14:33:26 +0200
Processing by ImagesController#create as JS
  Parameters: {"utf8"=>"✓", "image"=>{"image"=>#<ActionDispatch::Http::UploadedFile:0x0000000ae4e008 @tempfile=#<Tempfile:/tmp/RackMultipart20180130-28977-1nptliu.jpg>, @original_filename="solnce-kosmos-merkuriy.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"image[image]\"; filename=\"solnce-kosmos-merkuriy.jpg\"\r\nContent-Type: image/jpeg\r\n">, "user_id"=>"22"}, "commit"=>"Upload", "locale"=>"en", "id"=>"test"}
  Category Load (0.2ms)  SELECT  "categories".* FROM "categories" WHERE "categories"."slug" = $1 LIMIT $2  [["slug", "test"], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 22], ["LIMIT", 1]]
   (0.3ms)  SAVEPOINT active_record_2
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 22], ["LIMIT", 1]]
  Category Load (0.2ms)  SELECT  "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT $2  [["id", 18], ["LIMIT", 1]]
  SQL (0.4ms)  INSERT INTO "images" ("image", "category_id", "created_at", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["image", "solnce-kosmos-merkuriy.jpg"], ["category_id", 18], ["created_at", "2018-01-30 12:33:26.945577"], ["updated_at", "2018-01-30 12:33:26.945577"], ["user_id", 22]]
   (0.2ms)  RELEASE SAVEPOINT active_record_2
Redirected to http://127.0.0.1:38226/en/categories/test/12
Completed 200 OK in 22ms (ActiveRecord: 1.6ms)
Started GET "/en/categories/test/12" for 127.0.0.1 at 2018-01-30 14:33:26 +0200
Processing by ImagesController#show as HTML
  Parameters: {"locale"=>"en", "id"=>"test", "image_id"=>"12"}
  User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 22], ["LIMIT", 1]]
   (0.1ms)  SAVEPOINT active_record_2
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 22], ["LIMIT", 1]]
  SQL (0.3ms)  INSERT INTO "user_actions" ("user_id", "action_type", "url", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["user_id", 22], ["action_type", "navigation"], ["url", "http://127.0.0.1:38226/en/categories/test/12"], ["created_at", "2018-01-30 12:33:26.966397"], ["updated_at", "2018-01-30 12:33:26.966397"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_2
  Category Load (0.1ms)  SELECT  "categories".* FROM "categories" WHERE "categories"."slug" = $1 LIMIT $2  [["slug", "test"], ["LIMIT", 1]]
  Image Load (0.2ms)  SELECT  "images".* FROM "images" WHERE "images"."category_id" = $1 AND "images"."id" = $2 LIMIT $3  [["category_id", 18], ["id", 12], ["LIMIT", 1]]
  Like Load (0.2ms)  SELECT  "likes".* FROM "likes" WHERE "likes"."image_id" = $1 AND "likes"."user_id" = $2 LIMIT $3  [["image_id", 12], ["user_id", 22], ["LIMIT", 1]]
  Rendering images/show.html.haml within layouts/application
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 22], ["LIMIT", 1]]
   (0.3ms)  SELECT COUNT(*) FROM "comments" WHERE "comments"."image_id" = $1  [["image_id", 12]]
  Like Load (0.2ms)  SELECT  "likes".* FROM "likes" WHERE "likes"."user_id" = $1 AND "likes"."image_id" = 12 LIMIT $2  [["user_id", 22], ["LIMIT", 1]]
   (0.3ms)  SELECT COUNT(*) FROM "likes" WHERE "likes"."image_id" = $1  [["image_id", 12]]
  Rendered likes/_like.html.haml (3.2ms)
  Rendered likes/_like_form.html.haml (7.3ms)
  Rendered comments/_comments.html.haml (2.7ms)
  Rendered comments/_comments_container.html.haml (5.1ms)
  Rendered images/show.html.haml within layouts/application (25.6ms)
  Category Load (1.3ms)  SELECT  DISTINCT categories.*, COUNT(images.*) AS images_count, categories.*, COUNT(likes.*) AS likes_count, categories.*, COUNT(comments.*) AS comments_count FROM "categories" LEFT OUTER JOIN "images" ON "images"."category_id" = "categories"."id" LEFT OUTER JOIN "images" "images_categories_join" ON "images_categories_join"."category_id" = "categories"."id" LEFT OUTER JOIN "likes" ON "likes"."image_id" = "images_categories_join"."id" LEFT OUTER JOIN "images" "images_categories_join_2" ON "images_categories_join_2"."category_id" = "categories"."id" LEFT OUTER JOIN "comments" ON "comments"."image_id" = "images_categories_join_2"."id" GROUP BY categories.id ORDER BY images_count DESC, likes_count DESC, comments_count DESC LIMIT $1  [["LIMIT", 5]]
  Rendered layouts/_header.html.haml (4.6ms)
  Rendered layouts/_footer.haml (0.1ms)
Completed 200 OK in 43ms (Views: 30.0ms | ActiveRecord: 4.1ms)
Started GET "/home/Anton-S/RubymineProjects/rails_projects/gallery/spec/support/uploads/image/image/12/solnce-kosmos-merkuriy.jpg" for 127.0.0.1 at 2018-01-30 14:33:27 +0200
   (0.2ms)  ROLLBACK TO SAVEPOINT active_record_1
   (0.1ms)  ROLLBACK
   (0.1ms)  BEGIN
   (0.1ms)  COMMIT
   (0.0ms)  BEGIN
   (0.0ms)  SAVEPOINT active_record_1
   (0.1ms)  SAVEPOINT active_record_2

As I understand, image was successfully uploaded. But I still get this error

Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
     
     ActionController::RoutingError:
       No route matches [GET] "/home/Anton-S/RubymineProjects/rails_projects/gallery/spec/support/uploads/image/image/12/solnce-kosmos-merkuriy.jpg"

UPDATE 2

Unfortunately the only solution I found is to set in /config/environments/test.rb

config.action_dispatch.show_exceptions = true

But I still think that there is better solution.

Upvotes: 0

Views: 1646

Answers (1)

Thomas Walpole
Thomas Walpole

Reputation: 49950

So, from the logs, we can see the file is being successfully uploaded.

The issue is that when the show page is rendered the images are being added to the HTML with urls like '/home/Anton-S/RubymineProjects/rails_projects/gallery/spec/support/uploads/image/image/12/solnce-kosmos-merkuriy.jpg' which won't work because the rails app won't respond with random files from outside of the public directory in the apps root. Doing so would be a huge security issue. The easiest solution is for you to just store the images into the public directory so the rails app will serve them as static assets. Something along the lines of

  def cache_dir
    "public/test/uploads/tmp"
  end

  def store_dir
    "public/test/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

Note: I also commented on your use of the have_current_path matcher.

Upvotes: 2

Related Questions