使用 Selenium 模拟将文件拖到上传元素上

Posted

技术标签:

【中文标题】使用 Selenium 模拟将文件拖到上传元素上【英文标题】:Using Selenium to imitate dragging a file onto an upload element 【发布时间】:2011-07-08 11:27:12 【问题描述】:

我有一个网页,当您单击按钮时会打开一个 div。此 div 允许您将文件从桌面拖到其区域;然后文件被上传到服务器。我正在使用 Selenium 的 Ruby 实现。

通过在 Firefox 中使用 javascript 调试器,我可以看到一个名为“drop”的事件正在传递给一些 JavaScript 代码“handleFileDrop(event)”。我假设如果我要创建一个模拟事件并以某种方式触发它,我可以触发此代码。

如果发现interesting article 似乎为我指明了一个有希望的方向,但我仍然没有完全弄清楚。我可以使用 Selenium 的 get_eval 方法将 JavaScript 传递给页面。使用 this.browserbot 调用方法让我得到了我需要的元素。

所以:

    如何构建文件对象 需要成为模拟下降的一部分 事件? 如何触发 drop 事件 这样它就会被捡起来,就好像我 在 div 中删除了一个文件?

【问题讨论】:

【参考方案1】:

我发布了一个 RSpec 测试,该测试使用 Selenium webdriver 模拟文件拖放。 它使用 jQuery 来制作和触发一个虚假的 'drop' 事件。

此代码模拟单个文件的拖放。为简单起见,我删除了允许删除多个文件的代码。需要的话告诉我。

describe "when user drop files", :js => true do
  before do
    page.execute_script("seleniumUpload = window.$('<input/>').attr(id: 'seleniumUpload', type:'file').appendTo('body');")

    attach_file('seleniumUpload', Rails.root + 'spec/support/pdffile/pdfTest.pdf')

    # Trigger the drop event
    page.execute_script("e = $.Event('drop'); e.originalEvent = dataTransfer :  files : seleniumUpload.get(0).files  ; $('#fileDropArea').trigger(e);")
  end

  it "should ..." do
     should have_content '...'
  end

P.S.:记得将#fileDropArea 替换为放置区域的ID。

附注:不要使用 evaluate_script 代替 execute_script,否则 selenium 会在评估复杂的 jQuery 对象时卡住!

更新: 我写了一个方法,你可以重用并做上面写的东西。

def drop_files files, drop_area_id
  js_script = "fileList = Array();"
  files.count.times do |i|
    # Generate a fake input selector
    page.execute_script("if ($('#seleniumUpload#i').length == 0)  seleniumUpload#i = window.$('<input/>').attr(id: 'seleniumUpload#i', type:'file').appendTo('body'); ")
    # Attach file to the fake input selector through Capybara
    attach_file("seleniumUpload#i", files[i])
    # Build up the fake js event
    js_script = "#js_script fileList.push(seleniumUpload#i.get(0).files[0]);"
  end

  # Trigger the fake drop event
  page.execute_script("#js_script e = $.Event('drop'); e.originalEvent = dataTransfer :  files : fileList  ; $('##drop_area_id').trigger(e);")
end

用法:

describe "when user drop files", :js => true do
  before do
     files = [ Rails.root + 'spec/support/pdffile/pdfTest1.pdf',
               Rails.root + 'spec/support/pdffile/pdfTest2.pdf',
               Rails.root + 'spec/support/pdffile/pdfTest3.pdf' ]
     drop_files files, 'fileDropArea'
  end

  it "should ..." do
     should have_content '...'
  end
end   

【讨论】:

这看起来不错——我没有在我当前的项目中使用 RSpec,所以如果其他人可以确认这在 cmets 中有效,那么我会接受这个答案。 在 rspec 中确认 - 完美运行!先生,你成就了我的一天! :) 感谢您 - 我已经成功翻译了代码,以便使用 C# 进行测试。 @Shmoopy 添加为下面的答案 :) 请注意,drop_area_id 是附加了 ondrop 事件处理程序的 html 元素。【参考方案2】:

根据@Shmoopy 的要求,这是@micred 提供的代码的C# 翻译

private void DropImage(string dropBoxId, string filePath)

   var javascriptDriver = this.Driver as IJavaScriptExecutor;
   var inputId = dropBoxId + "FileUpload";

   // append input to HTML to add file path
   javascriptDriver.ExecuteScript(inputId + " = window.$('<input id=\"" + inputId + "\"/>').attr(type:'file').appendTo('body');");
   this.Driver.FindElement(By.Id(inputId)).SendKeys(filePath);

   // fire mock event pointing to inserted file path
   javascriptDriver.ExecuteScript("e = $.Event('drop'); e.originalEvent = dataTransfer :  files : " + inputId + ".get(0).files  ; $('#" + dropBoxId + "').trigger(e);");

【讨论】:

谢谢,这对我有帮助。虽然我收到了错误selenium Unexpected error. type property can't be changed。通过将 $('&lt;input id=\"" + inputId + "\"/&gt;').attr(type:'file') 更改为 $('&lt;input id=\"" + inputId + "\" type=\"file\" /&gt;') 来修复。【参考方案3】:

您可以使用 Blueduck Sda (http://sda.blueducktesting.com) 是一个实现了所有 selenium 功能的 OSS(它适用于 selenium RC),但它允许您自动执行 Windows 操作。所以你可以测试网络,并与操作系统交互。 因此,您可以进行测试,然后只需告诉鼠标单击元素并将其放到您想要的位置!

很好的测试!

【讨论】:

这看起来可能是一个选项,虽然我在 OSX 中工作,而不是 Windows。我也对在 JavaScript 上下文中与浏览器机器人交互和触发事件感到好奇。 在撰写本文时 blueducktesting.com 域名正在出售中。【参考方案4】:

注意:您还应该添加

    e.originalEvent.dataTransfer.types = [ 'Files' ];

【讨论】:

以上是关于使用 Selenium 模拟将文件拖到上传元素上的主要内容,如果未能解决你的问题,请参考以下文章

Python + Selenium + AutoIt 模拟键盘实现另存为上传下载操作详解

Selenium:设置元素等待上传文件下载文件

详解介绍Selenium常用API的使用--Java语言(完整版)

python使用selenium模拟点击网页实现自动导入上传文件功能

python使用selenium模拟点击网页实现自动导入上传文件功能

Selenium 中没有 <input type="file"> 元素的文件上传