selenium-webdriver 3.4.0 → 3.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGES +35 -1
  2. data/Gemfile.lock +54 -0
  3. data/lib/selenium/webdriver.rb +8 -10
  4. data/lib/selenium/webdriver/chrome.rb +2 -1
  5. data/lib/selenium/webdriver/chrome/{bridge.rb → driver.rb} +47 -32
  6. data/lib/selenium/webdriver/chrome/options.rb +170 -0
  7. data/lib/selenium/webdriver/common/driver.rb +20 -39
  8. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +5 -0
  9. data/lib/selenium/webdriver/common/element.rb +5 -1
  10. data/lib/selenium/webdriver/common/logger.rb +10 -0
  11. data/lib/selenium/webdriver/common/w3c_error.rb +2 -2
  12. data/lib/selenium/webdriver/edge.rb +2 -1
  13. data/lib/selenium/webdriver/edge/bridge.rb +2 -91
  14. data/lib/selenium/webdriver/edge/driver.rb +80 -0
  15. data/lib/selenium/webdriver/firefox.rb +6 -3
  16. data/lib/selenium/webdriver/firefox/driver.rb +50 -0
  17. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  18. data/lib/selenium/webdriver/firefox/legacy/driver.rb +81 -0
  19. data/lib/selenium/webdriver/firefox/marionette/driver.rb +101 -0
  20. data/lib/selenium/webdriver/firefox/options.rb +139 -0
  21. data/lib/selenium/webdriver/ie.rb +1 -1
  22. data/lib/selenium/webdriver/ie/{bridge.rb → driver.rb} +13 -13
  23. data/lib/selenium/webdriver/phantomjs.rb +1 -1
  24. data/lib/selenium/webdriver/phantomjs/{bridge.rb → driver.rb} +10 -13
  25. data/lib/selenium/webdriver/remote.rb +11 -13
  26. data/lib/selenium/webdriver/remote/bridge.rb +87 -586
  27. data/lib/selenium/webdriver/remote/capabilities.rb +3 -1
  28. data/lib/selenium/webdriver/remote/driver.rb +44 -0
  29. data/lib/selenium/webdriver/remote/http/default.rb +1 -1
  30. data/lib/selenium/webdriver/remote/oss/bridge.rb +586 -0
  31. data/lib/selenium/webdriver/remote/{commands.rb → oss/commands.rb} +9 -5
  32. data/lib/selenium/webdriver/remote/oss/driver.rb +44 -0
  33. data/lib/selenium/webdriver/remote/w3c/bridge.rb +573 -0
  34. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +266 -0
  35. data/lib/selenium/webdriver/remote/{w3c_commands.rb → w3c/commands.rb} +9 -4
  36. data/lib/selenium/webdriver/remote/w3c/driver.rb +41 -0
  37. data/lib/selenium/webdriver/safari.rb +3 -6
  38. data/lib/selenium/webdriver/safari/{bridge.rb → driver.rb} +14 -6
  39. data/selenium-webdriver.gemspec +1 -3
  40. metadata +29 -45
  41. data/lib/selenium/webdriver/firefox/bridge.rb +0 -70
  42. data/lib/selenium/webdriver/firefox/w3c_bridge.rb +0 -114
  43. data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -663
  44. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -231
@@ -20,14 +20,16 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Remote
23
- class Bridge
23
+ module OSS
24
+
24
25
  #
25
26
  # https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#command-reference
27
+ # @api private
26
28
  #
27
29
 
28
- COMMANDS = {
30
+ class Bridge
29
31
 
30
- new_session: [:post, 'session'.freeze],
32
+ COMMANDS = {
31
33
  get_capabilities: [:get, 'session/:session_id'.freeze],
32
34
  status: [:get, 'status'.freeze],
33
35
 
@@ -212,8 +214,10 @@ module Selenium
212
214
 
213
215
  get_available_log_types: [:get, 'session/:session_id/log/types'.freeze],
214
216
  get_log: [:post, 'session/:session_id/log'.freeze]
215
- }.freeze
216
- end
217
+ }.freeze
218
+
219
+ end # Bridge
220
+ end # OSS
217
221
  end # Remote
218
222
  end # WebDriver
219
223
  end # Selenium
