Skip to content

Commit

Permalink
Fix pending 'reorder redirects' cucumber test
Browse files Browse the repository at this point in the history
  • Loading branch information
artemave committed Mar 20, 2012
1 parent b173726 commit f451cef
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 38 deletions.
50 changes: 25 additions & 25 deletions Gemfile.lock
Expand Up @@ -12,23 +12,23 @@ PATH
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
activemodel (3.2.1) activemodel (3.2.2)
activesupport (= 3.2.1) activesupport (= 3.2.2)
builder (~> 3.0.0) builder (~> 3.0.0)
activerecord (3.2.1) activerecord (3.2.2)
activemodel (= 3.2.1) activemodel (= 3.2.2)
activesupport (= 3.2.1) activesupport (= 3.2.2)
arel (~> 3.0.0) arel (~> 3.0.2)
tzinfo (~> 0.3.29) tzinfo (~> 0.3.29)
activeresource (3.2.1) activeresource (3.2.2)
activemodel (= 3.2.1) activemodel (= 3.2.2)
activesupport (= 3.2.1) activesupport (= 3.2.2)
activesupport (3.2.1) activesupport (3.2.2)
i18n (~> 0.6) i18n (~> 0.6)
multi_json (~> 1.0) multi_json (~> 1.0)
addressable (2.2.7) addressable (2.2.7)
archive-tar-minitar (0.5.2) archive-tar-minitar (0.5.2)
arel (3.0.0) arel (3.0.2)
awesome_print (1.0.2) awesome_print (1.0.2)
builder (3.0.0) builder (3.0.0)
capybara (1.1.2) capybara (1.1.2)
Expand All @@ -43,18 +43,18 @@ GEM
childprocess (0.3.1) childprocess (0.3.1)
ffi (~> 1.0.6) ffi (~> 1.0.6)
columnize (0.3.6) columnize (0.3.6)
cucumber (1.1.4) cucumber (1.1.9)
builder (>= 2.1.2) builder (>= 2.1.2)
diff-lcs (>= 1.1.2) diff-lcs (>= 1.1.2)
gherkin (~> 2.7.1) gherkin (~> 2.9.0)
json (>= 1.4.6) json (>= 1.4.6)
term-ansicolor (>= 1.0.6) term-ansicolor (>= 1.0.6)
daemons (1.1.8) daemons (1.1.8)
database_cleaner (0.7.1) database_cleaner (0.7.1)
diff-lcs (1.1.3) diff-lcs (1.1.3)
eventmachine (0.12.10) eventmachine (0.12.10)
ffi (1.0.11) ffi (1.0.11)
gherkin (2.7.7) gherkin (2.9.0)
json (>= 1.4.6) json (>= 1.4.6)
growl (1.0.3) growl (1.0.3)
guard (1.0.0) guard (1.0.0)
Expand All @@ -72,12 +72,12 @@ GEM
addressable (~> 2.2.6) addressable (~> 2.2.6)
linecache (0.46) linecache (0.46)
rbx-require-relative (> 0.0.4) rbx-require-relative (> 0.0.4)
linecache19 (0.5.13) linecache19 (0.5.12)
ruby_core_source (>= 0.1.4) ruby_core_source (>= 0.1.4)
mime-types (1.17.2) mime-types (1.17.2)
multi_json (1.0.4) multi_json (1.1.0)
mysql2 (0.3.11) mysql2 (0.3.11)
nokogiri (1.5.0) nokogiri (1.5.2)
pg (0.13.2) pg (0.13.2)
rack (1.4.1) rack (1.4.1)
rack-protection (1.2.0) rack-protection (1.2.0)
Expand All @@ -87,7 +87,7 @@ GEM
rake (0.9.2.2) rake (0.9.2.2)
rb-fsevent (0.9.0) rb-fsevent (0.9.0)
rb-readline (0.4.2) rb-readline (0.4.2)
rbx-require-relative (0.0.5) rbx-require-relative (0.0.9)
relish (0.5.3) relish (0.5.3)
archive-tar-minitar (>= 0.5.2) archive-tar-minitar (>= 0.5.2)
json (>= 1.4.6) json (>= 1.4.6)
Expand All @@ -107,7 +107,7 @@ GEM
ruby-debug-base (~> 0.10.4.0) ruby-debug-base (~> 0.10.4.0)
ruby-debug-base (0.10.4) ruby-debug-base (0.10.4)
linecache (>= 0.3) linecache (>= 0.3)
ruby-debug-base19 (0.11.26) ruby-debug-base19 (0.11.25)
columnize (>= 0.3.1) columnize (>= 0.3.1)
linecache19 (>= 0.5.11) linecache19 (>= 0.5.11)
ruby_core_source (>= 0.1.4) ruby_core_source (>= 0.1.4)
Expand All @@ -118,14 +118,14 @@ GEM
ruby_core_source (0.1.5) ruby_core_source (0.1.5)
archive-tar-minitar (>= 0.5.2) archive-tar-minitar (>= 0.5.2)
rubyzip (0.9.6.1) rubyzip (0.9.6.1)
selenium-webdriver (2.19.0) selenium-webdriver (2.20.0)
childprocess (>= 0.2.5) childprocess (>= 0.2.5)
ffi (~> 1.0.9) ffi (~> 1.0)
multi_json (~> 1.0.4) multi_json (~> 1.0)
rubyzip rubyzip
shoulda-matchers (1.0.0) shoulda-matchers (1.0.0)
simplecov (0.5.4) simplecov (0.6.1)
multi_json (~> 1.0.3) multi_json (~> 1.0)
simplecov-html (~> 0.5.3) simplecov-html (~> 0.5.3)
simplecov-html (0.5.3) simplecov-html (0.5.3)
sinatra (1.3.2) sinatra (1.3.2)
Expand All @@ -146,7 +146,7 @@ GEM
rack (>= 1.0.0) rack (>= 1.0.0)
thor (0.14.6) thor (0.14.6)
tilt (1.3.3) tilt (1.3.3)
tzinfo (0.3.31) tzinfo (0.3.32)
xpath (0.1.4) xpath (0.1.4)
nokogiri (~> 1.3) nokogiri (~> 1.3)


