B Seven B Seven - 3 months ago 115
Ruby Question

How to test CSV file download in Capybara and RSpec?

The following is in the controller:

respond_to do |format|
format.csv { send_data as_csv, type:'text/csv' }
end


In spec:

click_link 'Download CSV'
page.driver.browser.switch_to.alert.accept

expect( page ).to have_content csv_data


But this doesn't work:
Failure/Error: page.driver.browser.switch_to.alert.accept
Selenium::WebDriver::Error::NoAlertPresentError:
No alert is present


I see the Save File dialog box display, but apparently it is not an "alert" dialog.

How to click OK and get Capybara to see the data?

Answer

Adapted from CollectiveIdea and another source.

Works on OSX. Firefox 34.0.5

Spec:

  describe 'Download CSV' do
    let( :submission_email ){ 'foo@example.com' }
    let( :email_csv ){ "id,email,created_at\n1,#{ submission_email }," }

    specify do
      visit '/emails'
      expect( page ).to have_content 'Email Submissions'

      click_on 'Download CSV'

      expect( DownloadHelpers::download_content ).to include email_csv
    end
  end

Spec helper:

require 'shared/download_helper'

Capybara.register_driver :selenium do |app|
  profile = Selenium::WebDriver::Firefox::Profile.new
  profile['browser.download.dir'] = DownloadHelpers::PATH.to_s
  profile['browser.download.folderList'] = 2

  # Suppress "open with" dialog
  profile['browser.helperApps.neverAsk.saveToDisk'] = 'text/csv'
  Capybara::Selenium::Driver.new(app, :browser => :firefox, :profile => profile)
end

config.before( :each ) do
    DownloadHelpers::clear_downloads
end

shared/download_helper.rb:

module DownloadHelpers
  TIMEOUT = 1
  PATH    = Rails.root.join("tmp/downloads")

  extend self

  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(/\.part$/).any?
  end

  def clear_downloads
    FileUtils.rm_f(downloads)
  end
end