【问题标题】:How do you test uploading a file with Capybara and Dropzone.js?你如何测试使用 Capybara 和 Dropzone.js 上传文件?
【发布时间】:2015-12-29 02:11:20
【问题描述】:

我已切换到使用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 没有可见的文件输入。

【问题讨论】:

    标签: testing capybara dropzone.js


    【解决方案1】:

    由于 Capybara 3.21.0,您可以将文件拖放到这样的元素上:

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

    详情请参阅Element#dropsource。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 你应该可以使用file_fixture('avatar.png'),而不是Rails.root.join('spec/fixtures/files/avatar.png')。
      【解决方案3】:

      要解决此问题,请模拟一个放置事件以触发将附件放置到 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 类。

      【讨论】:

      • var e = jQuery.Event('drop', { dataTransfer : { files : [fakeFileInput.get(0).files[0]] } }); 你就不能这样做var e = jQuery.Event('drop', { dataTransfer : { files : fileList } });
      • 这是一个完整的祝福。谢谢。
      • 2019 年 5 月更新:这仍然可以完美运行,无需更改。非常感谢!
      • 我更改了最后一个块以编程方式访问 dropzone 并删除了虚假输入,因此您可以在同一区域多次调用它。 var e = jQuery.Event("drop", { dataTransfer : { files : [fakeFileInput.get(0).files[0]] } }); Dropzone.instances[0].listeners[0].events.drop(e); $("#fakeFileInput").remove();
      • 我无法让它工作,它说 fakeFileInput.get 不是一个函数。我认为我在测试中没有正确加载 jquery?我发现这个宝石运行良好github.com/pikselpalette/dropybara
      【解决方案4】:

      以@deepwell 的回答为基础,这对我来说不太奏效,这是一个使用 vanilla 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 控制台中进行测试,但可以随意确定它们的范围。

      【讨论】:

        【解决方案5】:

        如果有人感兴趣,我将@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);
          });
        };
        

        【讨论】:

        • OP 要求的是 capybara,而不是 JS。最好将其作为对所选答案的评论的要点。
        • 似乎每个页面都可能以不同的方式实现 dropzone,我们需要稍微自定义此脚本以使其正常工作。就我而言,它与“originalEvent”属性有关
        猜你喜欢
        • 2013-05-19
        • 2013-08-26
        • 2019-01-16
        • 1970-01-01
        • 2017-01-02
        • 2013-12-21
        • 2017-05-14
        • 1970-01-01
        • 2019-10-01
        相关资源
        最近更新 更多