DEV Community

Alessandro Rodi
Alessandro Rodi

Posted on

14 4 1 1 1

Test downloaded files with RSpec and System Tests

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/)
Enter fullscreen mode Exit fullscreen mode

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.

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (1)

Collapse
 
michaelodumosu57 profile image
Michael Odumosu • Edited

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...

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more