Scenario
You have a download button, that downloads a dynamically generated file, and you want to test that the file is downloaded correctly. A request test is not sufficient, because the file changes, depending on the actions performed by the user in previous steps, so you want to test that in a system test.
Solution
The solution is based on the fact that you are using rspec
, selenium-webdriver
and Chrome to run you tests.
First of all, you need a custom driver, that is able to manage downloads. You can add the following to the rails_helper
:
Capybara.register_driver :selenium_chrome_headless do |app| browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts| opts.args << '--headless' opts.args << '--disable-site-isolation-trials' end browser_options.add_preference(:download, prompt_for_download: false, default_directory: DownloadHelpers::PATH.to_s) browser_options.add_preference(:browser, set_download_behavior: { behavior: 'allow' }) Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options) end
and implement a DownloadHelper
module:
module DownloadHelpers TIMEOUT = 10 PATH = Rails.root.join('tmp/downloads') 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(/\.crdownload$/).any? end def clear_downloads FileUtils.rm_f(downloads) end end
Now, you have a driver, that is able to manage downloads. Use it and clean the downloads before and after each test. So, magain, in the rails_helper.rb
:
config.before(:each, type: :system, js: true) do clear_downloads driven_by :selenium_chrome_headless end config.after(:each, type: :system, js: true) do clear_downloads end
and here is a small example of usage in a system test:
visit the_page_with_download_button_path find("Download as PDF").click wait_for_download expect(downloads.length).to eq(1) expect(download).to match(/.*\.pdf/)
If you want, the driver above is available as selenium_chrome_with_downloads
and selenium_chrome_headless_with_downloads
in the so_many_devices gem, kindly offered by Renuo.
Top comments (1)
Absolute lifesaver I have modified the downloadshelper to work with non Rails applications specifically Angular, why I choose capybara, so simple to mimick and complicated e2e such as file download. Subscribe to our youtube channel we really want to mention when we do a video on it youtube.com/channel/UCmqEX_zasOf3A...