Selenium: Drag and Drop from file system to WebDriver?

后端 未结 4 1553
野的像风
野的像风 2020-12-01 10:56

I have to test a web-application which contains a drag and drop area for uploading files from the local file system. My test environment is based on C#.

For the auto

相关标签:
4条回答
  • 2020-12-01 11:25

    It's possible with Selenium alone, but it's not simple. It requires to inject a new INPUT element in the page to receive the file through SendKeys. Then, the script needs to simulate the drop by sending the dragenter, dragover, drop events to the targeted area.

    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);
    }
    

    Source: https://gist.github.com/florentbr/349b1ab024ca9f3de56e6bf8af2ac69e

    0 讨论(0)
  • 2020-12-01 11:27

    You can do this with 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));
        }
    
    0 讨论(0)
  • 2020-12-01 11:42

    If you're using 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");
        }
    }
    
    0 讨论(0)
  • 2020-12-01 11:44

    The previous answer is correct and works perfectly with the Chrome driver, however might have problems with Mozilla Gecko driver, which throws org.openqa.selenium.ElementNotVisibleException

    In order to avoid that, remove input.style.display = 'none';

    You can use input.style.opacity = 0; if you need to make it disappear.

    0 讨论(0)
提交回复
热议问题