Update : After burning too many hours, decided to opt for the easy way out and use rack-test. This works straight out of the box and at least verifies that the content-type is a pdf.
scenario 'document can be downloaded' do
visit my_documents_path
click_on 'Download'
expect(page.response_headers['Content-Type']).to eq "application/pdf"
end
I am trying to write a feature spec to test the contents of my downloaded pdf, and I have followed the directions found here: https://stackoverflow.com/a/29544674/2464546
I am running Ruby 2.3, Rails 4.2.5.2, RSpec 3.4, Capybara 2.7.
In the code from the SO link above, there's a line that supposedly suppresses the Firefox Save popup, with this, adjusted to pdf
from csv
:
# Suppress "open with" dialog
profile['browser.helperApps.neverAsk.saveToDisk'] = 'application/pdf'
This doesn't seem to work because the dialog box still pops up, and then my test errors out.
My feature spec:
scenario 'document can be downloaded', js: true do
visit my_documents_path
click_on 'Download'
expect(DownloadHelpers::download_content).to have_content('Thingy')
end
Every time I run this spec, it errors out saying execution expired
with a different error each time. For example the latest one had the following:
1) My documents home page document can be downloaded
Failure/Error: Dir[PATH.join("*")]
Timeout::Error:
execution expired
# ./spec/features/shared/download_helper.rb:8:in `downloads'
# ./spec/features/shared/download_helper.rb:31:in `downloading?'
# ./spec/features/shared/download_helper.rb:27:in `downloaded?'
# ./spec/features/shared/download_helper.rb:22:in `block in wait_for_download'
# ./spec/features/shared/download_helper.rb:21:in `wait_for_download'
# ./spec/features/shared/download_helper.rb:16:in `download_content'
# ./spec/features/my_documents/index_spec.rb:42:in `block (2 levels) in <top (required)>'
In the DownloadHelpers
module, I've changed the sleep
from 0.1 to 1 to 3, and occasionally I'll get the following error, with the sleep count changing w/ whatever I've set it to:
Failure/Error: sleep 3 until downloaded?
Timeout::Error:
execution expired
# ./spec/features/shared/download_helper.rb:22:in `sleep'
# ./spec/features/shared/download_helper.rb:22:in `block in wait_for_download'
# ./spec/features/shared/download_helper.rb:21:in `wait_for_download'
# ./spec/features/shared/download_helper.rb:16:in `download_content'
# ./spec/features/my_documents/index_spec.rb:42:in `block (2 levels) in <top (required)>'
I have also changed the TIMEOUT
count with no change in failure result from the above. Ultimately, the dialog box still pops up and does not go away/doesn't look like it downloads the file.
My controller behind the Download button:
def download
pdf = @document.pdf_file_name
send_file pdf
end
The created document is not large, as all it has is a name and a few lines, so I don't suspect it'd need more than a few seconds to download and read the file.
Why is the execution expiring? How do I get Capybara/Feature spec to download the file so my expectation passes?
The view document.haml
using angular tho I don't think that matters.
%li.col-xs-6
%li.col-xs-6= link_to 'Download', download_document_path(id: document.id), "ng-click" => "logAnalytics('#{document.document_template_id}', 'download')"
Also, my feature spec has require 'rails_helper'
at the top, and my rails_helper has the code from the SO post (in full, w/ relevant bits):
require 'features/shared/download_helper'
RSpec.configure do |config|
Capybara.register_driver :selenium do |app|
profile = Selenium::WebDriver::Firefox::Profile.new
profile['browser.download.dir'] = DownloadHelpers::PATH.to_s
profile['browser.download.folderList'] = 2
profile['browser.helperApps.neverAsk.saveToDisk'] = 'application/pdf'
Capybara::Selenium::Driver.new(app, browser: :firefox, profile: profile)
end
config.before( :each ) do
DownloadHelpers::clear_downloads
end
end
I think any of the following will work:
page.driver.browser.switch_to.alert.accept
# or
page.driver.browser.switch_to.alert.dismiss
# or
page.driver.browser.switch_to.alert.text
Check the mime type your pdf download is returning, odds are it's not actually 'application/pdf' and is most likely being returned as 'application/octet-stream'. You should fix the return type to be correct, but in the short term you can try changing to profile['browser.helperApps.neverAsk.saveToDisk'] = 'application/pdf,application/octet-stream'
I got the 'execution expired' error too. I followed all the steps above, but the error remained. It turned out to be the download directory was missing in the filesystem ( DownloadHelpers::PATH
). Creating that one made the message disappear and the test pass.
I was experiencing this same error. The solution for me was to increase the timeout. So where ever you are doing the sleeping until timeout, increase the timeout. Of course it depends on your business logic but 5 seconds works for me consistently whereas 2 seconds failed consistently.
I have tried to implement something similar and spend lots of hours. Finally I have some solution, maybe it fit for you as well.
Gemfile:
#source 'https://rubygems.org'
gem 'rails', '4.2.2'
gem 'bcrypt', '3.1.7'
gem 'bootstrap-sass', '3.2.0.0'
gem 'faker', '1.4.2'
gem 'carrierwave', '0.10.0'
gem 'mini_magick', '3.8.0'
gem 'fog', '1.36.0'
gem 'will_paginate', '3.0.7'
gem 'bootstrap-will_paginate', '0.0.10'
gem 'sass-rails', '5.0.2'
gem 'uglifier', '2.5.3'
gem 'coffee-rails', '4.1.0'
gem 'jquery-rails', '4.0.3'
gem 'turbolinks', '2.3.0'
gem 'jbuilder', '2.2.3'
gem 'sdoc', '0.4.0', group: :doc
gem 'rename'
gem 'sprockets', '3.6.3'
gem 'responders', '~> 2.0'
gem 'inherited_resources'
group :development, :test do
gem 'sqlite3', '1.3.9'
gem 'byebug', '3.4.0'
gem 'web-console', '2.0.0.beta3'
gem 'spring', '1.1.3'
end
group :test do
gem 'minitest-reporters', '1.0.5'
gem 'mini_backtrace', '0.1.3'
gem 'guard-minitest', '2.3.1'
gem 'capybara', '2.8.1'
gem 'rspec', '3.5.0'
gem 'rspec-rails', '~> 3.4'
gem 'cucumber-rails', :require => false
gem 'shoulda-matchers', '~> 3.0', require: false
gem 'database_cleaner'
gem 'factory_girl_rails', '~> 4.5.0'
end
spec/rails_helper.rb
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 'spec_helper'
require 'rspec/rails'
require 'shoulda/matchers'
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
config.use_transactional_fixtures = false
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
spec/spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rspec'
require 'capybara/rails'
require 'download_helper'
Capybara.register_driver :selenium do |app|
profile = Selenium::WebDriver::Firefox::Profile.new
profile['browser.download.dir'] = DownloadHelpers::PATH.to_s
profile['browser.download.folderList'] = 2
profile['browser.helperApps.neverAsk.saveToDisk'] = 'text/csv'
Capybara::Selenium::Driver.new(app, :browser => :firefox, :profile => profile)
end
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
config.include Capybara::DSL
=begin
config.filter_run_when_matching :focus
config.example_status_persistence_file_path = "spec/examples.txt"
config.disable_monkey_patching!
if config.files_to_run.one?
config.default_formatter = 'doc'
end
config.profile_examples = 10
config.order = :random
Kernel.srand config.seed
=end
end
test/test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'capybara/rails'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
include ApplicationHelper
def is_logged_in?
!session[:user_id].nil?
end
# Logs in a test user.
def log_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
if integration_test?
post login_path, session: { email:user.email, password: password, remember_me: remember_me }
else
session[:user_id] = user.id
end
end
private
# Returns true inside an integration test.
def integration_test?
defined?(post_via_redirect)
end
end
class ActionDispatch::IntegrationTest
# Make the Capybara DSL available in all integration tests
include Capybara::DSL
# Reset sessions and driver between tests
# Use super wherever this method is redefined in your individual test classes
def teardown
Capybara.reset_sessions!
Capybara.use_default_driver
end
end
spec/download_helper.rb
module DownloadHelpers
TIMEOUT = 1
PATH = Rails.root.join("tmp/downloads")
extend self
def downloads
Dir[PATH.join("*")]
end
def download
downloads.first
end
def download_content
wait_for_download
File.read(download)
end
def wait_for_download
Timeout.timeout(TIMEOUT) do
sleep 0.1 until downloaded?
end
end
def downloaded?
!downloading? && downloads.any?
end
def downloading?
downloads.grep(/\.part$/).any?
end
def clear_downloads
FileUtils.rm_f(downloads)
end
end
spec/mpodels/spec.rb
describe 'Download file' do
specify do
visit '/createfile'
click_on 'create file'
page.response_headers['Content-Type'].should == "text/csv"
header = page.response_headers['Content-Disposition']
header.should match /^attachment/
header.should match /filename=\"temp.csv\"$/
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.