Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/rb/spec/integration/selenium/webdriver/action_builder_spec.rb
4054 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
require_relative 'spec_helper'
21
22
module Selenium
23
module WebDriver
24
describe ActionBuilder, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do
25
after { driver.action.clear_all_actions }
26
27
describe '#send_keys' do
28
it 'sends keys to the active element', except: {browser: %i[safari safari_preview]} do
29
driver.navigate.to url_for('bodyTypingTest.html')
30
keylogger = driver.find_element(id: 'body_result')
31
32
driver.find_element(css: 'body').click
33
driver.action.send_keys('ab').perform
34
35
wait.until { keylogger.text.split.length == 2 }
36
expect(keylogger.text.strip).to eq('keypress keypress')
37
38
expect(driver.find_element(id: 'result').text.strip).to be_empty
39
end
40
41
it 'sends keys to element', only: {browser: %i[chrome edge firefox]} do
42
driver.navigate.to url_for('formPage.html')
43
44
input = driver.find_element(css: '#working')
45
driver.execute_script('arguments[0].scrollIntoView({block: "center", inline: "nearest"});', input)
46
47
driver.action.send_keys(input, 'abcd').perform
48
wait.until { input.property(:value).length == 4 }
49
expect(input.property(:value)).to eq('abcd')
50
end
51
52
it 'sends keys with multiple arguments' do
53
driver.navigate.to url_for('formPage.html')
54
55
input = driver.find_element(css: '#working')
56
input.click
57
58
driver.action.send_keys('abcd', 'dcba').perform
59
wait.until { input.property(:value).length == 8 }
60
expect(input.property(:value)).to eq('abcddcba')
61
end
62
63
it 'sends non-ASCII keys' do
64
driver.navigate.to url_for('formPage.html')
65
66
input = driver.find_element(css: '#working')
67
input.click
68
69
driver.action.send_keys('abcd', :left, 'a').perform
70
wait.until { input.property(:value).length == 5 }
71
expect(input.property(:value)).to eq('abcad')
72
end
73
end
74
75
describe 'multiple key presses' do
76
it 'sends keys with shift pressed', except: {browser: %i[safari safari_preview]} do
77
driver.navigate.to url_for('javascriptPage.html')
78
79
event_input = driver.find_element(id: 'theworks')
80
keylogger = driver.find_element(id: 'result')
81
82
event_input.click
83
84
driver.action.key_down(:shift).send_keys('ab').key_up(:shift).perform
85
wait.until { event_input.property(:value).length == 2 }
86
87
expect(event_input.property(:value)).to eq('AB')
88
expected = keylogger.text.strip
89
expect(expected).to match(/^(focus )?keydown keydown keypress keyup keydown keypress keyup keyup$/)
90
end
91
92
it 'press and release modifier keys' do
93
driver.navigate.to url_for('javascriptPage.html')
94
95
event_input = driver.find_element(id: 'theworks')
96
keylogger = driver.find_element(id: 'result')
97
98
event_input.click
99
100
driver.action.key_down(:shift).perform
101
wait.until { keylogger.text.include? 'down' }
102
expect(keylogger.text).to match(/keydown *$/)
103
104
driver.action.key_up(:shift).perform
105
wait.until { keylogger.text.include? 'up' }
106
expect(keylogger.text).to match(/keyup *$/)
107
end
108
end
109
110
describe '#release_actions' do
111
it 'releases pressed keys' do
112
driver.navigate.to url_for('javascriptPage.html')
113
114
event_input = driver.find_element(id: 'theworks')
115
keylogger = driver.find_element(id: 'result')
116
117
event_input.click
118
119
driver.action.key_down(:shift).perform
120
wait.until { keylogger.text.include? 'down' }
121
expect(keylogger.text).to match(/keydown *$/)
122
123
driver.action.release_actions
124
wait.until { keylogger.text.include? 'up' }
125
expect(keylogger.text).to match(/keyup *$/)
126
end
127
128
it 'releases pressed buttons', except: [{browser: %i[safari safari_preview]},
129
{driver: :remote, browser: :ie}] do
130
driver.navigate.to url_for('javascriptPage.html')
131
132
event_input = driver.find_element(id: 'clickField')
133
134
driver.action.click_and_hold(event_input).perform
135
expect(event_input.property(:value)).to eq('Hello')
136
137
driver.action.release_actions
138
expect(event_input.property(:value)).to eq('Clicked')
139
end
140
end
141
142
describe '#click' do
143
it 'clicks provided element' do
144
driver.navigate.to url_for('javascriptPage.html')
145
element = driver.find_element(id: 'clickField')
146
driver.action.click(element).perform
147
expect(element.property(:value)).to eq('Clicked')
148
end
149
150
it 'executes with equivalent pointer methods' do
151
driver.navigate.to url_for('javascriptPage.html')
152
element = driver.find_element(id: 'clickField')
153
driver.action.move_to(element).pointer_down(:left).pointer_up(:left).perform
154
expect(element.property(:value)).to eq('Clicked')
155
end
156
end
157
158
describe '#double_click' do
159
# https://issues.chromium.org/issues/400087471
160
before { reset_driver! if GlobalTestEnv.rbe? && GlobalTestEnv.browser == :chrome }
161
162
it 'presses pointer twice', except: {browser: %i[safari safari_preview]} do
163
driver.navigate.to url_for('javascriptPage.html')
164
element = driver.find_element(id: 'doubleClickField')
165
166
driver.action.double_click(element).perform
167
expect(element.property(:value)).to eq('DoubleClicked')
168
end
169
170
it 'executes with equivalent pointer methods', except: {browser: %i[safari safari_preview]} do
171
driver.navigate.to url_for('javascriptPage.html')
172
element = driver.find_element(id: 'doubleClickField')
173
174
driver.action.move_to(element)
175
.pointer_down(:left).pointer_up(:left)
176
.pointer_down(:left).pointer_up(:left)
177
.perform
178
expect(element.property(:value)).to eq('DoubleClicked')
179
end
180
end
181
182
describe '#context_click' do
183
it 'right clicks an element' do
184
driver.navigate.to url_for('javascriptPage.html')
185
element = driver.find_element(id: 'doubleClickField')
186
187
driver.action.context_click(element).perform
188
expect(element.property(:value)).to eq('ContextClicked')
189
end
190
191
it 'executes with equivalent pointer methods' do
192
driver.navigate.to url_for('javascriptPage.html')
193
element = driver.find_element(id: 'doubleClickField')
194
195
driver.action.move_to(element).pointer_down(:right).pointer_up(:right).perform
196
expect(element.property(:value)).to eq('ContextClicked')
197
end
198
end
199
200
describe '#move_to' do
201
it 'moves to element' do
202
driver.navigate.to url_for('javascriptPage.html')
203
element = driver.find_element(id: 'clickField')
204
driver.action.move_to(element).click.perform
205
206
expect(element.property(:value)).to eq('Clicked')
207
end
208
209
it 'moves to element with offset' do
210
driver.navigate.to url_for('javascriptPage.html')
211
origin = driver.find_element(id: 'keyUpArea')
212
destination = driver.find_element(id: 'clickField')
213
origin_rect = origin.rect
214
destination_rect = destination.rect
215
x_offset = (destination_rect.x - origin_rect.x).ceil
216
y_offset = (destination_rect.y - origin_rect.y).ceil
217
218
driver.action.move_to(origin, x_offset, y_offset).click.perform
219
expect(destination.property(:value)).to eq('Clicked')
220
end
221
end
222
223
describe '#drag_and_drop' do
224
it 'moves one element to another' do
225
driver.navigate.to url_for('droppableItems.html')
226
227
draggable = long_wait.until do
228
driver.find_element(id: 'draggable')
229
end
230
231
droppable = driver.find_element(id: 'droppable')
232
233
driver.action.drag_and_drop(draggable, droppable).perform
234
235
text = droppable.find_element(tag_name: 'p').text
236
expect(text).to eq('Dropped!')
237
end
238
end
239
240
describe '#drag_and_drop_by' do
241
it 'moves one element a provided distance' do
242
driver.navigate.to url_for('droppableItems.html')
243
244
draggable = long_wait.until do
245
driver.find_element(id: 'draggable')
246
end
247
248
driver.action.drag_and_drop_by(draggable, 138, 50).perform
249
250
droppable = driver.find_element(id: 'droppable')
251
text = droppable.find_element(tag_name: 'p').text
252
expect(text).to eq('Dropped!')
253
end
254
end
255
256
describe '#move_to_location' do
257
it 'moves pointer to specified coordinates' do
258
driver.navigate.to url_for('javascriptPage.html')
259
element = driver.find_element(id: 'clickField')
260
rect = element.rect
261
driver.action.move_to_location(rect.x.ceil, rect.y.ceil).click.perform
262
263
expect(element.property(:value)).to eq('Clicked')
264
end
265
end
266
267
describe 'pen stylus', except: [{browser: :firefox, reason: 'Unknown pointerType'},
268
{browser: :safari, reason: 'Some issues with resolution?'}] do
269
it 'sets pointer event properties' do
270
driver.navigate.to url_for('pointerActionsPage.html')
271
pointer_area = driver.find_element(id: 'pointerArea')
272
rect = pointer_area.rect
273
x_val = rect.x
274
y_val = rect.y
275
276
pointer_options = {pressure: 0.8, tilt_x: -40, tilt_y: -10, twist: 177}
277
actions = driver.action(devices: :pen)
278
.move_by(x_val + 5, y_val + 5)
279
.pointer_down
280
.move_by(2, 2, duration: 0.8, **pointer_options)
281
.pointer_up
282
283
start = Time.now
284
actions.perform
285
expect(Time.now - start).to be > 0.8
286
287
moves = driver.find_elements(class: 'pointermove')
288
move_to = properties(moves[0])
289
down = properties(driver.find_element(class: 'pointerdown'))
290
move_by = properties(moves[1])
291
up = properties(driver.find_element(class: 'pointerup'))
292
293
expect(move_to).to include('button' => '-1',
294
'pageX' => (x_val + 5).to_s,
295
'pageY' => (y_val + 5).floor.to_s)
296
expect(down).to include('button' => '0')
297
expect(move_by).to include('button' => '-1',
298
'pageX' => (x_val + 5 + 2).to_s,
299
'pageY' => (y_val + 5 + 2).floor.to_s,
300
'tiltX' => '-40',
301
'tiltY' => '-10',
302
'twist' => '177')
303
expect(up).to include('button' => '0',
304
'pageX' => (x_val + 5 + 2).to_s,
305
'pageY' => (y_val + 5 + 2).floor.to_s)
306
end
307
end
308
309
describe '#scroll_to' do
310
it 'scrolls to element',
311
exclusive: {browser: %i[chrome edge], reason: 'incorrect MoveTargetOutOfBoundsError'} do
312
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
313
iframe = driver.find_element(tag_name: 'iframe')
314
315
expect(in_viewport?(iframe)).to be false
316
317
driver.action.scroll_to(iframe).perform
318
319
expect(in_viewport?(iframe)).to be true
320
end
321
end
322
323
describe '#scroll_by' do
324
it 'scrolls by given amount',
325
exclusive: {browser: %i[chrome edge], reason: 'inconsistent behavior between versions'} do
326
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
327
footer = driver.find_element(tag_name: 'footer')
328
delta_y = footer.rect.y.round
329
330
driver.action.scroll_by(0, delta_y).perform
331
sleep 0.5
332
333
expect(in_viewport?(footer)).to be true
334
end
335
end
336
337
describe '#scroll_from' do
338
it 'scrolls from element by given amount',
339
exclusive: {browser: %i[chrome edge], reason: 'incorrect MoveTargetOutOfBoundsError in Firefox'} do
340
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
341
iframe = driver.find_element(tag_name: 'iframe')
342
scroll_origin = WheelActions::ScrollOrigin.element(iframe)
343
344
driver.action.scroll_from(scroll_origin, 0, 200).perform
345
346
driver.switch_to.frame(iframe)
347
sleep 0.5
348
checkbox = driver.find_element(name: 'scroll_checkbox')
349
expect(in_viewport?(checkbox)).to be true
350
end
351
352
it 'scrolls from element by given amount with offset',
353
exclusive: {browser: %i[chrome edge], reason: 'incorrect MoveTargetOutOfBoundsError in Firefox'} do
354
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
355
footer = driver.find_element(tag_name: 'footer')
356
scroll_origin = WheelActions::ScrollOrigin.element(footer, 0, -50)
357
358
driver.action.scroll_from(scroll_origin, 0, 200).perform
359
sleep 0.5
360
361
iframe = driver.find_element(tag_name: 'iframe')
362
driver.switch_to.frame(iframe)
363
checkbox = driver.find_element(name: 'scroll_checkbox')
364
expect(in_viewport?(checkbox)).to be true
365
end
366
367
it 'raises MoveTargetOutOfBoundsError when origin offset from element is out of viewport' do
368
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
369
footer = driver.find_element(tag_name: 'footer')
370
scroll_origin = WheelActions::ScrollOrigin.element(footer, 0, 50)
371
372
expect {
373
driver.action.scroll_from(scroll_origin, 0, 200).perform
374
}.to raise_error(Error::MoveTargetOutOfBoundsError)
375
end
376
377
it 'scrolls by given amount with offset' do
378
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame.html')
379
scroll_origin = WheelActions::ScrollOrigin.viewport(10, 10)
380
381
driver.action.scroll_from(scroll_origin, 0, 200).perform
382
383
iframe = driver.find_element(tag_name: 'iframe')
384
driver.switch_to.frame(iframe)
385
checkbox = driver.find_element(name: 'scroll_checkbox')
386
387
expect {
388
wait.until { in_viewport?(checkbox) }
389
}.not_to raise_error
390
end
391
392
it 'raises MoveTargetOutOfBoundsError when origin offset is out of viewport',
393
only: {browser: %i[chrome edge firefox]} do
394
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame.html')
395
scroll_origin = WheelActions::ScrollOrigin.viewport(-10, -10)
396
397
expect {
398
driver.action.scroll_from(scroll_origin, 0, 200).perform
399
}.to raise_error(Error::MoveTargetOutOfBoundsError)
400
end
401
end
402
403
def properties(element)
404
element.text.sub(/.*?\s/, '').split(',').to_h { |item| item.lstrip.split(/\s*:\s*/) }
405
end
406
407
def in_viewport?(element)
408
in_viewport = <<~IN_VIEWPORT
409
for(var e=arguments[0],f=e.offsetTop,t=e.offsetLeft,o=e.offsetWidth,n=e.offsetHeight;
410
e.offsetParent;)f+=(e=e.offsetParent).offsetTop,t+=e.offsetLeft;
411
return f<window.pageYOffset+window.innerHeight&&t<window.pageXOffset+window.innerWidth&&f+n>
412
window.pageYOffset&&t+o>window.pageXOffset
413
IN_VIEWPORT
414
415
driver.execute_script(in_viewport, element)
416
end
417
end # ActionBuilder
418
end # WebDriver
419
end # Selenium
420
421