Expand Down
12 changes: 5 additions & 7 deletions features/step_definitions/redirect_rules_steps.rb
Expand Up @@ -46,19 +46,17 @@
end end


When /^I reorder second redirect to be the first one$/ do When /^I reorder second redirect to be the first one$/ do
handler = find("#redirects #redirect_#{RestAssured::Models::Redirect.last.id} td.handle") page.execute_script %{
target = find('#redirects thead') $('#redirects #redirect_#{RestAssured::Models::Redirect.order('position').last.id}').simulateDragSortable({move: -1, handle: '.handle'})

}
handler.drag_to target sleep 2
end end


Then /^"([^"]*)" should be redirected to "([^"]*)"$/ do |missing_request, url| Then /^"([^"]*)" should be redirected to "([^"]*)"$/ do |missing_request, url|
pending('This does not pass due to Capybara/Selelium broken drag and drop support')

get missing_request get missing_request
follow_redirect! follow_redirect!


last_request.url.should == "#{url}#{missing_request}" last_request.url.should == url
end end


Given /^blank slate$/ do Given /^blank slate$/ do
Expand Down
4 changes: 2 additions & 2 deletions features/web_ui/redirects.feature
Expand Up @@ -49,9 +49,9 @@ Feature: manage redirects via ui
| /api/bbb | http://twitter.com/api | | /api/bbb | http://twitter.com/api |
And I am on "redirects" page And I am on "redirects" page
When I reorder second redirect to be the first one When I reorder second redirect to be the first one
Then "/api/bbb" should be redirected to "http://twitter.com/api" Then "/api/bbb/ccc" should be redirected to "http://twitter.com/api/ccc"
When I reorder second redirect to be the first one When I reorder second redirect to be the first one
Then "/api/bbb" should be redirected to "http://google.com/api" Then "/api/bbb/ccc" should be redirected to "http://google.com/api"


@javascript @javascript
Scenario: delete redirect Scenario: delete redirect
Expand Down
2 changes: 1 addition & 1 deletion lib/rest-assured/version.rb
@@ -1,3 +1,3 @@
module RestAssured module RestAssured
VERSION = '1.1.2' VERSION = '1.1.3'
end end
235 changes: 235 additions & 0 deletions public/javascript/jquery.simulate.drag-sortable.js
@@ -0,0 +1,235 @@
(function($) {
/*
* Simulate drag of a JQuery UI sortable list
* Repository: https://github.com/mattheworiordan/jquery.simulate.drag-sortable.js
* Author: http://mattheworiordan.com
*
* options are:
* - move: move item up (positive) or down (negative) by Integer amount
* - dropOn: move item to a new linked list, move option now represents position in the new list (zero indexed)
* - handle: selector for the draggable handle element (optional)
* - listItem: selector to limit which sibling items can be used for reordering
* - placeHolder: if a placeholder is used during dragging, we need to consider it's height
* - tolerance: (optional) number of pixels to overlap by instead of the default 50% of the element height
*
*/
$.fn.simulateDragSortable = function(options) {
// build main options before element iteration
var opts = $.extend({}, $.fn.simulateDragSortable.defaults, options);

applyDrag = function(options) {
// allow for a drag handle if item is not draggable
var that = this,
options = options || opts, // default to plugin opts unless options explicitly provided
handle = options.handle ? $(this).find(options.handle)[0] : $(this)[0],
listItem = options.listItem,
placeHolder = options.placeHolder,
sibling = $(this),
moveCounter = Math.floor(options.move),
direction = moveCounter > 0 ? 'down' : 'up',
moveVerticalAmount = 0,
initialVerticalPosition = 0,
extraDrag = !isNaN(parseInt(options.tolerance, 10)) ? function() { return Number(options.tolerance); } : function(obj) { return ($(obj).outerHeight() / 2) + 5; },
dragPastBy = 0, // represents the additional amount one drags past an element and bounce back
dropOn = options.dropOn ? $(options.dropOn) : false,
center = findCenter(handle),
x = Math.floor(center.x),
y = Math.floor(center.y),
mouseUpAfter = (opts.debug ? 2500 : 10);

if (dropOn) {
if (dropOn.length === 0) {
if (console && console.log) { console.log('simulate.drag-sortable.js ERROR: Drop on target could not be found'); console.log(options.dropOn); }
return;
}
sibling = dropOn.find('>*:last');
moveCounter = -(dropOn.find('>*').length + 1) + (moveCounter + 1); // calculate length of list after this move, use moveCounter as a positive index position in list to reverse back up
if (dropOn.offset().top - $(this).offset().top < 0) {
// moving to a list above this list, so move to just above top of last item (tried moving to top but JQuery UI wouldn't bite)
initialVerticalPosition = sibling.offset().top - $(this).offset().top - extraDrag(this);
} else {
// moving to a list below this list, so move to bottom and work up (JQuery UI does not trigger new list below unless you move past top item first)
initialVerticalPosition = sibling.offset().top - $(this).offset().top - $(this).height();
}
} else if (moveCounter === 0) {
if (console && console.log) { console.log('simulate.drag-sortable.js WARNING: Drag with move set to zero has no effect'); }
return;
} else {
while (moveCounter !== 0) {
if (direction === 'down') {
if (sibling.next(listItem).length) {
sibling = sibling.next(listItem);
moveVerticalAmount += sibling.outerHeight();
}
moveCounter -= 1;
} else {
if (sibling.prev(listItem).length) {
sibling = sibling.prev(listItem);
moveVerticalAmount -= sibling.outerHeight();
}
moveCounter += 1;
}
}
}

dispatchEvent(handle, 'mousedown', createEvent('mousedown', handle, { clientX: x, clientY: y }));
// simulate drag start
dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x+1, clientY: y+1 }));

if (dropOn) {
// jump to top or bottom of new list but do it in increments so that JQuery UI registers the drag events
slideUpTo(x, y, initialVerticalPosition);

// reset y position to top or bottom of list and move from there
y += initialVerticalPosition;

// now call regular shift/down in a list
options = jQuery.extend(options, { move: moveCounter });
delete options.dropOn;

// add some delays to allow JQuery UI to catch up
setTimeout(function() {
dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y }));
}, 5);
setTimeout(function() {
dispatchEvent(handle, 'mouseup', createEvent('mouseup', handle, { clientX: x, clientY: y }));
setTimeout(function() {
if (options.move) {
applyDrag.call(that, options);
}
}, 5);
}, mouseUpAfter);

// stop execution as applyDrag has been called again
return;
}

