Downloading file to specific folder using Capybara and Poltergeist driver
Asked Answered
A

4

21

I am writing my acceptance tests using Capybara and Poltergeist driver.I need to validate the content of the CSV file downloaded.

  1. I tried various ways of rendering the content on the page itself instead of downloading it.
  2. Also tried changing the mime types, but it is not working.

Finally I want to settle down with the option of downloading the file in a specific folder and then read the CSV file using core ruby libraries.

In order to achieve this,when poltergeist driver clicks on download link then I want it to handle the pop-up and download the file directly in the given folder.

In Selenium's chrome and firefox drivers, I have option of configuring profiles to handle pop ups and configure download directory.

Is there any such option using poltergeist? Any information will be helpful.

Adelia answered 1/4, 2013 at 7:29 Comment(0)
K
25

It is not possible with Poltergeist, you can just check the headers.

 step 'I should get zipped file' do
    page.response_headers['Content-Disposition'].should include("filename=\"file.zip\"")
 end

But is is possible with Chrome driver and also with recent versions of Firefox and Selenium Webdriver. Unfortunately it runs via Selenium - i.e. not headless... See this article: http://collectiveidea.com/blog/archives/2012/01/27/testing-file-downloads-with-capybara-and-chromedriver/

My approach - slightly different as I'm working with Spinach and Rubyzip:

Add the following to your Gemfile

group :test do
  gem 'chromedriver-helper'  # for Chrome <= 28
  gem 'chromedriver2-helper' # for Chrome >= 29
  gem 'selenium-webdriver'
end

features/support/capybara.rb - I'm using Poltergeist for scenarios with @javascript tag and Chrome for scenarios with @download tag.

require 'spinach/capybara'
require 'capybara/poltergeist'
require 'selenium/webdriver'

# ChromeDriver 1.x, for Chrome <= 28 
Capybara.register_driver :chrome do |app|
  profile = Selenium::WebDriver::Chrome::Profile.new
  profile['download.default_directory'] = DownloadHelper::PATH.to_s
  args = ["--window-size=1024,768"]
  Capybara::Selenium::Driver.new(app, browser: :chrome, profile: profile, args: args)
end

# ChromeDriver 2.x, for Chrome >= 29 
Capybara.register_driver :chrome do |app|
  prefs = {
    download: {
      prompt_for_download: false,
      default_directory: DownloadHelper::PATH.to_s
    }
  }
  args = ['--window-size=1024,768']
  Capybara::Selenium::Driver.new(app, browser: :chrome, prefs: prefs, args: args)
end

# Tested with Firefox 27 and Selenium Webdriver 2.39
Capybara.register_driver :firefox do |app|
  profile = Selenium::WebDriver::Firefox::Profile.new
  profile['browser.download.dir'] = DownloadHelper::PATH.to_s
  profile['browser.download.folderList'] = 2 # 2 - save to user defined location
  profile['browser.helperApps.neverAsk.saveToDisk'] = 'application/zip'
  Capybara::Selenium::Driver.new(app, browser: :firefox, profile: profile)
end

Capybara.javascript_driver = :poltergeist # :webkit :selenium :poltergeist :chrome

Spinach.hooks.on_tag("javascript") do
  Capybara.current_driver = Capybara.javascript_driver
  Capybara.default_wait_time = 5
end

Spinach.hooks.on_tag("download") do
  Capybara.current_driver = :chrome # or :firefox
  Capybara.default_wait_time = 50
end

features/support/downloads.rb

module DownloadHelper
  TIMEOUT = 10
  PATH    = Rails.root.join("tmp/downloads")

  extend self

  def downloads
    Dir[PATH.join("*")]
  end

  def download_path
    wait_for_download
    downloads.first
  end

  def download_content
    wait_for_download
    File.read(download_path)
  end

  def wait_for_download
    Timeout.timeout(TIMEOUT) do
      sleep 0.1 until downloaded?
    end
  end

  def downloaded?
    downloads.any? && !downloading?
  end

  def downloading?
    downloads.grep(/\.crdownload$/).any?
  end

  def clear_downloads
    FileUtils.rm_f(downloads)
  end

