如何使用Capybara和Dropzone.js测试上传文件?

时间:2015-10-01 06:10:04

标签: testing capybara dropzone.js

我已切换到使用Dropzone.js插件进行拖放文件上传。如何编写Capybara测试以确保此功能继续工作?

以前我有一个带有输入文件元素的模板:

<input type="file" name="attachments">

测试很简单:

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

但是这不再有效,因为Dropzone没有可见的文件输入。

5 个答案:

答案 0 :(得分:21)

要解决此问题,请模拟放置事件以触发将附件丢弃到Dropzone。首先将此函数添加到步骤定义中:

# 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

然后测试:

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

注意:您需要加载jQuery,而Dropzone元素需要 dropzone 类。

答案 1 :(得分:3)

如果有人感兴趣,我将@ deepwell的功能移植到javascript,将其与javascript风格的selenium一起使用:

this.dropInDropzone = function(filePath) {
  var script = "fakeFileInput = $('#fakeFileInput'); if (fakeFileInput.length === 0) fakeFileInput = window.$('<input/>').attr({id: 'fakeFileInput', type:'file'}).appendTo('body');";
  // Generate a fake input selector
  return driver.executeScript(script).then(function() {
    // Attach the file to the fake input selector
    return driver.findElement(webdriver.By.css('#fakeFileInput')).sendKeys(filePath);
  }).then(function() {
    // Add the file to a fileList array
    return driver.executeScript("var fileList = [fakeFileInput.get(0).files[0]]");
  }).then(function() {
    // Trigger the fake drop event
    script = "var e = jQuery.Event('drop', { dataTransfer : { files : [fakeFileInput.get(0).files[0]] } }); $('.dropzone')[0].dropzone.listeners[0].events.drop(e);"
    return driver.executeScript(script);
  });
};

答案 2 :(得分:1)

以@deepwell的答案为基础,这对我而言并不奏效,这是一个使用香草JS进行事件和事件分发的解决方案,以及一个用于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

故意使用全局变量,因此我可以在js控制台中进行测试,但是可以自由定义它们的范围。

答案 3 :(得分:1)

这些天,我发现这种方式更加优雅

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

在我的情况下,#avatar-clickable是一个包含Dropzone表单标签的div。

答案 4 :(得分:0)

自从 Capybara 3.21.0 以来,您可以在元素上放置文件,如下所示:

find(".dropzone").drop(Rails.root.join("spec/fixtures/file.txt"))

有关详细信息,请参阅 Element#drop source