Drag and drop in cucumber with selenium

I needed to show a bug using a cucumber script which includes drag and drop. Turned out to be difficult for several reasons

  • drag and drop is not supported by webrat for selenium
  • ‘within’ for scoping is not supported by webrat for selenium

this made the feature difficult to quickly specifiy. The feature contains:

[...]
And I drag "Todo 3" to "Todo 2"
[...]
When I expand the dependencies of "Todo 1"
Then I should see "Todo 2" within the dependencies of "Todo 1"
[...]
For the drag you can do
drag_id = Todo.find_by_description(dragged).id
drop_id = Todo.find_by_description(target).id
drag_name = "xpath=//div[@id='line_todo_#{drag_id}']//img[@class='grip']"
drop_name = "xpath=//div[@id='line_todo_#{drop_id}']//div[@class='description']"
selenium.drag_and_drop_to_object(drag_name, drop_name)
This will work, but drag_and_drop does not wait for the ajax that is attached to the drop to finish. There is a wait_for_ajax, but this only works with Prototype, not with jQuery. If found a solution which is a bit of a hack, so I reverted to waiting_for the dependency arrow to appear:
arrow = "xpath=//div[@id='line_todo_#{drop_id}']/div/a[@class='show_successors']/img"
selenium.wait_for_element(arrow)
The next hurdle was to check if “Todo 1” as a dependency is shown in the dependency tree of “Todo 2”. There is a standard “I should see … within …” which comes with Cucumber, but unfortunately the webrat ‘within’ does not work for selenium :-(. Thus, this does not work:
xpath = "//div[@id='line_todo_#{todo.id}'"
Then "I should see \"#{successor_description}\" within \"xpath=#{xpath}\""
So I reverted to a xpath query on “Todo 1” within “Todo 2” like this:
xpath = "xpath=//div[@id='line_todo_#{todo.id}']//div[@id='successor_line_todo_#{successor.id}']//span"
selenium.wait_for_element(xpath)

UPDATE: In Tracks Eric added a change to show an image where to drop a todo. Problem is that the image only appears when dragging starts. Selenium does not have semantics for this, i.e. the drop target cannot be found when it is hidden before calling drag_and_drop_to_object.

I needed to ‘fix’ this by showing the image before I started the drag and drop. Somehow I also needed to use the id of the img. An xpath with the class did not seem to work:

  drag_name = "xpath=//div[@id='line_todo_#{drag_id}']//img[@class='grip']"
  drop_name = "xpath=//div[@id='line_todo_#{drop_id}']//img[@id='successor_target_#{drop_id}']"

  # the target img is hidden until drag starts. We need to show the img or the
  # xpath will not find it
  js="$('img#successor_target_#{drop_id}').show();"
  selenium.get_eval "(function() {with(this) {#{js}}}).call(selenium.browserbot.getCurrentWindow());"

  selenium.drag_and_drop_to_object(drag_name, drop_name)

Resulting patch here.

Explore posts in the same categories: Tracks

Comment: