selenium-webdriver 3.0.8 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. data/CHANGES +9 -3
  2. data/lib/selenium/webdriver/chrome/bridge.rb +0 -1
  3. data/lib/selenium/webdriver/common.rb +9 -1
  4. data/lib/selenium/webdriver/common/action_builder.rb +1 -3
  5. data/lib/selenium/webdriver/common/driver.rb +17 -0
  6. data/lib/selenium/webdriver/common/interactions/input_device.rb +53 -0
  7. data/lib/selenium/webdriver/common/interactions/interaction.rb +52 -0
  8. data/lib/selenium/webdriver/common/interactions/interactions.rb +43 -0
  9. data/lib/selenium/webdriver/common/interactions/key_actions.rb +112 -0
  10. data/lib/selenium/webdriver/common/interactions/key_input.rb +64 -0
  11. data/lib/selenium/webdriver/common/{driver_extensions/has_input_devices.rb → interactions/none_input.rb} +9 -29
  12. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +355 -0
  13. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +134 -0
  14. data/lib/selenium/webdriver/common/keys.rb +33 -12
  15. data/lib/selenium/webdriver/common/w3c_action_builder.rb +211 -0
  16. data/lib/selenium/webdriver/edge/bridge.rb +1 -4
  17. data/lib/selenium/webdriver/firefox/bridge.rb +1 -2
  18. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  19. data/lib/selenium/webdriver/firefox/w3c_bridge.rb +0 -1
  20. data/lib/selenium/webdriver/ie/bridge.rb +1 -1
  21. data/lib/selenium/webdriver/phantomjs/bridge.rb +1 -4
  22. data/lib/selenium/webdriver/remote/bridge.rb +25 -1
  23. data/lib/selenium/webdriver/remote/w3c_bridge.rb +16 -29
  24. data/lib/selenium/webdriver/remote/w3c_commands.rb +1 -0
  25. data/selenium-webdriver.gemspec +1 -1
  26. metadata +11 -3
