1
0
Fork 0
mirror of https://github.com/teamcapybara/capybara.git synced 2022-11-09 12:08:07 -05:00

Support keys and x,y offset with mouse clicks

This commit is contained in:
Thomas Walpole 2017-12-29 12:37:08 -08:00
parent 6599817769
commit 1a899c2a73
5 changed files with 186 additions and 21 deletions

View file

@ -39,15 +39,15 @@ module Capybara
raise NotImplementedError
end
def click
def click(*options)
raise NotImplementedError
end
def right_click
def right_click(*options)
raise NotImplementedError
end
def double_click
def double_click(*options)
raise NotImplementedError
end

View file

@ -138,9 +138,14 @@ module Capybara
#
# Click the Element
#
# @!macro click_modifiers
# @overload $0(*key_modifiers=[], offset={x: nil, y: nil})
# @param [Array<:alt, :control, :meta, :shift>] *key_modifiers Keys to be held down when clicking
# @param [Hash] offset x and y coordinates to offset the click location from the top left corner of the element. If not specified will click the middle of the element.
# @return [Capybara::Node::Element] The element
def click
synchronize { base.click }
def click(*options)
verify_click_options_support(__method__) if !options.empty?
synchronize { base.click(*options) }
return self
end
@ -148,9 +153,11 @@ module Capybara
#
# Right Click the Element
#
# @macro click_modifiers
# @return [Capybara::Node::Element] The element
def right_click
synchronize { base.right_click }
def right_click(*options)
verify_click_options_support(__method__) if !options.empty?
synchronize { base.right_click(*options) }
return self
end
@ -158,9 +165,11 @@ module Capybara
#
# Double Click the Element
#
# @macro click_modifiers
# @return [Capybara::Node::Element] The element
def double_click
synchronize { base.double_click }
def double_click(*options)
verify_click_options_support(__method__) if !options.empty?
synchronize { base.double_click(*options) }
return self
end
@ -381,6 +390,13 @@ module Capybara
raise
end
end
private
def verify_click_options_support(method)
if base.method(method).arity == 0
raise ArgumentError, "The current driver does not support #{method} options"
end
end
end
end
end

View file

@ -74,8 +74,20 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
native.click if selected?
end
def click
native.click
def click(*keys, **options)
if keys.empty? && options.empty? && !(options[:x] && options[:y])
native.click
else
scroll_if_needed do
action_with_modifiers(*keys, **options) do |a|
if options[:x] && options[:y]
a.click
else
a.click(native)
end
end
end
end
rescue => e
if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
e.message =~ /Other element would receive the click/
@ -87,15 +99,27 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
raise e
end
def right_click
def right_click(*keys, **options)
scroll_if_needed do
driver.browser.action.context_click(native).perform
action_with_modifiers(*keys, **options) do |a|
if options[:x] && options[:y]
a.context_click
else
a.context_click(native)
end
end
end
end
def double_click
def double_click(*keys, **options)
scroll_if_needed do
driver.browser.action.double_click(native).perform
action_with_modifiers(*keys, **options) do |a|
if options[:x] && options[:y]
a.double_click
else
a.double_click(native)
end
end
end
end
@ -267,4 +291,40 @@ private
native.send_keys(value.to_s)
end
end
def action_with_modifiers(*keys, x: nil, y: nil)
actions = driver.browser.action
actions.move_to(native, x, y)
modifiers_down(actions, keys)
yield actions
modifiers_up(actions, keys)
actions.perform
ensure
a = driver.browser.action
a.release_actions if a.respond_to?(:release_actions)
end
def modifiers_down(actions, keys)
keys.each do |key|
key = case key
when :ctrl then :control
when :command, :cmd then :meta
else
key
end
actions.key_down(key)
end
end
def modifiers_up(actions, keys)
keys.each do |key|
key = case key
when :ctrl then :control
when :command, :cmd then :meta
else
key
end
actions.key_up(key)
end
end
end

View file

