Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/rb/lib/selenium/webdriver/common/interactions/pointer_actions.rb
1990 views
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 PointerActions
23
attr_writer :default_move_duration
24
25
#
26
# By default this is set to 250ms in the ActionBuilder constructor
27
# It can be overridden with default_move_duration=
28
#
29
30
def default_move_duration
31
@default_move_duration ||= @duration / 1000.0 # convert ms to seconds
32
end
33
34
#
35
# Presses (without releasing) at the current location of the PointerInput device. This is equivalent to:
36
#
37
# driver.action.click_and_hold(nil)
38
#
39
# @example Clicking and holding at the current location
40
#
41
# driver.action.pointer_down(:left).perform
42
#
43
# @param [Selenium::WebDriver::Interactions::PointerPress::BUTTONS] button the button to press.
44
# @param [Symbol || String] device optional name of the PointerInput device with the button
45
# that will be pressed
46
# @return [ActionBuilder] A self reference.
47
#
48
49
def pointer_down(button = :left, device: nil, **)
50
button_action(button, :create_pointer_down, device: device, **)
51
end
52
53
#
54
# Releases the pressed mouse button at the current mouse location of the PointerInput device.
55
#
56
# @example Releasing a button after clicking and holding
57
#
58
# driver.action.pointer_down(:left).pointer_up(:left).perform
59
#
60
# @param [Selenium::WebDriver::Interactions::PointerPress::BUTTONS] button the button to release.
61
# @param [Symbol || String] device optional name of the PointerInput device with the button that will
62
# be released
63
# @return [ActionBuilder] A self reference.
64
#
65
66
def pointer_up(button = :left, device: nil, **)
67
button_action(button, :create_pointer_up, device: device, **)
68
end
69
70
#
71
# Moves the pointer to the in-view center point of the given element.
72
# Then the pointer is moved to optional offset coordinates.
73
#
74
# The element is not scrolled into view.
75
# MoveTargetOutOfBoundsError will be raised if element with offset is outside the viewport
76
#
77
# When using offsets, both coordinates need to be passed.
78
#
79
# @example Move the pointer to element
80
#
81
# el = driver.find_element(id: "some_id")
82
# driver.action.move_to(el).perform
83
#
84
# @example
85
#
86
# el = driver.find_element(id: "some_id")
87
# driver.action.move_to(el, 100, 100).perform
88
#
89
# @param [Selenium::WebDriver::Element] element to move to.
90
# @param [Integer] right_by Optional offset from the in-view center of the
91
# element. A negative value means coordinates to the left of the center.
92
# @param [Integer] down_by Optional offset from the in-view center of the
93
# element. A negative value means coordinates to the top of the center.
94
# @return [ActionBuilder] A self reference.
95
#
96
97
def move_to(element, right_by = nil, down_by = nil, **opts)
98
pointer = pointer_input(opts.delete(:device))
99
pointer.create_pointer_move(duration: opts.delete(:duration) || default_move_duration,
100
x: right_by || 0,
101
y: down_by || 0,
102
origin: element,
103
**opts)
104
tick(pointer)
105
self
106
end
107
108
#
109
# Moves the pointer from its current position by the given offset.
110
#
111
# The viewport is not scrolled if the coordinates provided are outside the viewport.
112
# MoveTargetOutOfBoundsError will be raised if the offsets are outside the viewport
113
#
114
# @example Move the pointer to a certain offset from its current position
115
#
116
# driver.action.move_by(100, 100).perform
117
#
118
# @param [Integer] right_by horizontal offset. A negative value means moving the pointer left.
119
# @param [Integer] down_by vertical offset. A negative value means moving the pointer up.
120
# @param [Symbol || String] device optional name of the PointerInput device to move
121
# @return [ActionBuilder] A self reference.
122
# @raise [MoveTargetOutOfBoundsError] if the provided offset is outside the document's boundaries.
123
#
124
125
def move_by(right_by, down_by, device: nil, duration: default_move_duration, **)
126
pointer = pointer_input(device)
127
pointer.create_pointer_move(duration: duration,
128
x: Integer(right_by),
129
y: Integer(down_by),
130
origin: Interactions::PointerMove::POINTER,
131
**)
132
tick(pointer)
133
self
134
end
135
136
#
137
# Moves the pointer to a given location in the viewport.
138
#
139
# The viewport is not scrolled if the coordinates provided are outside the viewport.
140
# MoveTargetOutOfBoundsError will be raised if the offsets are outside the viewport
141
#
142
# @example Move the pointer to a certain position in the viewport
143
#
144
# driver.action.move_to_location(100, 100).perform
145
#
146
# @param [Integer] x horizontal position. Equivalent to a css 'left' value.
147
# @param [Integer] y vertical position. Equivalent to a css 'top' value.
148
# @param [Symbol || String] device optional name of the PointerInput device to move
149
# @return [ActionBuilder] A self reference.
150
# @raise [MoveTargetOutOfBoundsError] if the provided x or y value is outside the document's boundaries.
151
#
152
153
def move_to_location(x, y, device: nil, duration: default_move_duration, **)
154
pointer = pointer_input(device)
155
pointer.create_pointer_move(duration: duration,
156
x: Integer(x),
157
y: Integer(y),
158
origin: Interactions::PointerMove::VIEWPORT,
159
**)
160
tick(pointer)
161
self
162
end
163
164
#
165
# Clicks (without releasing) in the middle of the given element. This is
166
# equivalent to:
167
#
168
# driver.action.move_to(element).click_and_hold
169
#
170
# @example Clicking and holding on some element
171
#
172
# el = driver.find_element(id: "some_id")
173
# driver.action.click_and_hold(el).perform
174
#
175
# @param [Selenium::WebDriver::Element] element the element to move to and click.
176
# @param [Symbol || String] device optional name of the PointerInput device to click with
177
# @return [ActionBuilder] A self reference.
178
#
179
180
def click_and_hold(element = nil, button: nil, device: nil)
181
move_to(element, device: device) if element
182
pointer_down(button || :left, device: device)
183
self
184
end
185
186
#
187
# Releases the depressed left mouse button at the current mouse location.
188
#
189
# @example Releasing an element after clicking and holding it
190
#
191
# el = driver.find_element(id: "some_id")
192
# driver.action.click_and_hold(el).release.perform
193
#
194
# @param [Symbol || String] device optional name of the PointerInput device with the button
195
# that will be released
196
# @return [ActionBuilder] A self reference.
197
#
198
199
def release(button: nil, device: nil)
200
pointer_up(button || :left, device: device)
201
self
202
end
203
204
#
205
# Clicks in the middle of the given element. Equivalent to:
206
#
207
# driver.action.move_to(element).click
208
#
209
# When no element is passed, the current mouse position will be clicked.
210
#
211
# @example Clicking on an element
212
#
213
# el = driver.find_element(id: "some_id")
214
# driver.action.click(el).perform
215
#
216
# @example Clicking at the current mouse position
217
#
218
# driver.action.click.perform
219
#
220
# @param [Selenium::WebDriver::Element] element An optional element to click.
221
# @param [Symbol || String] device optional name of the PointerInput device with the button
222
# that will be clicked
223
# @return [ActionBuilder] A self reference.
224
#
225
226
def click(element = nil, button: nil, device: nil)
227
move_to(element, device: device) if element
228
pointer_down(button || :left, device: device)
229
pointer_up(button || :left, device: device)
230
self
231
end
232
233
#
234
# Performs a double-click at middle of the given element. Equivalent to:
235
#
236
# driver.action.move_to(element).double_click
237
#
238
# When no element is passed, the current mouse position will be double-clicked.
239
#
240
# @example Double-click an element
241
#
242
# el = driver.find_element(id: "some_id")
243
# driver.action.double_click(el).perform
244
#
245
# @example Double-clicking at the current mouse position
246
#
247
# driver.action.double_click.perform
248
#
249
# @param [Selenium::WebDriver::Element] element An optional element to move to.
250
# @param [Symbol || String] device optional name of the PointerInput device with the button
251
# that will be double-clicked
252
# @return [ActionBuilder] A self reference.
253
#
254
255
def double_click(element = nil, device: nil)
256
move_to(element, device: device) if element
257
click(device: device)
258
click(device: device)
259
self
260
end
261
262
#
263
# Performs a context-click at middle of the given element. First performs
264
# a move_to to the location of the element.
265
#
266
# When no element is passed, the current mouse position will be context-clicked.
267
#
268
# @example Context-click at middle of given element
269
#
270
# el = driver.find_element(id: "some_id")
271
# driver.action.context_click(el).perform
272
#
273
# @example Context-clicking at the current mouse position
274
#
275
# driver.action.context_click.perform
276
#
277
# @param [Selenium::WebDriver::Element] element An element to context click.
278
# @param [Symbol || String] device optional name of the PointerInput device with the button
279
# that will be context-clicked
280
# @return [ActionBuilder] A self reference.
281
#
282
283
def context_click(element = nil, device: nil)
284
click(element, button: :right, device: device)
285
end
286
287
#
288
# A convenience method that performs click-and-hold at the location of the
289
# source element, moves to the location of the target element, then
290
# releases the mouse.
291
#
292
# @example Drag and drop one element onto another
293
#
294
# el1 = driver.find_element(id: "some_id1")
295
# el2 = driver.find_element(id: "some_id2")
296
# driver.action.drag_and_drop(el1, el2).perform
297
#
298
# @param [Selenium::WebDriver::Element] source element to emulate button down at.
299
# @param [Selenium::WebDriver::Element] target element to move to and release the
300
# mouse at.
301
# @param [Symbol || String] device optional name of the PointerInput device with the button
302
# that will perform the drag and drop
303
# @return [ActionBuilder] A self reference.
304
#
305
306
def drag_and_drop(source, target, device: nil)
307
click_and_hold(source, device: device)
308
move_to(target, device: device)
309
release(device: device)
310
self
311
end
312
313
#
314
# A convenience method that performs click-and-hold at the location of
315
# the source element, moves by a given offset, then releases the mouse.
316
#
317
# @example Drag and drop an element by offset
318
#
319
# el = driver.find_element(id: "some_id1")
320
# driver.action.drag_and_drop_by(el, 100, 100).perform
321
#
322
# @param [Selenium::WebDriver::Element] source Element to emulate button down at.
323
# @param [Integer] right_by horizontal move offset.
324
# @param [Integer] down_by vertical move offset.
325
# @param [Symbol || String] device optional name of the PointerInput device with the button
326
# that will perform the drag and drop
327
# @return [ActionBuilder] A self reference.
328
#
329
330
def drag_and_drop_by(source, right_by, down_by, device: nil)
331
click_and_hold(source, device: device)
332
move_by(right_by, down_by, device: device)
333
release(device: device)
334
self
335
end
336
337
private
338
339
def button_action(button, action, device: nil, **)
340
pointer = pointer_input(device)
341
pointer.send(action, button, **)
342
tick(pointer)
343
self
344
end
345
346
def pointer_input(name = nil)
347
device(name: name, type: Interactions::POINTER) || add_pointer_input(:mouse, 'mouse')
348
end
349
end # PointerActions
350
end # WebDriver
351
end # Selenium
352
353