// Sortable is using a fixed height placeholder meaning items jump up and down as you drag variable height items into fixed height placeholder
placeHolder = placeHolder && $(this).parent().find(placeHolder);

if (!placeHolder && (direction === 'down')) {
// need to move at least as far as this item and or the last sibling
if ($(this).outerHeight() > $(sibling).outerHeight()) {
moveVerticalAmount += $(this).outerHeight() - $(sibling).outerHeight();
}
moveVerticalAmount += extraDrag(sibling);
dragPastBy += extraDrag(sibling);
} else if (direction === 'up') {
// move a little extra to ensure item clips into next position
moveVerticalAmount -= Math.max(extraDrag(this), 5);
} else if (direction === 'down') {
// moving down with a place holder
if (placeHolder.height() < $(this).height()) {
moveVerticalAmount += Math.max(placeHolder.height(), 5);
} else {
moveVerticalAmount += extraDrag(sibling);
}
}

if (sibling[0] !== $(this)[0]) {
// step through so that the UI controller can determine when to show the placeHolder
slideUpTo(x, y, moveVerticalAmount, dragPastBy);
} else {
if (window.console) {
console.log('simulate.drag-sortable.js WARNING: Could not move as at top or bottom already');
}
}

setTimeout(function() {
dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + moveVerticalAmount }));
}, 5);
setTimeout(function() {
dispatchEvent(handle, 'mouseup', createEvent('mouseup', handle, { clientX: x, clientY: y + moveVerticalAmount }));
}, mouseUpAfter);
};

