lib/capybara/selenium/extensions/html5_drag.rb



# frozen_string_literal: true

class Capybara::Selenium::Node
  module Html5Drag
  private

    def html5_drag_to(element)
      driver.execute_script MOUSEDOWN_TRACKER
      scroll_if_needed { browser_action.click_and_hold(native).perform }
      if driver.evaluate_script('window.capybara_mousedown_prevented')
        element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
      else
        driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
        browser_action.release.perform
      end
    end

    def html5_draggable?
      # Workaround https://github.com/SeleniumHQ/selenium/issues/6396
      native.property('draggable')
    end

    MOUSEDOWN_TRACKER = <<~JS
      document.addEventListener('mousedown', ev => {
        window.capybara_mousedown_prevented = ev.defaultPrevented;
      }, { once: true, passive: true })
    JS

    HTML5_DRAG_DROP_SCRIPT = <<~JS
      var source = arguments[0];
      var target = arguments[1];

      var dt = new DataTransfer();
      var opts = { cancelable: true, bubbles: true, dataTransfer: dt };

      if (source.tagName == 'A'){
        dt.setData('text/uri-list', source.href);
        dt.setData('text', source.href);
      }
      if (source.tagName == 'IMG'){
        dt.setData('text/uri-list', source.src);
        dt.setData('text', source.src);
      }
      var dragEvent = new DragEvent('dragstart', opts);
      source.dispatchEvent(dragEvent);
      target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
      var dragOverEvent = new DragEvent('dragover', opts);
      target.dispatchEvent(dragOverEvent);
      var dragLeaveEvent = new DragEvent('dragleave', opts);
      target.dispatchEvent(dragLeaveEvent);
      if (dragOverEvent.defaultPrevented) {
        var dropEvent = new DragEvent('drop', opts);
        target.dispatchEvent(dropEvent);
      }
      var dragEndEvent = new DragEvent('dragend', opts);
      source.dispatchEvent(dragEndEvent);
    JS
  end
end