8

I want to test file upload, by dragging file to the drop zone in the page, however I can't find a way to simulate file dragging from the desktop folder. The only way I managed to found is the following one -

desktop.browser.actions().dragAndDrop(elem,target).mouseUp().perform();(Protractor)

However as far as I can understand, it drags only css element.

renchan
  • 519
  • 5
  • 24

2 Answers2

12

This is a working example to simulate a file drop from the desktop to a drop area:

const dropFile = require("./drop-file.js");
const EC = protractor.ExpectedConditions;

browser.ignoreSynchronization = true;

describe('Upload tests', function() {

  it('should drop a file to a drop area', function() {

    browser.get('http://html5demos.com/file-api');

    // drop an image file on the drop area
    dropFile($("#holder"), "./image.png");

    // wait for the droped image to be displayed in the drop area
    browser.wait(EC.presenceOf($("#holder[style*='data:image']")));
  });

});

The content of drop-file.js :

var fs = require('fs');
var path = require('path');

var JS_BIND_INPUT = function (target) {
  var input = document.createElement('input');
  input.type = 'file';
  input.style.display = 'none';
  input.addEventListener('change', function () {
    target.scrollIntoView(true);

    var rect = target.getBoundingClientRect(),
      x = rect.left + (rect.width >> 1),
      y = rect.top + (rect.height >> 1),
      data = { files: input.files };

    ['dragenter','dragover','drop'].forEach(function (name) {
      var event = document.createEvent('MouseEvent');
      event.initMouseEvent(name, !0, !0, window, 0, 0, 0, x, y, !1, !1, !1, !1, 0, null);
      event.dataTransfer = data;
      target.dispatchEvent(event);
    });

    document.body.removeChild(input);
  }, false);

  document.body.appendChild(input);
  return input;
};


/**
 * Support function to drop a file to a drop area.
 *
 * @view
 * <div id="drop-area"></div>
 *
 * @example
 * dropFile($("#drop-area"), "./image.png");
 *
 * @param {ElementFinder} drop area
 * @param {string} file path
 */
module.exports = function (dropArea, filePath) {
  // get the full path
  filePath = path.resolve(filePath);

   // assert the file is present
  fs.accessSync(filePath, fs.F_OK);

  // resolve the drop area
  return dropArea.getWebElement().then(function (element) {

    // bind a new input to the drop area
    browser.executeScript(JS_BIND_INPUT, element).then(function (input) {

      // upload the file to the new input
      input.sendKeys(filePath);

    });
  });
};
Florent B.
  • 41,537
  • 7
  • 86
  • 101
  • 4
    sheesh thats complicated – SuperUberDuper Jul 04 '17 at 17:02
  • 1
    is in't this code manipulating the client html DOM, does this actually validate the real application behavior? the same code could be used to create new buttons, links or whatever we want in the test application – PDHide Nov 28 '19 at 13:34
  • 1
    @PDHide, the code simulates the same events as if it were a real user. It doesn't interact with the code from the page and it doesn't check whether the page did something with those events. To validate the feature, you'll have to add some assertions from a user perspective to validate that the drop was successful (new message on the page..). For a more recent version: https://gist.github.com/florentbr/349b1ab024ca9f3de56e6bf8af2ac69e – Florent B. Nov 28 '19 at 16:06
  • @FlorentB. Document.body.append is adding to the Dom right ? Or what is it actually doing ? – PDHide Nov 28 '19 at 20:13
  • Could you just explain the basic logic – PDHide Nov 28 '19 at 20:15
  • @PDHide, the code injects an element in the DOM. The file is then attached to this `` via Selenium with sendKeys. Once the `` has the file, the code removes the `` and emits the events to simulate the drag&drop. – Florent B. Nov 29 '19 at 09:01
  • Ok so you are manipulating the DOM to generate the event and once event is generated you are removing the input element ? thanks @FlorentB. – PDHide Nov 29 '19 at 10:18
  • Hello @FlorentB. thanks for your code, is there an adaption for angular, i'm trying to use it in protractor but i got the following error : ```Error: Cannot find module './drop-file.js'``` – Benjamin Barbé Jun 28 '20 at 22:52
  • Btw I explained my problem here: https://stackoverflow.com/questions/62629229/cannot-find-module-drop-file-js-protractor-angular @FlorentB. – Benjamin Barbé Jun 28 '20 at 23:17
4

You cannot drag an element from your desktop using protractor, its actions are limited to the browser capabilities.

You may have to consider dragging from the desktop to work (unless you want to test your operating system), and check that once the file is given to the HTML element, everything works correctly.

One way to achieve that is with the following:

dropElement.sendKeys(path);

For instance if that element is, as usual, an input of type file:

$('input[type="file"]').sendKeys(path);

Note that path should be the absolute path to the file you want to upload, such as /Users/me/foo/bar/myFile.json or c:\foo\bar\myFile.json.

floribon
  • 19,175
  • 5
  • 54
  • 66
  • You should reconsider you statement. It's possible to drop a file to a drop area with protractor alone. – Florent B. May 31 '16 at 17:46
  • @FlorentB. do you have a specific example to support the statement? Thanks. – alecxe May 31 '16 at 17:52
  • @alex, I've done it, but I'm not going to add an example in a comment. It requires to inject a new element in the page with .executeScript to obtain the file. Then send the drop event with the file attached to the drop zone once the file is uploaded with .sendKeys. – Florent B. May 31 '16 at 18:22
  • @FlorentB. I never said that drag and drop is not doable with protractor, as long as it's within the browser. What is not possible is to emulate a drag and drop from a desktop file - on the host computer - into the browser window. We can only simulate the behavior that happens on the browser, using `sendKeys` as you said, which happens to be my answer as well. – floribon May 31 '16 at 20:02
  • @floribon, It is possible to emulate a drag and drop from a desktop file, on the host computer, into the browser's window with protractor. Usually a drop area is a simple `div` without an `input`. So all it takes to simulate the drop is to send the `dragenter`, `dragover` and `drop` events to the div with the file define in `dataTransfer`. So a simple `dropElement.sendKeys(path);` or `$('input[type="file"]').sendKeys(path);` is not going to work. See by yourself and try to drop a file in this example: http://html5demos.com/file-api – Florent B. Jun 01 '16 at 00:22
  • @FlorentB. I see, you are totally right! Indeed if the implementation of the drag drop doesn't include an `input` elem but relies on a `drop` event, we must execute a script to feed that listener with a dataTransfer. Feel free to add an answer or edit mine, thanks for pointing that out!! – floribon Jun 01 '16 at 08:42