How do you test uploading a file with Capybara and Dropzone.js?

To solve this, simulate a drop event to trigger dropping an attachment onto Dropzone. First add this function to your step definition:

    # Upload a file to Dropzone.js
    def drop_in_dropzone(file_path)
      # Generate a fake input selector
      page.execute_script <<-JS
        fakeFileInput = window.$('<input/>').attr(
          {id: 'fakeFileInput', type:'file'}
        ).appendTo('body');
      JS
      # Attach the file to the fake input selector
      attach_file("fakeFileInput", file_path)
      # Add the file to a fileList array
      page.execute_script("var fileList = [fakeFileInput.get(0).files[0]]")
      # Trigger the fake drop event
      page.execute_script <<-JS
        var e = jQuery.Event('drop', { dataTransfer : { files : [fakeFileInput.get(0).files[0]] } });
        $('.dropzone')[0].dropzone.listeners[0].events.drop(e);
      JS
    end

Then test with:

    When(/^I upload "([^"]*)"$/) do |filename|
      drop_in_dropzone File.expand_path(filename)
      # add assertion here
    end

NOTE: You need to have jQuery loaded, and the Dropzone element requires the dropzone class.


These days I find this way more graceful

page.attach_file(Rails.root.join('spec/fixtures/files/avatar.png')) do
  page.find('#avatar-clickable').click
end

Where is in my case #avatar-clickable is a div which contain Dropzone form tag.


Building off of @deepwell's answer which didn't quite work for me, here is a solution using vanilla JS for the events and event dispatching, and a neutral selector for the dropzone:

  def drop_in_dropzone(file_path, zone_selector)
    # Generate a fake input selector
    page.execute_script <<-JS
      fakeFileInput = window.$('<input/>').attr(
        {id: 'fakeFileInput', type:'file'}
      ).appendTo('body');
    JS

    # Attach the file to the fake input selector
    attach_file("fakeFileInput", file_path)

    # Add the file to a fileList array
    page.execute_script("fileList = [fakeFileInput.get(0).files[0]]")

    # Trigger the fake drop event
    page.execute_script <<-JS
      dataTransfer = new DataTransfer()
      dataTransfer.items.add(fakeFileInput.get(0).files[0])
      testEvent = new DragEvent('drop', {bubbles:true, dataTransfer: dataTransfer })
      $('#{zone_selector}')[0].dispatchEvent(testEvent)
    JS
  end

uses global vars on purpose, so I could test in js console, but feel free to scope them.