Skip to main content

_ruby

TL;DR - Show Me The Code

2-download-a-file/code/ruby/download.rb
require 'selenium-webdriver'
require 'rspec/expectations'
require 'uuid'
require 'fileutils'

include RSpec::Matchers

def setup
@download_dir = File.join(Dir.pwd, UUID.new.generate)
FileUtils.mkdir_p(@download_dir)

options = Selenium::WebDriver::Firefox::Options.new
options.add_preference('browser.download.dir', @download_dir)
options.add_preference('browser.download.folderList', 2)
options.add_preference('browser.helperApps.neverAsk.saveToDisk',
'image/jpeg, application/pdf, application/octet-stream')
options.add_preference('pdfjs.disabled', true)
@driver = Selenium::WebDriver.for(:firefox, options: options)
end

def teardown
@driver.quit
FileUtils.rm_rf(@download_dir)
end

def run
setup
yield
teardown
end

run do
@driver.get('https://the-internet.herokuapp.com/download')
download_link = @driver.find_element(css: '.example a')
download_link.click
# Wait up to 5 seconds for the file to download
wait = Selenium::WebDriver::Wait.new(timeout: 5)
wait.until { Dir.glob("#{@download_dir}/*").any? }
files = Dir.glob("#{@download_dir}/**")
expect(files.size).to eq 1
file = files.first
expect(File.size(file)).to be > 0
end

Code Walkthrough

Importing Libraries

Lines 1 to 4 are pulling in our requisite libraries (e.g., selenium-webdriver to drive the browser, rspec/expectations and RSpec::Matchers for assertions, uuid to help create a uniquely named temporary download directory, and fileutils to create & destroy the temp directory) and wiring up our setup method.

Setup and Teardown

Lines 8 to 24 are setting up and tearing down the test. Our setup method is where the magic is happening in this example. In it, we're creating a uniquely named temp directory and storing the absolute path of it in an instance variable that we'll use throughout this file.

We're also setting a few browser preferences (for Firefox in this case) and plying it with the necessary configuration parameters to make it automatically download the file where we want. Here's a breakdown of each of them:

  • browser.download.dir accepts a string. This is how we set the custom download path. It needs to be an absolute path.
  • browser.download.folderList takes a number. It tells Firefox which download directory to use. 2 tells it to use a custom download path, wheras 1 would use the browser's default path, and 0 would place them on the Desktop.
  • browser.helperApps.neverAsk.saveToDisk tells Firefox when not to prompt for a file download. It accepts a string of the file's MIME type. If you want to specify more than one, you do it with a comma-separated string.
  • pdfjs.disabled is for when downloading PDFs. This overrides the sensible default in Firefox that previews PDFs in the browser. It accepts a boolean.

These preferences are then passed into our instance of Selenium.

In teardown we make sure to clean up the temp directory after closing the browser. Other than that, it's business as usual.

The run method is a custom method that we've defined to execute our test. It's a simple wrapper around the setup and teardown methods. We're using it to keep our test code clean and easy to read.

The Test

Lines 33 to 42 are the test itself.

After loading the page we find the first download link and click it. The click triggers an automatic download to the temp directory created in setup. After that, we perform some rudimentary checks to make sure the directory isn't empty and that the file isn't empty either.

Executing the Test

Before executing the test, we need to make sure the required dependencies are declared on the Gemfile file.

Toggle to see the Gemfile file.
2-download-a-file/code/ruby/Gemfile
source 'https://rubygems.org'

gem 'rspec', '~> 3.10'
gem 'selenium-webdriver', '~> 4.24.0'
gem 'uuid'

Finally, we can run the test by executing ruby download.rb from the command-line.