Capybaraのdrag_toを使ってドラッグアンドドロップのテストを書いたら動かなかったのでSeleniumのdrag_and_drop_byを使って書いた

Request SpecとCapybaraで、jQuery UI のSortableを使った並び替え部分のエンドツーエンドテストを書いていて、ハマったのでメモ。


Capybaraのドキュメントを見てみるとdrag_toというメソッドがあったので、これを使ってみたけど、ドラッグアンドドロップされなかった。動き的には、ドラッグはしてるけど、ドロップした時に元の場所に戻ってる感じかな?

ちなみにコードはこんな感じ

     #drag&dropされない
     it "リストを並び替えると、「item2」が一番上に表示されること", :js => true do
        click_link "リストを並び替える" # 並び替えの画面に移動
        source = page.find_by_id("item_#{items(:item2).id}")
        target = page.find_by_id("item_#{items(:item1).id}")
        source.drag_to(target)
        within("#jquery-ui-sortable li:first-child") do
          page.should have_content("item2")
        end
      end


で、drag_toのソースコードを確認してみると、実際にはこれが呼ばれている。

resynchronize { driver.browser.action.drag_and_drop(native, element.native).perform }

Seleniumのメソッドなので、Seleniumドキュメントのdrag_and_dropを確認してみる。

def drag_and_drop(source, target)
  click_and_hold source
  move_to        target
  release        target

  self
end

drag_and_dropの中身で呼ばれているメソッドにバラして、なぞってみたりしたけど、やはりドラッグアンドドロップできていない。


他にいいメソッドはないかと、Seleniumのドキュメントでdragと入れて調べてみると、drag_and_drop_byという移動距離を指定するメソッドがあったので、これを使ってやってみた。

     it "リストを並び替えると、「item2」が一番上に表示されること", :js => true do
        click_link "リストを並び替える" # 並び替えの画面に移動
        source = page.find_by_id("item_#{items(:item2).id}")
        page.driver.browser.action.drag_and_drop_by(source.native, 10,-30).perform
        within("#jquery-ui-sortable li:first-child") do
          page.should have_content("item2")
        end
      end

ドラッグの移動距離を大きくしすぎると、scroll出来なかったりで、失敗したけど、適切な移動距離を指定したら動きました。

Capybaraのissueでdragと入れてググると、わりと既知な話だったみたい。
https://github.com/jnicklas/capybara/issues/119
https://github.com/jnicklas/capybara/issues/216
https://github.com/jnicklas/capybara/issues/222
https://github.com/jnicklas/capybara/issues/477

以上。