Skip to content

Commit fe30005

Browse files
committed
[rb] add support for pointer event properties
1 parent 4915e2b commit fe30005

9 files changed

Lines changed: 290 additions & 24 deletions

File tree

rb/lib/selenium/webdriver/common.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
require 'selenium/webdriver/common/search_context'
4141
require 'selenium/webdriver/common/interactions/interaction'
4242
require 'selenium/webdriver/common/interactions/interactions'
43+
require 'selenium/webdriver/common/interactions/pointer_event_properties'
4344
require 'selenium/webdriver/common/interactions/pointer_cancel'
4445
require 'selenium/webdriver/common/interactions/pointer_move'
4546
require 'selenium/webdriver/common/interactions/pointer_press'

rb/lib/selenium/webdriver/common/interactions/pointer_actions.rb

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ def default_move_duration
4545
# @return [ActionBuilder] A self reference.
4646
#
4747

48-
def pointer_down(button, device: nil)
49-
button_action(button, action: :create_pointer_down, device: device)
48+
def pointer_down(button, device: nil, **opts)
49+
button_action(button, :create_pointer_down, device: device, **opts)
5050
end
5151

5252
#
@@ -62,8 +62,8 @@ def pointer_down(button, device: nil)
6262
# @return [ActionBuilder] A self reference.
6363
#
6464

65-
def pointer_up(button, device: nil)
66-
button_action(button, action: :create_pointer_up, device: device)
65+
def pointer_up(button, device: nil, **opts)
66+
button_action(button, :create_pointer_up, device: device, **opts)
6767
end
6868

6969
#
@@ -95,7 +95,7 @@ def pointer_up(button, device: nil)
9595
# @return [ActionBuilder] A self reference.
9696
#
9797

98-
def move_to(element, right_by = nil, down_by = nil, device: nil)
98+
def move_to(element, right_by = nil, down_by = nil, device: nil, **opts)
9999
pointer = pointer_input(device)
100100
# New actions offset is from center of element
101101
if right_by || down_by
@@ -111,7 +111,8 @@ def move_to(element, right_by = nil, down_by = nil, device: nil)
111111
pointer.create_pointer_move(duration: default_move_duration,
112112
x: left,
113113
y: top,
114-
origin: element)
114+
origin: element,
115+
**opts)
115116
tick(pointer)
116117
self
117118
end
@@ -348,9 +349,9 @@ def drag_and_drop_by(source, right_by, down_by, device: nil)
348349

349350
private
350351

351-
def button_action(button, action: nil, device: nil)
352+
def button_action(button, action, device: nil, **opts)
352353
pointer = pointer_input(device)
353-
pointer.send(action, button)
354+
pointer.send(action, button, **opts)
354355
tick(pointer)
355356
self
356357
end
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# frozen_string_literal: true
2+
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
module Selenium
21+
module WebDriver
22+
module Interactions
23+
module PointerEventProperties
24+
VALID = {width: {'width' => {min: 0.0}},
25+
height: {'height' => {min: 0.0}},
26+
pressure: {'pressure' => {min: 0.0, max: 1.0}},
27+
tangential_pressure: {'tangentialPressure' => {min: -1.0, max: 1.0}},
28+
tilt_x: {'tiltX' => {min: -90, max: 90}},
29+
tilt_y: {'tiltY' => {min: -90, max: 90}},
30+
twist: {'twist' => {min: 0, max: 359}},
31+
altitude_angle: {'altitudeAngle' => {min: 0.0, max: (Math::PI / 2)}},
32+
azimuth_angle: {'azimuthAngle' => {min: 0.0, max: (Math::PI * 2)}}}
33+
34+
def process_opts
35+
raise ArgumentError, "Unknown options found: #{@opts.inspect}" unless (@opts.keys - VALID.keys).empty?
36+
37+
VALID.each_with_object({}) do |(key, val), hash|
38+
next unless @opts.key?(key)
39+
40+
name = val.keys.first
41+
values = val.values.first
42+
hash[name] = assert_number(@opts[key], values[:min], values[:max])
43+
end
44+
end
45+
46+
private
47+
48+
def assert_number(num, min, max = nil)
49+
return if num.nil?
50+
51+
klass = min.is_a?(Integer) ? Integer : Numeric
52+
raise TypeError, "#{num} is not a #{klass}" unless num.is_a?(klass)
53+
54+
raise ArgumentError, "#{num} is not greater than or equal to #{min}" if num < min
55+
56+
raise ArgumentError, "#{num} is not less than or equal to #{max}" if max && num > max
57+
58+
num
59+
end
60+
end # PointerEventProperties
61+
end # Interactions
62+
end # WebDriver
63+
end # Selenium