@@ -0,0 +1,44 @@
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 Remote
23
+ module OSS
24
+
25
+ #
26
+ # Driver implementation for remote server talking OSS dialect.
27
+ # @api private
28
+ #
29
+
30
+ class Driver < WebDriver::Driver
31
+ include DriverExtensions::UploadsFiles
32
+ include DriverExtensions::TakesScreenshot
33
+ include DriverExtensions::HasSessionId
34
+ include DriverExtensions::Rotatable
35
+ include DriverExtensions::HasTouchScreen
36
+ include DriverExtensions::HasLocation
37
+ include DriverExtensions::HasNetworkConnection
38
+ include DriverExtensions::HasRemoteStatus
39
+ include DriverExtensions::HasWebStorage
40
+ end # Driver
41
+ end # OSS
42
+ end # Remote
43
+ end # WebDriver
44
+ end # Selenium
@@ -0,0 +1,573 @@
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
+ require 'json'
21
+
22
+ module Selenium
23
+ module WebDriver
24
+ module Remote
25
+ module W3C
26
+
27
+ #
28
+ # Low level bridge to the remote server implementing JSON wire
29
+ # protocol (W3C dialect), through which the rest of the API works.
30
+ # @api private
31
+ #
32
+
33
+ class Bridge < Remote::Bridge
34
+
35
+ def initialize(capabilities, session_id, **opts)
36
+ @capabilities = capabilities
37
+ @session_id = session_id
38
+ super(opts)
39
+ end
40
+
41
+ def dialect
42
+ :w3c
43
+ end
44
+
45
+ def commands(command)
46
+ case command
47
+ when :status, :is_element_displayed
48
+ Remote::OSS::Bridge::COMMANDS[command]
49
+ else
50
+ COMMANDS[command]
51
+ end
52
+ end
53
+
54
+ def status
55
+ execute :status
56
+ end
57
+
58
+ def get(url)
59
+ execute :get, {}, {url: url}
60
+ end
61
+
62
+ def implicit_wait_timeout=(milliseconds)
63
+ timeout('implicit', milliseconds)
64
+ end
65
+
66
+ def script_timeout=(milliseconds)
67
+ timeout('script', milliseconds)
68
+ end
69
+
70
+ def timeout(type, milliseconds)
71
+ type = 'pageLoad' if type == 'page load'
72
+ execute :set_timeout, {}, {type => milliseconds}
73
+ end
74
+
75
+ #
76
+ # alerts
77
+ #
78
+
79
+ def accept_alert
80
+ execute :accept_alert
81
+ end
82
+
83
+ def dismiss_alert
84
+ execute :dismiss_alert
85
+ end
86
+
87
+ def alert=(keys)
88
+ execute :send_alert_text, {}, {value: keys.split(//), text: keys}
89
+ end
90
+
91
+ def alert_text
92
+ execute :get_alert_text
93
+ end
94
+
95
+ #
96
+ # navigation
97
+ #
98
+
99
+ def go_back
100
+ execute :back
101
+ end
102
+
103
+ def go_forward
104
+ execute :forward
105
+ end
106
+
107
+ def url
108
+ execute :get_current_url
109
+ end
110
+
111
+ def title
112
+ execute :get_title
113
+ end
114
+
115
+ def page_source
116
+ execute_script('var source = document.documentElement.outerHTML;' \
117
+ 'if (!source) { source = new XMLSerializer().serializeToString(document); }' \
118
+ 'return source;')
119
+ end
120
+
121
+ def switch_to_window(name)
122
+ execute :switch_to_window, {}, {handle: name}
123
+ end
124
+
125
+ def switch_to_frame(id)
126
+ id = find_element_by('id', id) if id.is_a? String
127
+ execute :switch_to_frame, {}, {id: id}
128
+ end
129
+
130
+ def switch_to_parent_frame
131
+ execute :switch_to_parent_frame
132
+ end
133
+
134
+ def switch_to_default_content
135
+ switch_to_frame nil
136
+ end
137
+
138
+ QUIT_ERRORS = [IOError].freeze
139
+
140
+ def quit
141
+ execute :delete_session
142
+ http.close
143
+ rescue *QUIT_ERRORS
144
+ end
145
+
146
+ def close
147
+ execute :close_window
148
+ end
149
+
150
+ def refresh
151
+ execute :refresh
152
+ end
153
+
154
+ #
155
+ # window handling
156
+ #
157
+
158
+ def window_handles
159
+ execute :get_window_handles
160
+ end
161
+
162
+ def window_handle
163
+ execute :get_window_handle
164
+ end
165
+
166
+ def resize_window(width, height, handle = :current)
167
+ unless handle == :current
168
+ raise Error::WebDriverError, 'Switch to desired window before changing its size'
169
+ end
170
+ execute :set_window_rect, {}, {width: width, height: height}
171
+ end
172
+
173
+ def window_size(handle = :current)
174
+ unless handle == :current
175
+ raise Error::UnsupportedOperationError, 'Switch to desired window before getting its size'
176
+ end
177
+ data = execute :get_window_rect
178
+
179
+ Dimension.new data['width'], data['height']
180
+ end
181
+
182
+ def minimize_window
183
+ execute :minimize_window
184
+ end
185
+
186
+ def maximize_window(handle = :current)
187
+ unless handle == :current
188
+ raise Error::UnsupportedOperationError, 'Switch to desired window before changing its size'
189
+ end
190
+ execute :maximize_window
191
+ end
192
+
193
+ def full_screen_window
194
+ execute :fullscreen_window
195
+ end
196
+
197
+ def reposition_window(x, y)
198
+ execute :set_window_rect, {}, {x: x, y: y}
199
+ end
200
+
201
+ def window_position
202
+ data = execute :get_window_rect
203
+ Point.new data['x'], data['y']
204
+ end
205
+
206
+ def set_window_rect(x: nil, y: nil, width: nil, height: nil)
207
+ execute :set_window_rect, {}, {x: x, y: y, width: width, height: height}
208
+ end
209
+
210
+ def window_rect
211
+ data = execute :get_window_rect
212
+ Rectangle.new data['x'], data['y'], data['width'], data['height']
213
+ end
214
+
215
+ def screenshot
216
+ execute :take_screenshot
217
+ end
218
+
219
+ #
220
+ # HTML 5
221
+ #
222
+
223
+ def local_storage_item(key, value = nil)
224
+ if value
225
+ execute_script("localStorage.setItem('#{key}', '#{value}')")
226
+ else
227
+ execute_script("return localStorage.getItem('#{key}')")
228
+ end
229
+ end
230
+
231
+ def remove_local_storage_item(key)
232
+ execute_script("localStorage.removeItem('#{key}')")
233
+ end
234
+
235
+ def local_storage_keys
236
+ execute_script('return Object.keys(localStorage)')
237
+ end
238
+
239
+ def clear_local_storage
240
+ execute_script('localStorage.clear()')
241
+ end
242
+
243
+ def local_storage_size
244
+ execute_script('return localStorage.length')
245
+ end
246
+
247
+ def session_storage_item(key, value = nil)
248
+ if value
249
+ execute_script("sessionStorage.setItem('#{key}', '#{value}')")
250
+ else
251
+ execute_script("return sessionStorage.getItem('#{key}')")
252
+ end
253
+ end
254
+
255
+ def remove_session_storage_item(key)
256
+ execute_script("sessionStorage.removeItem('#{key}')")
257
+ end
258
+
259
+ def session_storage_keys
260
+ execute_script('return Object.keys(sessionStorage)')
261
+ end
262
+
263
+ def clear_session_storage
264
+ execute_script('sessionStorage.clear()')
265
+ end
266
+
267
+ def session_storage_size
268
+ execute_script('return sessionStorage.length')
269
+ end
270
+
271
+ def location
272
+ raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting location'
273
+ end
274
+
275
+ def set_location(_lat, _lon, _alt)
276
+ raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting location'
277
+ end
278
+
279
+ def network_connection
280
+ raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting network connection'
281
+ end
282
+
283
+ def network_connection=(_type)
284
+ raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting network connection'
285
+ end
286
+
287
+ #
288
+ # javascript execution
289
+ #
290
+
291
+ def execute_script(script, *args)
292
+ result = execute :execute_script, {}, {script: script, args: args}
293
+ unwrap_script_result result
294
+ end
295
+
296
+ def execute_async_script(script, *args)
297
+ result = execute :execute_async_script, {}, {script: script, args: args}
298
+ unwrap_script_result result
299
+ end
300
+
301
+ #
302
+ # cookies
303
+ #
304
+
305
+ def options
306
+ @options ||= WebDriver::W3COptions.new(self)
307
+ end
308
+
309
+ def add_cookie(cookie)
310
+ execute :add_cookie, {}, {cookie: cookie}
311
+ end
312
+
313
+ def delete_cookie(name)
314
+ execute :delete_cookie, name: name
315
+ end
316
+
317
+ def cookie(name)
318
+ execute :get_cookie, name: name
319
+ end
320
+
321
+ def cookies
322
+ execute :get_all_cookies
323
+ end
324
+
325
+ def delete_all_cookies
326
+ execute :delete_all_cookies
327
+ end
328
+
329
+ #
330
+ # actions
331
+ #
332
+
333
+ def action(async = false)
334
+ W3CActionBuilder.new self,
335
+ Interactions.pointer(:mouse, name: 'mouse'),
336
+ Interactions.key('keyboard'),
337
+ async
338
+ end
339
+ alias_method :actions, :action
340
+
341
+ def mouse
342
+ raise Error::UnsupportedOperationError, '#mouse is no longer supported, use #action instead'
343
+ end
344
+
345
+ def keyboard
346
+ raise Error::UnsupportedOperationError, '#keyboard is no longer supported, use #action instead'
347
+ end
348
+
349
+ def send_actions(data)
350
+ execute :actions, {}, {actions: data}
351
+ end
352
+
353
+ def release_actions
354
+ execute :release_actions
355
+ end
356
+
357
+ def click_element(element)
358
+ execute :element_click, id: element
359
+ end
360
+
361
+ # TODO: - Implement file verification
362
+ def send_keys_to_element(element, keys)
363
+ # Keep .split(//) for backward compatibility for now
364
+ text = keys.join('')
365
+ execute :element_send_keys, {id: element}, {value: text.split(//), text: text}
366
+ end
367
+
368
+ def clear_element(element)
369
+ execute :element_clear, id: element
370
+ end
371
+
372
+ def submit_element(element)
373
+ form = find_element_by('xpath', "./ancestor-or-self::form", element)
374
+ execute_script("var e = arguments[0].ownerDocument.createEvent('Event');" \
375
+ "e.initEvent('submit', true, true);" \
376
+ 'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }', form.as_json)
377
+ end
378
+
379
+ def drag_element(element, right_by, down_by)
380
+ execute :drag_element, {id: element}, {x: right_by, y: down_by}
381
+ end
382
+
383
+ def touch_single_tap(element)
384
+ execute :touch_single_tap, {}, {element: element}
385
+ end
386
+
387
+ def touch_double_tap(element)
388
+ execute :touch_double_tap, {}, {element: element}
389
+ end
390
+
391
+ def touch_long_press(element)
392
+ execute :touch_long_press, {}, {element: element}
393
+ end
394
+
395
+ def touch_down(x, y)
396
+ execute :touch_down, {}, {x: x, y: y}
397
+ end
398
+
399
+ def touch_up(x, y)
400
+ execute :touch_up, {}, {x: x, y: y}
401
+ end
402
+
403
+ def touch_move(x, y)
404
+ execute :touch_move, {}, {x: x, y: y}
405
+ end
406
+
407
+ def touch_scroll(element, x, y)
408
+ if element
409
+ execute :touch_scroll, {}, {element: element,
410
+ xoffset: x,
411
+ yoffset: y}
412
+ else
413
+ execute :touch_scroll, {}, {xoffset: x, yoffset: y}
414
+ end
415
+ end
416
+
417
+ def touch_flick(xspeed, yspeed)
418
+ execute :touch_flick, {}, {xspeed: xspeed, yspeed: yspeed}
419
+ end
420
+
421
+ def touch_element_flick(element, right_by, down_by, speed)
422
+ execute :touch_flick, {}, {element: element,
423
+ xoffset: right_by,
424
+ yoffset: down_by,
425
+ speed: speed}
426
+ end
427
+
428
+ def screen_orientation=(orientation)
429
+ execute :set_screen_orientation, {}, {orientation: orientation}
430
+ end
431
+
432
+ def screen_orientation
433
+ execute :get_screen_orientation
434
+ end
435
+
436
+ #
437
+ # element properties
438
+ #
439
+
440
+ def element_tag_name(element)
441
+ execute :get_element_tag_name, id: element
442
+ end
443
+
444
+ def element_attribute(element, name)
445
+ execute_atom :getAttribute, element, name
446
+ end
447
+
448
+ def element_property(element, name)
449
+ execute :get_element_property, id: element.ref, name: name
450
+ end
451
+
452
+ def element_value(element)
453
+ element_property element, 'value'
454
+ end
455
+
456
+ def element_text(element)
457
+ execute :get_element_text, id: element
458
+ end
459
+
460
+ def element_location(element)
461
+ data = execute :get_element_rect, id: element
462
+
463
+ Point.new data['x'], data['y']
464
+ end
465
+
466
+ def element_rect(element)
467
+ data = execute :get_element_rect, id: element
468
+
469
+ Rectangle.new data['x'], data['y'], data['width'], data['height']
470
+ end
471
+
472
+ def element_location_once_scrolled_into_view(element)
473
+ send_keys_to_element(element, [''])
474
+ element_location(element)
475
+ end
476
+
477
+ def element_size(element)
478
+ data = execute :get_element_rect, id: element
479
+
480
+ Dimension.new data['width'], data['height']
481
+ end
482
+
483
+ def element_enabled?(element)
484
+ execute :is_element_enabled, id: element
485
+ end
486
+
487
+ def element_selected?(element)
488
+ execute :is_element_selected, id: element
489
+ end
490
+
491
+ def element_displayed?(element)
492
+ execute :is_element_displayed, id: element
493
+ end
494
+
495
+ def element_value_of_css_property(element, prop)
496
+ execute :get_element_css_value, id: element, property_name: prop
497
+ end
498
+
499
+ #
500
+ # finding elements
501
+ #
502
+
503
+ def active_element
504
+ Element.new self, element_id_from(execute(:get_active_element))
505
+ end
506
+
507
+ alias_method :switch_to_active_element, :active_element
508
+
509
+ def find_element_by(how, what, parent = nil)
510
+ how, what = convert_locators(how, what)
511
+
512
+ id = if parent
513
+ execute :find_child_element, {id: parent}, {using: how, value: what}
514
+ else
515
+ execute :find_element, {}, {using: how, value: what}
516
+ end
517
+ Element.new self, element_id_from(id)
518
+ end
519
+
520
+ def find_elements_by(how, what, parent = nil)
521
+ how, what = convert_locators(how, what)
522
+
523
+ ids = if parent
524
+ execute :find_child_elements, {id: parent}, {using: how, value: what}
525
+ else
526
+ execute :find_elements, {}, {using: how, value: what}
527
+ end
528
+
529
+ ids.map { |id| Element.new self, element_id_from(id) }
530
+ end
531
+
532
+ private
533
+
534
+ def execute(*)
535
+ super['value']
536
+ end
537
+
538
+ def convert_locators(how, what)
539
+ case how
540
+ when 'class name'
541
+ how = 'css selector'
542
+ what = ".#{escape_css(what)}"
543
+ when 'id'
544
+ how = 'css selector'
545
+ what = "##{escape_css(what)}"
546
+ when 'name'
547
+ how = 'css selector'
548
+ what = "*[name='#{escape_css(what)}']"
549
+ when 'tag name'
550
+ how = 'css selector'
551
+ end
552
+ [how, what]
553
+ end
554
+
555
+ ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]\(\)])/
556
+ UNICODE_CODE_POINT = 30
557
+
558
+ # Escapes invalid characters in CSS selector.
559
+ # @see https://mathiasbynens.be/notes/css-escapes
560
+ def escape_css(string)
561
+ string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
562
+ if !string.empty? && string[0] =~ /[[:digit:]]/
563
+ string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..-1]}"
564
+ end
565
+
566
+ string
567
+ end
568
+
569
+ end # Bridge
570
+ end # W3C
571
+ end # Remote
572
+ end # WebDriver
573
+ end # Selenium