selenium-webdriver 3.0.8 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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