mirror of
https://github.com/teampoltergeist/poltergeist.git
synced 2022-11-09 12:05:00 -05:00
Implement #hover
This commit is contained in:
parent
5546d0344c
commit
6c52e69ca1
14 changed files with 88 additions and 66 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -239,7 +239,7 @@ class PoltergeistAgent.Node
|
|||
|
||||
@element.dispatchEvent(event)
|
||||
|
||||
clickTest: (x, y) ->
|
||||
mouseEventTest: (x, y) ->
|
||||
frameOffset = this.frameOffset()
|
||||
|
||||
x -= frameOffset.left
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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) ->
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue