1
0
Fork 0
mirror of https://github.com/teampoltergeist/poltergeist.git synced 2022-11-09 12:05:00 -05:00

Implement #hover

This commit is contained in:
Jon Leighton 2013-03-03 15:18:05 +00:00
parent 5546d0344c
commit 6c52e69ca1
14 changed files with 88 additions and 66 deletions

View file

@ -238,7 +238,7 @@ If you experience sporadic crashes a lot, it may be worth configuring
your CI to automatically re-run failing tests before reporting a failed
build.
### ClickFailed errors ###
### MouseEventFailed errors ###
When Poltergeist clicks on an element, rather than generating a DOM
click event, it actually generates a "proper" click. This is much closer
@ -251,7 +251,7 @@ your user won't be able to click a covered up element either).
Sometimes there can be issues with this behavior. If you have problems,
it's worth taking screenshots of the page and trying to work out what's
going on. If your click is failing, but you're not getting a
`ClickFailed` error, then you can turn on the `:debug` option and look
`MouseEventFailed` error, then you can turn on the `:debug` option and look
in the output to see what co-ordinates Poltergeist is using for the
click. You can then cross-reference this with a screenshot to see if
something is obviously wrong.

View file

@ -123,6 +123,10 @@ module Capybara::Poltergeist
command 'double_click', page_id, id
end
def hover(page_id, id)
command 'hover', page_id, id
end
def drag(page_id, id, other_id)
command 'drag', page_id, id, other_id
end

View file

@ -239,7 +239,7 @@ class PoltergeistAgent.Node
@element.dispatchEvent(event)
clickTest: (x, y) ->
mouseEventTest: (x, y) ->
frameOffset = this.frameOffset()
x -= frameOffset.left

View file

@ -19,10 +19,10 @@ class Poltergeist.Browser
@page.setViewportSize(width: @width, height: @height)
@page.onLoadStarted = =>
this.setState 'loading' if @state == 'clicked'
this.setState 'loading' if @state == 'mouse_event'
@page.onNavigationRequested = (url, navigation) =>
this.setState 'loading' if @state == 'clicked' && navigation == 'FormSubmitted'
this.setState 'loading' if @state == 'mouse_event' && navigation == 'FormSubmitted'
@page.onLoadFinished = (status) =>
if @state == 'loading'
@ -188,24 +188,30 @@ class Poltergeist.Browser
@page = prev_page if prev_page
this.sendResponse(true)
click: (page_id, id, event = 'click') ->
mouse_event: (page_id, id, name) ->
# Get the node before changing state, in case there is an exception
node = this.node(page_id, id)
# If the click event triggers onNavigationRequested, we will transition to the 'loading'
# If the event triggers onNavigationRequested, we will transition to the 'loading'
# state and wait for onLoadFinished before sending a response.
this.setState 'clicked'
this.setState 'mouse_event'
@last_click = node.click(event)
@last_mouse_event = node.mouseEvent(name)
setTimeout =>
if @state != 'loading'
this.setState 'default'
this.sendResponse(@last_click)
this.sendResponse(@last_mouse_event)
, 5
click: (page_id, id) ->
this.mouse_event page_id, id, 'click'
double_click: (page_id, id) ->
this.click page_id, id, 'doubleclick'
this.mouse_event page_id, id, 'doubleclick'
hover: (page_id, id) ->
this.mouse_event page_id, id, 'mousemove'
click_coordinates: (x, y) ->
@page.sendEvent('click', x, y)

View file