rb/lib/selenium/webdriver/common/interactions/pointer_input.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@ def assert_kind(pointer)
4949
KIND[pointer]
5050
end
5151

52-
def create_pointer_move(duration: 0, x: 0, y: 0, origin: nil)
53-
add_action(PointerMove.new(self, duration, x, y, origin: origin))
52+
def create_pointer_move(duration: 0, x: 0, y: 0, origin: nil, **opts)
53+
add_action(PointerMove.new(self, duration, x, y, origin: origin, **opts))
5454
end
5555

56-
def create_pointer_down(button)
57-
add_action(PointerPress.new(self, :down, button))
56+
def create_pointer_down(button, **opts)
57+
add_action(PointerPress.new(self, :down, button, **opts))
5858
end
5959

60-
def create_pointer_up(button)
61-
add_action(PointerPress.new(self, :up, button))
60+
def create_pointer_up(button, **opts)
61+
add_action(PointerPress.new(self, :up, button, **opts))
6262
end
6363

6464
def create_pointer_cancel

rb/lib/selenium/webdriver/common/interactions/pointer_move.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,32 @@ module Interactions
2727
#
2828

2929
class PointerMove < Interaction
30+
include PointerEventProperties
31+
3032
VIEWPORT = :viewport
3133
POINTER = :pointer
3234
ORIGINS = [VIEWPORT, POINTER].freeze
3335

34-
def initialize(source, duration, x, y, element: nil, origin: nil)
36+
def initialize(source, duration, x, y, element: nil, origin: nil, **opts)
3537
super(source)
3638
@duration = duration * 1000
3739
@x_offset = x
3840
@y_offset = y
3941
@origin = element || origin || :viewport
4042
@type = :pointerMove
43+
@opts = opts
4144
end
4245

4346
def assert_source(source)
4447
raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? PointerInput
4548
end
4649

4750
def encode
48-
{type: type, duration: @duration.to_i, x: @x_offset, y: @y_offset, origin: @origin}
51+
process_opts.merge('type' => type.to_s,
52+
'duration' => @duration.to_i,
53+
'x' => @x_offset,
54+
'y' => @y_offset,
55+
'origin' => @origin)
4956
end
5057
end # PointerMove
5158
end # Interactions

rb/lib/selenium/webdriver/common/interactions/pointer_press.rb

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,25 @@ module Interactions
2727
#
2828

2929
class PointerPress < Interaction
30+
include PointerEventProperties
31+
3032
BUTTONS = {left: 0, middle: 1, right: 2}.freeze
3133
DIRECTIONS = {down: :pointerDown, up: :pointerUp}.freeze
3234

33-
def initialize(source, direction, button)
35+
def initialize(source, direction, button, **opts)
3436
super(source)
3537
@direction = assert_direction(direction)
3638
@button = assert_button(button)
3739
@type = @direction
40+
@opts = opts
41+
end
42+
43+
def encode
44+
process_opts.merge('type' => type.to_s, 'button' => @button)
3845
end
3946

47+
private
48+
4049
def assert_source(source)
4150
raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? PointerInput
4251
end
@@ -61,10 +70,6 @@ def assert_direction(direction)
6170