// iterate and move each matched element
return this.each(applyDrag);
};

// fire mouse events, go half way, then the next half, so small mouse movements near target and big at the start
function slideUpTo(x, y, targetOffset, goPastBy) {
var moveBy, offset;

if (!goPastBy) { goPastBy = 0; }
if ((targetOffset < 0) && (goPastBy > 0)) { goPastBy = -goPastBy; } // ensure go past is in the direction as often passed in from object height so always positive

// go forwards including goPastBy
for (offset = 0; Math.abs(offset) + 1 < Math.abs(targetOffset + goPastBy); offset += ((targetOffset + goPastBy - offset)/2) ) {
dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + Math.ceil(offset) }));
}
offset = targetOffset + goPastBy;
dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + offset }));

// now bounce back
for (; Math.abs(offset) - 1 >= Math.abs(targetOffset); offset += ((targetOffset - offset)/2) ) {
dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + Math.ceil(offset) }));
}
dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + targetOffset }));
}

function createEvent(type, target, options) {
var evt;
var e = $.extend({
target: target,
preventDefault: function() { },
stopImmediatePropagation: function() { },
stopPropagation: function() { },
isPropagationStopped: function() { return true; },
isImmediatePropagationStopped: function() { return true; },
isDefaultPrevented: function() { return true; },
bubbles: true,
cancelable: (type != "mousemove"),
view: window,
detail: 0,
screenX: 0,
screenY: 0,
clientX: 0,
clientY: 0,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
button: 0,
relatedTarget: undefined
}, options || {});

if ($.isFunction(document.createEvent)) {
evt = document.createEvent("MouseEvents");
evt.initMouseEvent(type, e.bubbles, e.cancelable, e.view, e.detail,
e.screenX, e.screenY, e.clientX, e.clientY,
e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
e.button, e.relatedTarget || document.body.parentNode);
} else if (document.createEventObject) {
evt = document.createEventObject();
$.extend(evt, e);
evt.button = { 0:1, 1:4, 2:2 }[evt.button] || evt.button;
}
return evt;
}

function dispatchEvent(el, type, evt) {
if (el.dispatchEvent) {
el.dispatchEvent(evt);
} else if (el.fireEvent) {
el.fireEvent('on' + type, evt);
}
return evt;
}

function findCenter(el) {
var elm = $(el),
o = elm.offset();
return {
x: o.left + elm.outerWidth() / 2,
y: o.top + elm.outerHeight() / 2
};
}

//
// plugin defaults
//
$.fn.simulateDragSortable.defaults = {
move: 0
};
})(jQuery);

0 comments on commit f451cef

Please sign in to comment.