【问题标题】:Selenium: Drag and Drop from file system to WebDriver?Selenium:从文件系统拖放到 WebDriver?
【发布时间】:2016-08-08 12:24:05
【问题描述】:

我必须测试一个 Web 应用程序,该应用程序包含一个拖放区域,用于从本地文件系统上传文件。我的测试环境是基于 C# 的。

对于自动化测试,我使用了 Selenium,但无法从文件系统中拖动文件。上传区域是一个div 标签(没有input 标签)。那么最好的方法是什么? AutoIt(是否可以放入网络浏览器)?西库里?

【问题讨论】:

标签: c# selenium ui-automation


【解决方案1】:

单独使用 Selenium 是可能的,但这并不简单。它需要在页面中注入一个新的INPUT 元素以通过SendKeys 接收文件。然后,脚本需要通过向目标区域发送dragenterdragoverdrop 事件来模拟跌落。

static void Main(string[] args)
{
    var driver = new ChromeDriver();
    driver.Url = "https://react-dropzone.js.org/";

    IWebElement droparea = driver.FindElementByCssSelector("[data-preview='Basic example'] [style]");
    DropFile(droparea, @"C:\Users\florent\Desktop\capture.png");

    driver.Quit();
}

const string JS_DROP_FILE = "for(var b=arguments[0],k=arguments[1],l=arguments[2],c=b.ownerDocument,m=0;;){var e=b.getBoundingClientRect(),g=e.left+(k||e.width/2),h=e.top+(l||e.height/2),f=c.elementFromPoint(g,h);if(f&&b.contains(f))break;if(1<++m)throw b=Error('Element not interractable'),b.code=15,b;b.scrollIntoView({behavior:'instant',block:'center',inline:'center'})}var a=c.createElement('INPUT');a.setAttribute('type','file');a.setAttribute('style','position:fixed;z-index:2147483647;left:0;top:0;');a.onchange=function(){var b={effectAllowed:'all',dropEffect:'none',types:['Files'],files:this.files,setData:function(){},getData:function(){},clearData:function(){},setDragImage:function(){}};window.DataTransferItemList&&(b.items=Object.setPrototypeOf([Object.setPrototypeOf({kind:'file',type:this.files[0].type,file:this.files[0],getAsFile:function(){return this.file},getAsString:function(b){var a=new FileReader;a.onload=function(a){b(a.target.result)};a.readAsText(this.file)}},DataTransferItem.prototype)],DataTransferItemList.prototype));Object.setPrototypeOf(b,DataTransfer.prototype);['dragenter','dragover','drop'].forEach(function(a){var d=c.createEvent('DragEvent');d.initMouseEvent(a,!0,!0,c.defaultView,0,0,0,g,h,!1,!1,!1,!1,0,null);Object.setPrototypeOf(d,null);d.dataTransfer=b;Object.setPrototypeOf(d,DragEvent.prototype);f.dispatchEvent(d)});a.parentElement.removeChild(a)};c.documentElement.appendChild(a);a.getBoundingClientRect();return a;";

static void DropFile(IWebElement target, string filePath, double offsetX = 0, double offsetY = 0)
{
    if (!File.Exists(filePath))
        throw new FileNotFoundException(filePath);

    IWebDriver driver = ((RemoteWebElement)target).WrappedDriver;
    IJavaScriptExecutor jse = (IJavaScriptExecutor)driver;

    IWebElement input = (IWebElement)jse.ExecuteScript(JS_DROP_FILE, target, offsetX, offsetY);
    input.SendKeys(filePath);
}

来源:https://gist.github.com/florentbr/349b1ab024ca9f3de56e6bf8af2ac69e

【讨论】:

  • 您能解释一下 JavaScript 代码中发生了什么吗?是否正确,您添加输入,在此输入路径中设置文件,然后触发/模拟“dragenter”、“dragover”、“drop”事件?最后应该怎么办?我已经尝试过这段代码,但它对我没有用。我看到输入是在页面上注入的,输入的值包含文件路径,但放置区域没有任何反应。
  • @Alex.K,很难从一个简单的“不起作用”来说明为什么,因为该页面可以通过多种方式处理下降。请注意,它不应再与 Firefox 一起使用,因为输入需要难以处理。您应该针对您的问题发布一个问题。
  • @Alex.K.,不能说没有可重复的例子。稍后我会看到使用最新的数据传输 API 更新代码。可能是您没有选择具有预期偏移量的正确元素。
  • @FlorentB。我已经为“react-dropzone.js.org”尝试了您的新代码,但我没有看到,该文件列在“已删除的文件”中。我是手动完成的,然后我可以看到我的文件列在“已删除的文件”中。现在我没有任何 JavaScript 错误,但它不起作用(通过 Selenium 拖放)。有什么想法吗?
