Capybara 'drag & drop' does not work when the elements moved are too close in Rails

Using Rails + Vue.js for a project, and there is a bug with Capybara's 'drag & drop' feature while testing with Rspec. This is a workaround on how to fix it.

Capybara's 'Drag & Drop' feature works perfectly when the items being dragged are not too close. 

For example, in the code below, I am dragging the image from one product to another.  And it works as expected. 

feature "dragging image", :vcr, js: true  do
  before(:each) do 
    @apple = Fabricate(:shop)
    @apple.products.delete_all
    
    @ipod = Fabricate(:product, shop: @apple)
    @ipod_image_src = "https://cdn2.iconfinder.com/data/icons/gadget-linicons/100/iPod_Nano-512.png"
    @ipod_image = Image.create(position: 1, src: @ipod_image_src, product: @ipod, shop: @apple)
  end
   
  scenario "from one product to another product" do
    second_ipod_without_initial_image = Fabricate(:product, shop: @apple)
    sign_in(@apple)
    
    ipod_image_element = find("#product_image_#{@ipod.id}")     
    second_ipod_without_initial_image_element = find("#product_non_image_#{second_ipod_without_initial_image.id}")
    
    ipod_image_element.drag_to second_ipod_without_initial_image_element
    
    sleep 0.3
    
    @apple.reload
    expect(@apple.products.first.images.first.src).to eq(@apple.products.second.images.first.src)
  end  

This is the resultant feature test, and it works as expected. 
Image

But when I do the same and try to drag an image within the same product using Capybara, the test fails.

Below is the feature test. 
Image

If you look closely, you can see that the drag does not persist. It is immediately reverted back to how it was before.  This is happening even though the code works well in production. The tests are failing, and this is a bug in Capybara's drag and drop.
Image

One easy way to fix this is to create some dummy images between the items you want to drag. 

I created a method 'create_many_images' which does this in my spec. 
scenario "within the same product" do
    last_ipod_image_src = "https://secure.webtoolhub.com/static/resources/icons/set35/a606baef.png"
    last_ipod_image = Image.create(position: 2, src: last_ipod_image_src, product: @ipod, shop: @apple)
    create_many_more_images
    
    sign_in(@apple)
    
    ipod_image_element = find("#product_image_#{@ipod_image.id}")     
    last_ipod_image_element = find("#product_image_#{last_ipod_image.id}")     
     
    ipod_image_element.drag_to last_ipod_image_element
    
    sleep 0.3
    
    @ipod_image.reload
    expect(@ipod_image.position).to_not eq(1)
  end  

And all create_many_more_images does is that it creates 10 images. 
def create_many_more_images
 10.times do |i|
   src = "image_#{i}"
   Image.create(position: 2, src: src, product: @ipod, shop: @apple)
 end
end

Once I do this, all the tests pass, and if you look closely - you can see that the dragged items persists unlike before.


Image


Weekly Newsletter

Subscribe to get my weekly newsletter and latest articles on startups, marketing and growth.

Hello 👋! I'm Arjun, a remote Ruby on Rails developer that can bring your digital product ideas to life.

Here is a list of things i've built recently.