Download buttons can be difficult to test, especially with Selenium. Depending on browser, user settings and response headers, one of three things can happen:
Below you can find multiple methods how to test file downloads with Selenium and Capybara despite these issues. Both have different advantages and drawbacks.
There are ways to download and inspect a file without navigating away from the current page.
See Capybara: Most okayest helper to download and inspect files.
Only test that the download link's [href]
attribute points to a particular route.
Then test that route with a request spec. Or, if the uploaded file lives in public/
, test that the linked file exists.
If you can get away with it, run that one test with Rack::Test
instead of Selenium.
With a Rack::Test
driver Capybara allows access to the response headers via page.driver.response.headers
and page.driver.response.body
.
Warning
The code examples for this method need to be updated.
Pros:
Cons:
Note: The step below will override a spreewald step Show archive.org snapshot and allows to use Regex for the filename. You might want to rename this step within you project in case you are using Spreewald Show archive.org snapshot .
features/step_definitions/spreewald_overrides_steps.rb
Then(/^I should get a download with filename "(.*?)"$/) do |filename|
if Capybara.current_driver == :selenium
patiently do
expect(DownloadHelpers.last_download&.basename&.to_s).to match(filename)
end
else
expect(response_headers['Content-Disposition']).to match(/filename=\"#{filename}\"/)
end
end
features/support/download_helpers.rb
# This is an adapted approach from
# https://collectiveidea.com/blog/archives/2012/01/27/testing-file-downloads-with-capybara-and-chromedriver
module DownloadHelpers
TIMEOUT = 10
module_function
def download_path
download_path = Rails.root.join("tmp/test_downloads#{ENV['TEST_ENV_NUMBER']}")
FileUtils.mkdir_p(download_path)
download_path
end
def clear_downloads
FileUtils.rm_r(download_path) if File.exist?(download_path)
end
def last_download
downloads = Pathname.new(download_path)
Timeout.timeout(TIMEOUT) do
sleep 0.1 until downloads.glob('*.crdownload').blank?
end
downloads.children.sort_by(&File.method(:ctime)).last
end
end
Before do
DownloadHelpers.clear_downloads
end
After do
DownloadHelpers.clear_downloads
end
features/support/selenium.rb
Capybara.register_driver :selenium do |app|
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('--mute-audio')
options.add_argument('--disable-infobars')
options.add_preference('credentials_enable_service', false)
options.add_preference('profile.password_manager_enabled', false)
options.add_preference(:download, default_directory: DownloadHelpers.download_path.to_s)
Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end
Before do
Capybara.current_driver = :rack_test
end
Before('@javascript') do
Capybara.current_driver = :selenium
end