Working on a project these days where I work with Cucumber and Capybara, I had to test the contents of filed downloaded from the application, the downloads were being sent using “send_file” and chrome was adding them directly to the user Downloads folder, and the capybara tests had no access to the file contents after that.
My goal was to have the test code in Gherkin like this:
When I click on "Download"
Then the downloaded content should be:
"""
id,name,age
1,John Doe,35
3,Mary Jane, 27
8,Little Joe, 9
"""
But after cucumber executes
When I click on "Download"
The file was downloaded, it doesn’t wait for the download to finish and the next step had no access to the downloaded file.
After some research I found out that I could configure a profile to Chrome before instantiating the browser in Capybara.
Capybara.register_driver :chrome do |app|
profile = Selenium::WebDriver::Chrome::Profile.new
profile['download.default_directory'] = TestDownloadHelpers::PATH.to_s
Capybara::Selenium::Driver.new(app, browser: :chrome, profile: profile)
end
Capybara.javascript_driver = :chrome
In a simple explanation, this snippet sets a profile in the browser to be instantiated, configuring the default download directory to the tmp/downloads folder in my rails app.
This also mean that we can set ay number of other chrome properties as required, but we’ll get there sometime in the future 😀
Then I created this download helper in a new file under features/support
module TestDownloadHelpers
TIMEOUT = 30
PATH = Rails.root.join('tmp/downloads')
extend self
def downloads
Dir[PATH.join('*')].sort_by{ |f| File.mtime(f) }
end
def download
downloads.last
end
def download_content
wait_for_download
File.open(download, &:read)
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.mkdir_p(PATH)
FileUtils.rm_f(downloads)
end
end
World(DownloadHelpers)
Before do
clear_downloads
end
After do
clear_downloads
end
An important point here is that the “downloading?” method uses the chrome partial download extension, so this entire solution only works for Chrome, if you need a multi browser solution this code will need a lot of updating.
With these small changes I was able to download files directly to the temp download folder, then check the downloaded file contents correctly with this simple snippet.
Then('the downloaded content should be:') do |content|
text = download_content
expect(text).to eq(content)
end
Making my initial test pass, and allowing me to completely test my downloads.
I hope this small snippets help someone else 😀
Please feel free to leave any comments bellow.