12

I have to automate a web-application, which contains a drag and drop area for uploading files from the local file system. My test environment is developed using Python. For the automation tests I have used Selenium, but it is not possible to drag files from the file system, once the upload area is a div tag (No input tag - this way I know it would be easy).

I read a lot of different articles, but by the moment none worked for me. It's important to highlight that I'm not interested in using AutoIT, only native python with selenium.

I found this Selenium: Drag and Drop from file system to webdriver? what looks really promising, however I do not know to adapt to Python.

Thank you a lot in advance!

Community
  • 1
  • 1
FelipeG
  • 121
  • 1
  • 1
  • 4
  • 1
    File managers are all desktop GUI apps, so it's impossible to automate them using Selenium if there is no special trick inside WebDriver. If you're on Windows, `explorer.exe` can be automated using [pywinauto](http://pywinauto.github.io/). My student wrote [an example that drags-n-drops the file](https://github.com/vasily-v-ryabov/ui-automation-course/blob/master/02_google_drive_Murashov/test_explorer_google_drive.py) from `explorer.exe` to Chrome (Google Disk). Can it be helpful? – Vasily Ryabov Apr 15 '17 at 16:52
  • To be honest, I would like to know how to inject a new INPUT element in the page to receive the file through SendKeys. I know it is possible with c# and java, however I have no idea how can I manage it with python. – FelipeG Apr 16 '17 at 04:53

2 Answers2

32

Here's the python version of the trick with input injection via script.

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.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;
"""

def drag_and_drop_file(drop_target, path):
    driver = drop_target.parent
    file_input = driver.execute_script(JS_DROP_FILE, drop_target, 0, 0)
    file_input.send_keys(path)

As drop_target pass it some element visible on the page which you can get using any function from the family of driver.get_element_by_....

The approach is to invoke a javascript using selenium's execute_script function to emulate drag and drop events. The code works as following:

  1. selenium invokes javascript code
  2. javascript creates input element and attaches it to DOM
  3. javascript attaches a handler to the input which emulates mouse events that happens when user actually drops a file, namely dragenter, dragover, drop.
  4. selenium updates the input with the path to the file. At this point the handler from step 2 is invoked and it emulates drag and drop events.
  • 4
    Excellent. Worked for me. Should be the accepted answer. – Zaid Afzal Jul 08 '19 at 09:08
  • 4
    People who do this kind of things <3 – romainm Dec 27 '19 at 18:36
  • when you call "drag_and_drop_file" what value do you pass for "drop_target"? – Alex Alves Jun 17 '20 at 23:51
  • For test automation purposes, adding the element directly into the body like its on @RomanKonoval 's comment will create small jerkiness when input element is received. Somewhat better approach would be to get parentElement of target and add/remove it there directly. – rasjani Jun 23 '20 at 09:50
  • Can you add some explanation of what this thing does? – Alon Gouldman Sep 25 '20 at 05:39
  • 1
    I know this is a late comment but I had a similar problem. I was stuck on my code for hours trying to find a solution until I found this answer. I tried it and it worked like a charm. Thanks for the answer. – Daniel Afriyie Oct 31 '21 at 20:03
  • Just clarifying here (since it took me a few minutes to figure out) that `drop_target` can be found by calling one of the `driver.get_element_by_....` functions – acciolurker Jan 27 '22 at 22:03
  • 1
    This solution still rings true in 2023! Props to the author of this :clap: – Luke Shinn May 11 '23 at 16:15
1

I know this may be a late answer but just in case for people who find the answer!

If you are using Chrome please go to this site to download the Chrome driver. (Try to find your chrome version thru this and choose the suitable one)

There's still another thing you will need to do I'll show it right now


First: Download chrome driver and copy the Xpath

Step 1: Go to the site you want and copy fullXPath of your "drag and drop", by right click on the drag and drop area then hit the inspect.

Plz do this twice just in case it inspects the right place

Step 2: You will see the highlight color, again right-click on them

then you will find "copy" -> "copy fullXpath"

Finally, let's code

Wait!!! Just one more suggestion plz : If you see something goes wrong with pasting the "Xpath" or "link to the folder" for example

you might use ' ' instead of " "

from selenium import webdriver
driver = webdriver.Chrome('D:\Folder\chromedriver')
driver.get('https://exmaple.com')
drag_&_drop = driver.find_element_by_xpath('paste-the-full-xpath-here')
drag_&_drop.send_keys('D:\Folder\picture.png')
#python 3.9
Ralph
  • 29
  • 4