@ -348,7 +348,7 @@ PoltergeistAgent.Node = (function() {
return this.element.dispatchEvent(event);
};
Node.prototype.clickTest = function(x, y) {
Node.prototype.mouseEventTest = function(x, y) {
var el, frameOffset, origEl;
frameOffset = this.frameOffset();
x -= frameOffset.left;

View file

@ -25,12 +25,12 @@ Poltergeist.Browser = (function() {
height: this.height
});
this.page.onLoadStarted = function() {
if (_this.state === 'clicked') {
if (_this.state === 'mouse_event') {
return _this.setState('loading');
}
};
this.page.onNavigationRequested = function(url, navigation) {
if (_this.state === 'clicked' && navigation === 'FormSubmitted') {
if (_this.state === 'mouse_event' && navigation === 'FormSubmitted') {
return _this.setState('loading');
}
};
@ -247,25 +247,30 @@ Poltergeist.Browser = (function() {
return this.sendResponse(true);
};
Browser.prototype.click = function(page_id, id, event) {
Browser.prototype.mouse_event = function(page_id, id, name) {
var node,
_this = this;
if (event == null) {
event = 'click';
}
node = this.node(page_id, id);
this.setState('clicked');
this.last_click = node.click(event);
this.setState('mouse_event');
this.last_mouse_event = node.mouseEvent(name);
return setTimeout(function() {
if (_this.state !== 'loading') {
_this.setState('default');
return _this.sendResponse(_this.last_click);
return _this.sendResponse(_this.last_mouse_event);
}
}, 5);
};
Browser.prototype.click = function(page_id, id) {
return this.mouse_event(page_id, id, 'click');
};
Browser.prototype.double_click = function(page_id, id) {
return this.click(page_id, id, 'doubleclick');
return this.mouse_event(page_id, id, 'doubleclick');
};
Browser.prototype.hover = function(page_id, id) {
return this.mouse_event(page_id, id, 'mousemove');
};
Browser.prototype.click_coordinates = function(x, y) {

View file

@ -104,22 +104,23 @@ Poltergeist.FrameNotFound = (function(_super) {
})(Poltergeist.Error);
Poltergeist.ClickFailed = (function(_super) {
Poltergeist.MouseEventFailed = (function(_super) {
__extends(ClickFailed, _super);
__extends(MouseEventFailed, _super);
function ClickFailed(selector, position) {
function MouseEventFailed(eventName, selector, position) {
this.eventName = eventName;
this.selector = selector;
this.position = position;
}
ClickFailed.prototype.name = "Poltergeist.ClickFailed";
MouseEventFailed.prototype.name = "Poltergeist.MouseEventFailed";
ClickFailed.prototype.args = function() {
return [this.selector, this.position];
MouseEventFailed.prototype.args = function() {
return [this.eventName, this.selector, this.position];
};
return ClickFailed;
return MouseEventFailed;
})(Poltergeist.Error);

View file

@ -4,7 +4,7 @@ Poltergeist.Node = (function() {
var name, _fn, _i, _len, _ref,
_this = this;
Node.DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'isVisible', 'position', 'trigger', 'parentId', 'clickTest', 'scrollIntoView', 'isDOMEqual'];
Node.DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'isVisible', 'position', 'trigger', 'parentId', 'mouseEventTest', 'scrollIntoView', 'isDOMEqual'];
function Node(page, id) {
this.page = page;
@ -28,7 +28,7 @@ Poltergeist.Node = (function() {
_fn(name);
}
Node.prototype.clickPosition = function() {
Node.prototype.mouseEventPosition = function() {
var middle, pos, viewport;
viewport = this.page.viewportSize();
pos = this.position();
@ -41,27 +41,24 @@ Poltergeist.Node = (function() {
};
};
Node.prototype.click = function(event) {
Node.prototype.mouseEvent = function(name) {
var pos, test;
if (event == null) {
event = 'click';
}
this.scrollIntoView();
pos = this.clickPosition();
test = this.clickTest(pos.x, pos.y);
pos = this.mouseEventPosition();
test = this.mouseEventTest(pos.x, pos.y);
if (test.status === 'success') {
this.page.mouseEvent(event, pos.x, pos.y);
this.page.mouseEvent(name, pos.x, pos.y);
return pos;
} else {
throw new Poltergeist.ClickFailed(test.selector, pos);
throw new Poltergeist.MouseEventFailed(name, test.selector, pos);
}
};
Node.prototype.dragTo = function(other) {
var otherPosition, position;
this.scrollIntoView();
position = this.clickPosition();
otherPosition = other.clickPosition();
position = this.mouseEventPosition();
otherPosition = other.mouseEventPosition();
this.page.mouseEvent('mousedown', position.x, position.y);
return this.page.mouseEvent('mouseup', otherPosition.x, otherPosition.y);
};

View file

@ -55,10 +55,10 @@ class Poltergeist.FrameNotFound extends Poltergeist.Error
name: "Poltergeist.FrameNotFound"
args: -> [@frameName]
class Poltergeist.ClickFailed extends Poltergeist.Error
constructor: (@selector, @position) ->
name: "Poltergeist.ClickFailed"
args: -> [@selector, @position]
class Poltergeist.MouseEventFailed extends Poltergeist.Error
constructor: (@eventName, @selector, @position) ->
name: "Poltergeist.MouseEventFailed"
args: -> [@eventName, @selector, @position]
class Poltergeist.JavascriptError extends Poltergeist.Error
constructor: (@errors) ->

View file

@ -3,7 +3,7 @@
class Poltergeist.Node
@DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete',
'removeAttribute', 'isMultiple', 'select', 'tagName', 'find',
'isVisible', 'position', 'trigger', 'parentId', 'clickTest',
'isVisible', 'position', 'trigger', 'parentId', 'mouseEventTest',
'scrollIntoView', 'isDOMEqual']
constructor: (@page, @id) ->
@ -16,7 +16,7 @@ class Poltergeist.Node
this.prototype[name] = (args...) ->
@page.nodeCall(@id, name, args)
clickPosition: ->
mouseEventPosition: ->
viewport = @page.viewportSize()
pos = this.position()
@ -28,23 +28,23 @@ class Poltergeist.Node
y: middle(pos.top, pos.bottom, viewport.height)
}
click: (event = 'click') ->
mouseEvent: (name) ->
this.scrollIntoView()
pos = this.clickPosition()
test = this.clickTest(pos.x, pos.y)
pos = this.mouseEventPosition()
test = this.mouseEventTest(pos.x, pos.y)
if test.status == 'success'
@page.mouseEvent(event, pos.x, pos.y)
@page.mouseEvent(name, pos.x, pos.y)
pos
else
throw new Poltergeist.ClickFailed(test.selector, pos)
throw new Poltergeist.MouseEventFailed(name, test.selector, pos)
dragTo: (other) ->
this.scrollIntoView()
position = this.clickPosition()
otherPosition = other.clickPosition()
position = this.mouseEventPosition()
otherPosition = other.mouseEventPosition()
@page.mouseEvent('mousedown', position.x, position.y)
@page.mouseEvent('mouseup', otherPosition.x, otherPosition.y)

View file

@ -211,7 +211,7 @@ module Capybara::Poltergeist
end
def invalid_element_errors
[Capybara::Poltergeist::ObsoleteNode, Capybara::Poltergeist::ClickFailed]
[Capybara::Poltergeist::ObsoleteNode, Capybara::Poltergeist::MouseEventFailed]
end
end
end

View file

@ -83,19 +83,24 @@ module Capybara
end
end
class ClickFailed < NodeError
def selector
class MouseEventFailed < NodeError
def name
response['args'][0]
end
def selector
response['args'][1]
end
def position
[response['args'][1]['x'], response['args'][1]['y']]
[response['args'][2]['x'], response['args'][2]['y']]
end
def message
"Click at co-ordinates [#{position.join(', ')}] failed. Poltergeist detected " \
"Firing a #{name} at co-ordinates [#{position.join(', ')}] failed. Poltergeist detected " \
"another element with CSS selector '#{selector}' at this position. " \
"It may be overlapping the element you are trying to click."
"It may be overlapping the element you are trying to interact with. " \
"If you don't care about overlapping elements, try using node.trigger('#{name}')."
end
end

View file

@ -22,8 +22,8 @@ module Capybara::Poltergeist
case error.name
when 'Poltergeist.ObsoleteNode'
raise ObsoleteNode.new(self, error.response)
when 'Poltergeist.ClickFailed'
raise ClickFailed.new(self, error.response)
when 'Poltergeist.MouseEventFailed'
raise MouseEventFailed.new(self, error.response)
else
raise
end
@ -112,6 +112,10 @@ module Capybara::Poltergeist
command :double_click
end
def hover
command :hover
end
def drag_to(other)
command :drag, other.id
end

View file

@ -63,8 +63,8 @@ describe Capybara::Session do
@session.visit("/poltergeist/with_js")
end
it "raises a ClickFailed error" do
expect { @session.click_link("O hai") }.to raise_error(Capybara::Poltergeist::ClickFailed)
it "raises a MouseEventFailed error" do
expect { @session.click_link("O hai") }.to raise_error(Capybara::Poltergeist::MouseEventFailed)
end
context "and is then brought in" do
@ -74,7 +74,7 @@ describe Capybara::Session do
end
it "clicks properly" do
expect { @session.click_link "O hai" }.to_not raise_error(Capybara::Poltergeist::ClickFailed)
expect { @session.click_link "O hai" }.to_not raise_error(Capybara::Poltergeist::MouseEventFailed)
end
after do
@ -250,7 +250,7 @@ describe Capybara::Session do
it 'detects if an element is obscured when clicking' do
expect {
@session.find(:css, '#one').click
}.to raise_error(Capybara::Poltergeist::ClickFailed)
}.to raise_error(Capybara::Poltergeist::MouseEventFailed)
begin
@session.find(:css, '#one').click