【解决方案2】:

前面的答案是正确的,并且与 Chrome 驱动程序完美配合,但是 Mozilla Gecko 驱动程序可能会出现问题,它会抛出 org.openqa.selenium.ElementNotVisibleException

为了避免这种情况,请删除input.style.display = 'none';

如果你需要让它消失,你可以使用input.style.opacity = 0;

【讨论】:

    【解决方案3】:

    如果您使用的是 Selenide:

        public static void dragAndDropFileUpload(File file, SelenideElement target) throws IOException {
    
        String inputId = "seleniumDragAndDropInput";
    
        // Create the FileList
        executeJavaScript(inputId + "_files = [];");
            executeJavaScript(inputId + "_files.push(new File([new Blob(['" + file.getAbsolutePath() + "'], {type: '" + Files.probeContentType(file.toPath()) + "'})], '" + file.getName() + "'));");
    
    
        String targetId = target.getAttribute("id");
    
        // Add an id if the target doesn't have one
        if (targetId == null || targetId.isEmpty()) {
            targetId = "seleniumDragAndDropInput_target";
            executeJavaScript("sId=function(e, i){e.id = i;};sId(arguments[0], arguments[1]);", target, targetId);
        }
    
        // Add the item function the the FileList
        // Create the drop event and dispatch it on the target
        String initEventJS = inputId + "_files.item = function (i) {return this[i];};"
                + "var eve=document.createEvent(\"HTMLEvents\");"
                + "eve.initEvent(\"drop\", true, true);"
                + "eve.dataTransfer = {files:seleniumDragAndDropInput_files};"
                + "eve.preventDefault = function () {};"
                + "eve.type = \"drop\";"
                + "document.getElementById('" + targetId + "').dispatchEvent(eve);";
    
        executeJavaScript(initEventJS);
    
        if (targetId == "seleniumDragAndDropInput_target") {
            executeJavaScript("document.getElementById('seleniumDragAndDropInput_target').id = null");
        }
    }
    

    【讨论】:

    • 我应该补充一点,这是针对 Java 的,但其中大部分只是包装 JS。只需将 'executeJs' 替换为您自己的 JS 执行器实现即可。
    【解决方案4】:

    你可以用 JSExecutor 做到这一点:

    public void dropFile(File filePath, WebElement target, int offsetX, int offsetY) {
            if (!filePath.exists())
                throw new WebDriverException("File not found: " + filePath.toString());
    
            JavascriptExecutor jse = (JavascriptExecutor) driver;
    
            String JS_DROP_FILE =
                    "var target = arguments[0]," +
                            "    offsetX = arguments[1]," +
                            "    offsetY = arguments[2]," +
                            "    document = target.ownerDocument || document," +
                            "    window = document.defaultView || window;" +
                            "" +
                            "var input = document.createElement('INPUT');" +
                            "input.type = 'file';" +
                            "input.style.display = 'none';" +
                            "input.onchange = function () {" +
                            "  var rect = target.getBoundingClientRect()," +
                            "      x = rect.left + (offsetX || (rect.width >> 1))," +
                            "      y = rect.top + (offsetY || (rect.height >> 1))," +
                            "      dataTransfer = { files: this.files };" +
                            "" +
                            "  ['dragenter', 'dragover', 'drop'].forEach(function (name) {" +
                            "    var evt = document.createEvent('MouseEvent');" +
                            "    evt.initMouseEvent(name, !0, !0, window, 0, 0, 0, x, y, !1, !1, !1, !1, 0, null);" +
                            "    evt.dataTransfer = dataTransfer;" +
                            "    target.dispatchEvent(evt);" +
                            "  });" +
                            "" +
                            "  setTimeout(function () { document.body.removeChild(input); }, 25);" +
                            "};" +
                            "document.body.appendChild(input);" +
                            "return input;";
    
            WebElement input = (WebElement) jse.executeScript(JS_DROP_FILE, target, offsetX, offsetY);
            input.sendKeys(filePath.getAbsoluteFile().toString());
            wait.until(ExpectedConditions.stalenessOf(input));
        }
    

    【讨论】:

      猜你喜欢
      • 2017-09-08
      • 1970-01-01
      • 2015-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多