6271
DIRECTIONS[direction]
6372
end
64-
65-
def encode
66-
{type: type, button: @button}
67-
end
6873
end # PointerPress
6974
end # Interactions
7075
end # WebDriver
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# frozen_string_literal: true
2+
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
require File.expand_path('../../spec_helper', __dir__)
21+
22+
module Selenium
23+
module WebDriver
24+
module Interactions
25+
describe PointerEventProperties do
26+
let(:pointer_event) do
27+
Class.new(Interaction) do
28+
include PointerEventProperties
29+
30+
def initialize(opts)
31+
@opts = opts
32+
end
33+
34+
def assert_source(*) ; end
35+
end
36+
end
37+
let(:opts) { {width: 0,
38+
height: 0,
39+
pressure: 0.5,
40+
tangential_pressure: 0.4,
41+
tilt_x: -40,
42+
tilt_y: -10,
43+
twist: 177,
44+
altitude_angle: 1.0,
45+
azimuth_angle: 0.5} }
46+
47+
describe '#process_opts' do
48+
it 'validates all pointer event properties' do
49+
pointer = pointer_event.new(opts)
50+
allow(pointer).to receive(:assert_number)
51+
52+
pointer.process_opts
53+
expect(pointer).to have_received(:assert_number).exactly(9).times
54+
end
55+
56+
it 'validates subset of pointer event properties' do
57+
opts.delete :width
58+
opts.delete :tilt_x
59+
60+
pointer = pointer_event.new(opts)
61+
allow(pointer).to receive(:assert_number)
62+
63+
pointer.process_opts
64+
expect(pointer).to have_received(:assert_number).exactly(7).times
65+
end
66+
67+
end
68+
69+
describe '#assert_number' do
70+
context 'when Numeric' do
71+
let(:min) { 0.0 }
72+
let(:max) { 1 }
73+
74+
it 'raises a TypeError if not a Number' do
75+
expect { pointer_event.new(opts).send(:assert_number, 'nan', min, max) }.to raise_error(TypeError)
76+
end
77+
78+
it 'raises a ArgumentError if below minimum' do
79+
expect { pointer_event.new(nil).send(:assert_number, -max, min, max) }.to raise_error(ArgumentError)
80+
end
81+
82+
it 'raises a ArgumentError if number above maximum' do
83+
expect { pointer_event.new(nil).send(:assert_number, max * 2, min, max) }.to raise_error(ArgumentError)
84+
end
85+
86+
it 'returns number if minimum' do
87+
expect(pointer_event.new(nil).send(:assert_number, min, min, max)).to eq min
88+
end
89+
90+
it 'returns number if maximum' do
91+
expect(pointer_event.new(nil).send(:assert_number, max, min, max)).to eq max
92+
end
93+
94+
it 'returns number if no max provided' do
95+
expect(pointer_event.new(nil).send(:assert_number, max, min)).to eq max
96+
end
97+
end
98+
99+
context 'when Integer' do
100+
let(:min) { 0 }
101+
let(:max) { 1 }
102+
103+
it 'raises a TypeError if not an Integer' do
104+
expect { pointer_event.new(nil).send(:assert_number, 4.4, min, max) }.to raise_error(TypeError)
105+
end
106+
107+
it 'raises a ArgumentError if below minimum' do
108+
expect { pointer_event.new(nil).send(:assert_number, -max, min, max) }.to raise_error(ArgumentError)
109+
end
110+
111+
it 'raises a ArgumentError if number above maximum' do
112+
expect { pointer_event.new(nil).send(:assert_number, max * 2, min, max) }.to raise_error(ArgumentError)
113+
end
114+
115+
it 'returns number if minimum' do
116+
expect(pointer_event.new(nil).send(:assert_number, min, min, max)).to eq min
117+
end
118+
119+
it 'returns number if maximum' do
120+
expect(pointer_event.new(nil).send(:assert_number, max, min, max)).to eq max
121+
end
122+
end
123+
end
124+
end
125+
end # Interactions
126+
end # WebDriver
127+
end # Selenium

0 commit comments

Comments
 (0)