@ -8,7 +8,8 @@ $(function() {
$(this).html('Dropped!');
}
});
$('#clickable').click(function() {
$('#clickable').click(function(e) {
debugger;
var link = $(this);
setTimeout(function() {
$(link).after('<a id="has-been-clicked" href="#">Has been clicked</a>');
@ -64,12 +65,40 @@ $(function() {
}, 400)
});
$('#click-test').on({
dblclick: function() {
$(this).after('<a id="has-been-double-clicked" href="#">Has been double clicked</a>');
click: function(e) {
var desc = "";
if (e.altKey|| e.ctrlKey || e.metaKey || e.shiftKey) {
if (e.altKey) desc += 'alt ';
if (e.ctrlKey) desc += 'control ';
if (e.metaKey) desc += 'meta ';
if (e.shiftKey) desc += 'shift ';
}
var pos = this.getBoundingClientRect();
$(this).after('<a id="has-been-clicked" href="#">Has been ' + desc + 'clicked at ' + (e.clientX - pos.left) + ',' + (e.clientY - pos.top) + '</a>');
},
dblclick: function(e) {
var desc = "";
if (e.altKey|| e.ctrlKey || e.metaKey || e.shiftKey) {
if (e.altKey) desc += 'alt ';
if (e.ctrlKey) desc += 'control ';
if (e.metaKey) desc += 'meta ';
if (e.shiftKey) desc += 'shift ';
$(this).after('<a id="href="#">Has been ' + desc + 'double clicked</a>');
}
var pos = this.getBoundingClientRect();
$(this).after('<a id="has-been-double-clicked" href="#">Has been ' + desc + 'double clicked at ' + (e.clientX - pos.left) + ',' + (e.clientY - pos.top) + '</a>');
},
contextmenu: function(e) {
e.preventDefault();
$(this).after('<a id="has-been-right-clicked" href="#">Has been right clicked</a>');
var desc = "";
if (e.altKey|| e.ctrlKey || e.metaKey || e.shiftKey) {
if (e.altKey) desc += 'alt ';
if (e.ctrlKey) desc += 'control ';
if (e.metaKey) desc += 'meta ';
if (e.shiftKey) desc += 'shift ';
}
var pos = this.getBoundingClientRect();
$(this).after('<a id="has-been-right-clicked" href="#">Has been ' + desc + 'right clicked at ' + (e.clientX - pos.left) + ',' + (e.clientY - pos.top) + '</a>');
}
});
$('#open-alert').click(function() {

View file

@ -338,15 +338,58 @@ Capybara::SpecHelper.spec "node" do
radio.click
expect(radio).to be_checked
end
it "should allow modifiers", requires: [:js] do
@session.visit('/with_js')
@session.find(:css, '#click-test').click(:control)
expect(@session).to have_link('Has been control clicked')
end
it "should allow multiple modifiers", requires: [:js] do
@session.visit('with_js')
@session.find(:css, '#click-test').click(:control, :alt, :meta, :shift)
expect(@session).to have_link('Has been alt control meta shift clicked')
end
it "should allow to adjust the click offset", requires: [:js] do
@session.visit('with_js')
@session.find(:css, '#click-test').click(x:0, y:0)
link = @session.find(:link, 'has-been-clicked')
locations = link.text.match /^Has been clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/
# Resulting click location should be very close to 0, 0 relative to top left corner of the element, but may not be exact due to
# integer/float conversions and rounding.
expect(locations[:x].to_f).to be_within(1).of(0)
expect(locations[:y].to_f).to be_within(1).of(0)
end
end
describe '#double_click', requires: [:js] do
it "should double click an element" do
describe '#double_click', requires: [:js], focus_: true do
before do
pending "selenium-webdriver/geckodriver doesn't generate double click event" if marionette?(@session)
end
it "should double click an element" do
@session.visit('/with_js')
@session.find(:css, '#click-test').double_click
expect(@session.find(:css, '#has-been-double-clicked')).to be
end
it "should allow modifiers", requires: [:js] do
@session.visit('/with_js')
@session.find(:css, '#click-test').double_click(:alt)
expect(@session).to have_link('Has been alt double clicked')
end
it "should allow to adjust the offset", requires: [:js] do
@session.visit('with_js')
@session.find(:css, '#click-test').double_click(x:10, y:5)
link = @session.find(:link, 'has-been-double-clicked')
locations = link.text.match /^Has been double clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/
# Resulting click location should be very close to 10, 5 relative to top left corner of the element, but may not be exact due
# to integer/float conversions and rounding.
expect(locations[:x].to_f).to be_within(1).of(10)
expect(locations[:y].to_f).to be_within(1).of(5)
end
end
describe '#right_click', requires: [:js] do
@ -355,6 +398,23 @@ Capybara::SpecHelper.spec "node" do
@session.find(:css, '#click-test').right_click
expect(@session.find(:css, '#has-been-right-clicked')).to be
end
it "should allow modifiers", requires: [:js] do
@session.visit('/with_js')
@session.find(:css, '#click-test').right_click(:meta)
expect(@session).to have_link('Has been meta right clicked')
end
it "should allow to adjust the offset", requires: [:js] do
@session.visit('with_js')
@session.find(:css, '#click-test').right_click(x:10, y:10)
link = @session.find(:link, 'has-been-right-clicked')
locations = link.text.match /^Has been right clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/
# Resulting click location should be very close to 10, 10 relative to top left corner of the element, but may not be exact due
# to integer/float conversions and rounding
expect(locations[:x].to_f).to be_within(1).of(10)
expect(locations[:y].to_f).to be_within(1).of(10)
end
end
describe '#send_keys', requires: [:send_keys] do