Selenium: Drag and Drop from file system to WebDriver?
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
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.
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));
}