end

Spinach.hooks.before_scenario do |scenario|
  DownloadHelper.clear_downloads
end

Spinach.hooks.after_scenario do
  DownloadHelper.clear_downloads
end

features/file_download.feature

Feature: File download
   As a user
   I want to be able to download my files

Background:
  Given I am logged in as a user
  And I have uploaded files in the system

@download
Scenario: Successfull download
  When I click on the download button
  Then I should get zipped files

features/steps/file_download.rb - Note that you can't use page.response_headers as it is not supported by the Selenium/ChromeDriver. But you can check the filename of the downloaded file using the File.basename().

class Spinach::Features::FileDownload < Spinach::FeatureSteps
  include SharedAuthentication

  step 'I click on the download button' do
    click_link "Download"
  end

  step 'I should get zipped files' do
    File.basename(DownloadHelper.download_path).should == 'file.zip'
    Zip::ZipFile.open(DownloadHelper.download_path) do |zipfile|
      zipfile.find_entry('myfile.txt').should_not be_nil
      zipfile.find_entry('myphoto.jpg').should_not be_nil
    end
  end

end
Knutson answered 25/4, 2013 at 14:28 Comment(4)
When I tried using poltergeist and just checking the headers (page.response_headers), it returned the headers from the last HTML page, apparently, and not for the PDF file I triggered the download for – page.response_headers['Content-Type'] was "text/html"... I wonder if something has changed or am I doing something wrong? Just using the regular :selenium (WebDriver) driver works fine though, when I loop until the downloaded file is finished downloading like in your example...Fillander
Well it could be problem with ChromeDriver, since my original example was written for ChromeDriver 1.x and Chromium up to v29.Knutson
However for Chromium 29+ you need to use ChromeDriver 2.x. You can download it here: chromedriver.storage.googleapis.com/index.html and move it to executable path. Or you can use chromedriver2-helper gem. But unfortunately ChromeDriver 2.x needs slightly different documentation.During the recent experimenting I've made the DownloadHelper to work with Firefox as well.Knutson
I've edited the example above and added example configuration for ChromeDriver 2.x and also for Firefox(!)Knutson
S
15

I've had to do similar things in my rails app. My solution is using Javascript to make a XMLHttpRequest to the URL, downloading the file, returning the contents of the file back to Capybara, and using ruby to save the file somewhere on disk. Then in another step, I check the contents to the downloaded CSV file.

Here's the step definition for downloading the file:

Then /^I download the csv file$/ do
  page.execute_script("window.downloadCSVXHR = function(){ var url = window.location.protocol + '//' + window.location.host + '/file.csv'; return getFile(url); }")
  page.execute_script("window.getFile = function(url) { var xhr = new XMLHttpRequest();  xhr.open('GET', url, false);  xhr.send(null); return xhr.responseText; }")

  data = page.evaluate_script("downloadCSVXHR()")
  File.open(File.join(Rails.root, "tmp", "csv.data"), "w") { |f| f.write(data) }
end

Change the URL in the Javascript code to your CSV's location.

And finally, here's my step definition for validating the CSV file's contents:

And /^the contents of the downloaded csv should be:$/ do |contents|
  file = File.open(File.join(Rails.root, "tmp", "csv.data"), "r")
  file_contents = file.read
  file_contents.chop!
  file_contents.should == contents
end

Good luck. Hope this helps.

Seventeen answered 14/6, 2013 at 14:48 Comment(1)
I had to update to PhantomJS 2.1 and this solution doesn't work anymore. Any ideas how to get it working?Medovich
C
4

This is not currently possible with Poltergeist.

I think you'd be better off writing a test for this CSV which doesn't use Capybara. (E.g. by using the built-in Rails integration testing stuff and parsing the response as a CSV.)

Chela answered 2/4, 2013 at 12:31 Comment(0)
K
2

There is an ticket to support downloading files in PhantomJS/Poltergeist and there are one or two forks which claims that they made it to work somehow. See https://github.com/ariya/phantomjs/issues/10052

Knutson answered 18/2, 2014 at 23:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.