@@ -0,0 +1,355 @@
1
+ # encoding: utf-8
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 PointerActions
23
+ DEFAULT_MOVE_DURATION = 0.25 # 250 milliseconds
24
+
25
+ #
26
+ # Presses (without releasing) at the current location of the PointerInput device. This is equivalent to:
27
+ #
28
+ # driver.action.click_and_hold(nil)
29
+ #
30
+ # @example Clicking and holding at the current location
31
+ #
32
+ # driver.action.pointer_down(:left).perform
33
+ #
34
+ # @param [Selenium::WebDriver::Interactions::PointerPress::BUTTONS] button the button to press.
35
+ # @param [Symbol || String] device optional name of the PointerInput device with the button
36
+ # that will be pressed
37
+ # @return [W3CActionBuilder] A self reference.
38
+ #
39
+
40
+ def pointer_down(button, device: nil)
41
+ button_action(button, action: :create_pointer_down, device: device)
42
+ end
43
+
44
+ #
45
+ # Releases the pressed mouse button at the current mouse location of the PointerInput device.
46
+ #
47
+ # @example Releasing a button after clicking and holding
48
+ #
49
+ # driver.action.pointer_down(:left).pointer_up(:left).perform
50
+ #
51
+ # @param [Selenium::WebDriver::Interactions::PointerPress::BUTTONS] button the button to release.
52
+ # @param [Symbol || String] device optional name of the PointerInput device with the button that will
53
+ # be released
54
+ # @return [W3CActionBuilder] A self reference.
55
+ #
56
+
57
+ def pointer_up(button, device: nil)
58
+ button_action(button, action: :create_pointer_up, device: device)
59
+ end
60
+
61
+ #
62
+ # Moves the mouse to the middle of the given element. The element is scrolled into
63
+ # view and its location is calculated using getBoundingClientRect. Then the
64
+ # mouse is moved to optional offset coordinates from the element.
65
+ #
66
+ # This is adapted to be backward compatible from non-W3C actions. W3C calculates offset from the center point
67
+ # of the element
68
+ #
69
+ # Note that when using offsets, both coordinates need to be passed.
70
+ #
71
+ # @example Scroll element into view and move the mouse to it
72
+ #
73
+ # el = driver.find_element(id: "some_id")
74
+ # driver.action.move_to(el).perform
75
+ #
76
+ # @example
77
+ #
78
+ # el = driver.find_element(id: "some_id")
79
+ # driver.action.move_to(el, 100, 100).perform
80
+ #
81
+ # @param [Selenium::WebDriver::Element] element to move to.
82
+ # @param [Integer] right_by Optional offset from the top-left corner. A negative value means
83
+ # coordinates to the left of the element.
84
+ # @param [Integer] down_by Optional offset from the top-left corner. A negative value means
85
+ # coordinates above the element.
86
+ # @param [Symbol || String] device optional name of the PointerInput device to move.
87
+ # @return [W3CActionBuilder] A self reference.
88
+ #
89
+
90
+ def move_to(element, right_by = nil, down_by = nil, device: nil)
91
+ pointer = get_pointer(device)
92
+ # New actions offset is from center of element
93
+ if right_by || down_by
94
+ size = element.size
95
+ left_offset = (size[:width] / 2).to_i
96
+ top_offset = (size[:height] / 2).to_i
97
+ left = -left_offset + (right_by || 0)
98
+ top = -top_offset + (down_by || 0)
99
+ else
100
+ left = 0
101
+ top = 0
102
+ end
103
+ pointer.create_pointer_move(duration: DEFAULT_MOVE_DURATION,
104
+ x: left,
105
+ y: top,
106
+ element: element)
107
+ tick(pointer)
108
+ self
109
+ end
110
+
111
+ #
112
+ # Moves the mouse from its current position by the given offset.
113
+ # If the coordinates provided are outside the viewport (the mouse will
114
+ # end up outside the browser window) then the viewport is scrolled to
115
+ # match.
116
+ #
117
+ # @example Move the mouse to a certain offset from its current position
118
+ #
119
+ # driver.action.move_by(100, 100).perform
120
+ #
121
+ # @param [Integer] right_by horizontal offset. A negative value means moving the mouse left.
122
+ # @param [Integer] down_by vertical offset. A negative value means moving the mouse up.
123
+ # @param [Symbol || String] device optional name of the PointerInput device to move
124
+ # @return [W3CActionBuilder] A self reference.
125
+ # @raise [MoveTargetOutOfBoundsError] if the provided offset is outside the document's boundaries.
126
+ #
127
+
128
+ def move_by(right_by, down_by, device: nil)
129
+ pointer = get_pointer(device)
130
+ pointer.create_pointer_move(duration: DEFAULT_MOVE_DURATION,
131
+ x: Integer(right_by),
132
+ y: Integer(down_by),
133
+ origin: Interactions::PointerMove::POINTER)
134
+ tick(pointer)
135
+ self
136
+ end
137
+
138
+ #
139
+ # Moves the mouse to a given location in the viewport.
140
+ # If the coordinates provided are outside the viewport (the mouse will
141
+ # end up outside the browser window) then the viewport is scrolled to
142
+ # match.
143
+ #
144
+ # @example Move the mouse to a certain position in the viewport
145
+ #
146
+ # driver.action.move_to_location(100, 100).perform
147
+ #
148
+ # @param [Integer] x horizontal position. Equivalent to a css 'left' value.
149
+ # @param [Integer] y vertical position. Equivalent to a css 'top' value.
150
+ # @param [Symbol || String] device optional name of the PointerInput device to move
151
+ # @return [W3CActionBuilder] A self reference.
152
+ # @raise [MoveTargetOutOfBoundsError] if the provided x or y value is outside the document's boundaries.
153
+ #
154
+
155
+ def move_to_location(x, y, device: nil)
156
+ pointer = get_pointer(device)
157
+ pointer.create_pointer_move(duration: DEFAULT_MOVE_DURATION,
158
+ x: Integer(x),
159
+ y: Integer(y),
160
+ origin: Interactions::PointerMove::VIEWPORT)
161
+ tick(pointer)
162
+ self
163
+ end
164
+
165
+ #
166
+ # Clicks (without releasing) in the middle of the given element. This is
167
+ # equivalent to:
168
+ #
169
+ # driver.action.move_to(element).click_and_hold
170
+ #
171
+ # @example Clicking and holding on some element
172
+ #
173
+ # el = driver.find_element(id: "some_id")
174
+ # driver.action.click_and_hold(el).perform
175
+ #
176
+ # @param [Selenium::WebDriver::Element] element the element to move to and click.
177
+ # @param [Symbol || String] device optional name of the PointerInput device to click with
178
+ # @return [W3CActionBuilder] A self reference.
179
+ #
180
+
181
+ def click_and_hold(element = nil, device: nil)
182
+ move_to(element, device: device) if element
183
+ pointer_down(:left, device: device)
184
+ self
185
+ end
186
+
187
+ #
188
+ # Releases the depressed left mouse button at the current mouse location.
189
+ #
190
+ # @example Releasing an element after clicking and holding it
191
+ #
192
+ # el = driver.find_element(id: "some_id")
193
+ # driver.action.click_and_hold(el).release.perform
194
+ #
195
+ # @param [Symbol || String] device optional name of the PointerInput device with the button
196
+ # that will be released
197
+ # @return [W3CActionBuilder] A self reference.
198
+ #
199
+
200
+ def release(device: nil)
201
+ pointer_up(:left, device: device)
202
+ self
203
+ end
204
+
205
+ #
206
+ # Clicks in the middle of the given element. Equivalent to:
207
+ #
208
+ # driver.action.move_to(element).click
209
+ #
210
+ # When no element is passed, the current mouse position will be clicked.
211
+ #
212
+ # @example Clicking on an element
213
+ #
214
+ # el = driver.find_element(id: "some_id")
215
+ # driver.action.click(el).perform
216
+ #
217
+ # @example Clicking at the current mouse position
218
+ #
219
+ # driver.action.click.perform
220
+ #
221
+ # @param [Selenium::WebDriver::Element] element An optional element to click.
222
+ # @param [Symbol || String] device optional name of the PointerInput device with the button
223
+ # that will be clicked
224
+ # @return [W3CActionBuilder] A self reference.
225
+ #
226
+
227
+ def click(element = nil, device: nil)
228
+ move_to(element, device: device) if element
229
+ pointer_down(:left, device: device)
230
+ pointer_up(:left, device: device)
231
+ self
232
+ end
233
+
234
+ #
235
+ # Performs a double-click at middle of the given element. Equivalent to:
236
+ #
237
+ # driver.action.move_to(element).double_click
238
+ #
239
+ # When no element is passed, the current mouse position will be double-clicked.
240
+ #
241
+ # @example Double-click an element
242
+ #
243
+ # el = driver.find_element(id: "some_id")
244
+ # driver.action.double_click(el).perform
245
+ #
246
+ # @example Double-clicking at the current mouse position
247
+ #
248
+ # driver.action.double_click.perform
249
+ #
250
+ # @param [Selenium::WebDriver::Element] element An optional element to move to.
251
+ # @param [Symbol || String] device optional name of the PointerInput device with the button
252
+ # that will be double-clicked
253
+ # @return [W3CActionBuilder] A self reference.
254
+ #
255
+
256
+ def double_click(element = nil, device: nil)
257
+ move_to(element, device: device) if element
258
+ click(device: device)
259
+ click(device: device)
260
+ self
261
+ end
262
+
263
+ #
264
+ # Performs a context-click at middle of the given element. First performs
265
+ # a move_to to the location of the element.
266
+ #
267
+ # When no element is passed, the current mouse position will be context-clicked.
268
+ #
269
+ # @example Context-click at middle of given element
270
+ #
271
+ # el = driver.find_element(id: "some_id")
272
+ # driver.action.context_click(el).perform
273
+ #
274
+ # @example Context-clicking at the current mouse position
275
+ #
276
+ # driver.action.context_click.perform
277
+ #
278
+ # @param [Selenium::WebDriver::Element] element An element to context click.
279
+ # @param [Symbol || String] device optional name of the PointerInput device with the button
280
+ # that will be context-clicked
281
+ # @return [W3CActionBuilder] A self reference.
282
+ #
283
+
284
+ def context_click(element = nil, device: nil)
285
+ move_to(element, device: device) if element
286
+ pointer_down(:right, device: device)
287
+ pointer_up(:right, device: device)
288
+ self
289
+ end
290
+
291
+ #
292
+ # A convenience method that performs click-and-hold at the location of the
293
+ # source element, moves to the location of the target element, then
294
+ # releases the mouse.
295
+ #
296
+ # @example Drag and drop one element onto another
297
+ #
298
+ # el1 = driver.find_element(id: "some_id1")
299
+ # el2 = driver.find_element(id: "some_id2")
300
+ # driver.action.drag_and_drop(el1, el2).perform
301
+ #
302
+ # @param [Selenium::WebDriver::Element] source element to emulate button down at.
303
+ # @param [Selenium::WebDriver::Element] target element to move to and release the
304
+ # mouse at.
305
+ # @param [Symbol || String] device optional name of the PointerInput device with the button
306
+ # that will perform the drag and drop
307
+ # @return [W3CActionBuilder] A self reference.
308
+ #
309
+
310
+ def drag_and_drop(source, target, device: nil)
311
+ click_and_hold(source, device: device)
312
+ move_to(target, device: device)
313
+ release(device: device)
314
+ self
315
+ end
316
+
317
+ #
318
+ # A convenience method that performs click-and-hold at the location of
319
+ # the source element, moves by a given offset, then releases the mouse.
320
+ #
321
+ # @example Drag and drop an element by offset
322
+ #
323
+ # el = driver.find_element(id: "some_id1")
324
+ # driver.action.drag_and_drop_by(el, 100, 100).perform
325
+ #
326
+ # @param [Selenium::WebDriver::Element] source Element to emulate button down at.
327
+ # @param [Integer] right_by horizontal move offset.
328
+ # @param [Integer] down_by vertical move offset.
329
+ # @param [Symbol || String] device optional name of the PointerInput device with the button
330
+ # that will perform the drag and drop
331
+ # @return [W3CActionBuilder] A self reference.
332
+ #
333
+
334
+ def drag_and_drop_by(source, right_by, down_by, device: nil)
335
+ click_and_hold(source, device: device)
336
+ move_by(right_by, down_by, device: device)
337
+ release(device: device)
338
+ self
339
+ end
340
+
341
+ private
342
+
343
+ def button_action(button, action: nil, device: nil)
344
+ pointer = get_pointer(device)
345
+ pointer.send(action, button)
346
+ tick(pointer)
347
+ self
348
+ end
349
+
350
+ def get_pointer(device = nil)
351
+ get_device(device) || pointer_inputs.first
352
+ end
353
+ end # PointerActions
354
+ end # WebDriver
355
+ end # Selenium
@@ -0,0 +1,134 @@
1
+ # encoding: utf-8
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
+ class PointerInput < InputDevice
24
+ KIND = {mouse: :mouse, pen: :pen, touch: :touch}.freeze
25
+
26
+ attr_reader :kind
27
+
28
+ def initialize(kind, name: nil)
29
+ super(name)
30
+ @kind = assert_kind(kind)
31
+ end
32
+
33
+ def type
34
+ Interactions::POINTER
35
+ end
36
+
37
+ def encode
38
+ return nil if no_actions?
39
+ output = {type: type, id: name, actions: @actions.map(&:encode)}
40
+ output[:parameters] = {pointerType: kind}
41
+ output
42
+ end
43
+
44
+ def assert_kind(pointer)
45
+ raise TypeError, "#{pointer.inspect} is not a valid pointer type" unless KIND.key? pointer
46
+ KIND[pointer]
47
+ end
48
+
49
+ def create_pointer_move(duration: 0, x: 0, y: 0, element: nil, origin: nil)
50
+ add_action(PointerMove.new(self, duration, x, y, element: element, origin: origin))
51
+ end
52
+
53
+ def create_pointer_down(button)
54
+ add_action(PointerPress.new(self, :down, button))
55
+ end
56
+
57
+ def create_pointer_up(button)
58
+ add_action(PointerPress.new(self, :up, button))
59
+ end
60
+
61
+ def create_pointer_cancel
62
+ add_action(PointerCancel.new(self))
63
+ end
64
+ end # PointerInput
65
+
66
+ class PointerPress < Interaction
67
+ BUTTONS = {left: 0, middle: 1, right: 2}.freeze
68
+ DIRECTIONS = {down: :pointerDown, up: :pointerUp}.freeze
69
+
70
+ def initialize(source, direction, button)
71
+ super(source)
72
+ @direction = assert_direction(direction)
73
+ @button = assert_button(button)
74
+ end
75
+
76
+ def type
77
+ @direction
78
+ end
79
+
80
+ def assert_button(button)
81
+ if button.is_a? Symbol
82
+ raise TypeError, "#{button.inspect} is not a valid button!" unless BUTTONS.key? button
83
+ button = BUTTONS[button]
84
+ end
85
+ raise ArgumentError, 'Button number cannot be negative!' unless button >= 0
86
+ button
87
+ end
88
+
89
+ def assert_direction(direction)
90
+ raise TypeError, "#{direction.inspect} is not a valid button direction" unless DIRECTIONS.key? direction
91
+ DIRECTIONS[direction]
92
+ end
93
+
94
+ def encode
95
+ {type: type, button: @button}
96
+ end
97
+ end # PointerPress
98
+
99
+ class PointerMove < Interaction
100
+ VIEWPORT = :viewport
101
+ POINTER = :pointer
102
+ ORIGINS = [VIEWPORT, POINTER].freeze
103
+
104
+ def initialize(source, duration, x, y, element: nil, origin: nil)
105
+ super(source)
106
+ @duration = duration * 1000
107
+ @x_offset = x
108
+ @y_offset = y
109
+ @origin = element || origin
110
+ end
111
+
112
+ def type
113
+ :pointerMove
114
+ end
115
+
116
+ def encode
117
+ output = {type: type, duration: @duration.to_i, x: @x_offset, y: @y_offset}
118
+ output[:origin] = @origin
119
+ output
120
+ end
121
+ end # Move
122
+
123
+ class PointerCancel < Interaction
124
+ def type
125
+ :pointerCancel
126
+ end
127
+
128
+ def encode
129
+ {type: type}
130
+ end
131
+ end # Cancel
132
+ end # Interactions
133
+ end # WebDriver
134
+ end # Selenium