Downloading file to specific folder using Capybara and Poltergeist driver

后端 未结 4 707
星月不相逢
星月不相逢 2020-12-14 02:47

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 rende
相关标签:
4条回答
  • 2020-12-14 02:50

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

    0 讨论(0)
  • 2020-12-14 03:03

    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

    0 讨论(0)
  • 2020-12-14 03:04

    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
    
    0 讨论(0)
  • 2020-12-14 03:14

    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.

    0 讨论(0)
提交回复
热议问题