selenium-webdriver 4.1.0 → 4.15.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.
- checksums.yaml +4 -4
- data/CHANGES +304 -1
- data/Gemfile +2 -0
- data/LICENSE +1 -1
- data/NOTICE +1 -1
- data/README.md +2 -2
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/server.rb +36 -39
- data/lib/selenium/webdriver/atoms/findElements.js +3 -4
- data/lib/selenium/webdriver/atoms/getAttribute.js +0 -0
- data/lib/selenium/webdriver/atoms/isDisplayed.js +0 -0
- data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
- data/lib/selenium/webdriver/atoms.rb +5 -3
- data/lib/selenium/webdriver/bidi/browsing_context.rb +88 -0
- data/lib/selenium/webdriver/bidi/browsing_context_info.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/base_log_entry.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +35 -0
- data/lib/selenium/webdriver/{common/driver_extensions/has_network_connection.rb → bidi/log/filter_by.rb} +14 -11
- data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log_inspector.rb +143 -0
- data/lib/selenium/webdriver/bidi/navigate_result.rb +33 -0
- data/lib/selenium/webdriver/bidi/session.rb +51 -0
- data/lib/selenium/webdriver/bidi.rb +56 -0
- data/lib/selenium/webdriver/chrome/driver.rb +9 -29
- data/lib/selenium/webdriver/chrome/features.rb +9 -67
- data/lib/selenium/webdriver/chrome/options.rb +3 -223
- data/lib/selenium/webdriver/chrome/profile.rb +3 -83
- data/lib/selenium/webdriver/chrome/service.rb +4 -19
- data/lib/selenium/webdriver/chrome.rb +0 -16
- data/lib/selenium/webdriver/chromium/driver.rb +60 -0
- data/lib/selenium/webdriver/chromium/features.rb +99 -0
- data/lib/selenium/webdriver/chromium/options.rb +243 -0
- data/lib/selenium/webdriver/chromium/profile.rb +113 -0
- data/lib/selenium/webdriver/chromium.rb +29 -0
- data/lib/selenium/webdriver/common/action_builder.rb +62 -22
- data/lib/selenium/webdriver/common/child_process.rb +124 -0
- data/lib/selenium/webdriver/common/driver.rb +31 -81
- data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +0 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/{has_location.rb → has_bidi.rb} +9 -10
- data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -4
- data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_file_downloads.rb +65 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -69
- data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -3
- data/lib/selenium/webdriver/common/driver_finder.rb +45 -0
- data/lib/selenium/webdriver/common/element.rb +8 -8
- data/lib/selenium/webdriver/common/error.rb +28 -5
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
- data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
- data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
- data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
- data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
- data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
- data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +59 -70
- data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
- data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
- data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
- data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
- data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
- data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
- data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
- data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
- data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
- data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
- data/lib/selenium/webdriver/common/keys.rb +1 -0
- data/lib/selenium/webdriver/common/local_driver.rb +46 -0
- data/lib/selenium/webdriver/common/logger.rb +90 -25
- data/lib/selenium/webdriver/common/manager.rb +0 -27
- data/lib/selenium/webdriver/common/options.rb +17 -17
- data/lib/selenium/webdriver/common/platform.rb +5 -51
- data/lib/selenium/webdriver/common/port_prober.rb +1 -1
- data/lib/selenium/webdriver/common/profile_helper.rb +1 -1
- data/lib/selenium/webdriver/common/proxy.rb +2 -2
- data/lib/selenium/webdriver/common/search_context.rb +0 -6
- data/lib/selenium/webdriver/common/selenium_manager.rb +134 -0
- data/lib/selenium/webdriver/common/service.rb +21 -30
- data/lib/selenium/webdriver/common/service_manager.rb +8 -15
- data/lib/selenium/webdriver/common/shadow_root.rb +2 -3
- data/lib/selenium/webdriver/common/socket_lock.rb +3 -3
- data/lib/selenium/webdriver/common/socket_poller.rb +2 -2
- data/lib/selenium/webdriver/common/takes_screenshot.rb +2 -3
- data/lib/selenium/webdriver/common/target_locator.rb +2 -3
- data/lib/selenium/webdriver/common/timeouts.rb +2 -2
- data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
- data/lib/selenium/webdriver/common/websocket_connection.rb +164 -0
- data/lib/selenium/webdriver/common/window.rb +6 -6
- data/lib/selenium/webdriver/common/zipper.rb +1 -1
- data/lib/selenium/webdriver/common.rb +22 -5
- data/lib/selenium/webdriver/devtools/console_event.rb +0 -2
- data/lib/selenium/webdriver/devtools/exception_event.rb +0 -2
- data/lib/selenium/webdriver/devtools/mutation_event.rb +0 -2
- data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
- data/lib/selenium/webdriver/devtools/pinned_script.rb +0 -2
- data/lib/selenium/webdriver/devtools/request.rb +1 -3
- data/lib/selenium/webdriver/devtools/response.rb +1 -3
- data/lib/selenium/webdriver/devtools.rb +17 -114
- data/lib/selenium/webdriver/edge/driver.rb +9 -3
- data/lib/selenium/webdriver/edge/features.rb +8 -4
- data/lib/selenium/webdriver/edge/options.rb +17 -5
- data/lib/selenium/webdriver/edge/profile.rb +2 -2
- data/lib/selenium/webdriver/edge/service.rb +8 -7
- data/lib/selenium/webdriver/edge.rb +0 -2
- data/lib/selenium/webdriver/firefox/driver.rb +9 -2
- data/lib/selenium/webdriver/firefox/features.rb +11 -7
- data/lib/selenium/webdriver/firefox/options.rb +7 -16
- data/lib/selenium/webdriver/firefox/profile.rb +8 -12
- data/lib/selenium/webdriver/firefox/service.rb +0 -18
- data/lib/selenium/webdriver/firefox/util.rb +46 -0
- data/lib/selenium/webdriver/firefox.rb +1 -14
- data/lib/selenium/webdriver/ie/driver.rb +7 -1
- data/lib/selenium/webdriver/{common/driver_extensions/has_remote_status.rb → ie/features.rb} +10 -7
- data/lib/selenium/webdriver/ie/options.rb +4 -3
- data/lib/selenium/webdriver/ie/service.rb +0 -22
- data/lib/selenium/webdriver/ie.rb +4 -17
- data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +10 -9
- data/lib/selenium/webdriver/remote/bridge.rb +63 -52
- data/lib/selenium/webdriver/remote/capabilities.rb +3 -53
- data/lib/selenium/webdriver/remote/driver.rb +35 -14
- data/lib/selenium/webdriver/remote/features.rb +75 -0
- data/lib/selenium/webdriver/remote/http/common.rb +3 -3
- data/lib/selenium/webdriver/remote/http/curb.rb +1 -3
- data/lib/selenium/webdriver/remote/http/default.rb +8 -14
- data/lib/selenium/webdriver/remote/response.rb +2 -3
- data/lib/selenium/webdriver/remote/server_error.rb +1 -1
- data/lib/selenium/webdriver/remote.rb +1 -1
- data/lib/selenium/webdriver/safari/driver.rb +7 -1
- data/lib/selenium/webdriver/safari/features.rb +5 -3
- data/lib/selenium/webdriver/safari/options.rb +5 -1
- data/lib/selenium/webdriver/safari/service.rb +10 -4
- data/lib/selenium/webdriver/safari.rb +1 -15
- data/lib/selenium/webdriver/support/color.rb +22 -22
- data/lib/selenium/webdriver/support/guards/guard.rb +6 -5
- data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -3
- data/lib/selenium/webdriver/support/guards.rb +2 -2
- data/lib/selenium/webdriver/support/relative_locator.rb +0 -1
- data/lib/selenium/webdriver/support/select.rb +3 -1
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +6 -4
- data/selenium-webdriver.gemspec +14 -12
- metadata +76 -65
- data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
- data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
- data/lib/selenium/webdriver/support/cdp_client_generator.rb +0 -108
|
@@ -21,6 +21,7 @@ module Selenium
|
|
|
21
21
|
module WebDriver
|
|
22
22
|
module Remote
|
|
23
23
|
class Bridge
|
|
24
|
+
autoload :COMMANDS, 'selenium/webdriver/remote/bridge/commands'
|
|
24
25
|
include Atoms
|
|
25
26
|
|
|
26
27
|
PORT = 4444
|
|
@@ -30,8 +31,8 @@ module Selenium
|
|
|
30
31
|
|
|
31
32
|
#
|
|
32
33
|
# Initializes the bridge with the given server URL
|
|
33
|
-
# @param [String, URI]
|
|
34
|
-
# @param [Object]
|
|
34
|
+
# @param [String, URI] url url for the remote server
|
|
35
|
+
# @param [Object] http_client an HTTP client instance that implements the same protocol as Http::Default
|
|
35
36
|
# @api private
|
|
36
37
|
#
|
|
37
38
|
|
|
@@ -118,7 +119,7 @@ module Selenium
|
|
|
118
119
|
end
|
|
119
120
|
|
|
120
121
|
def alert=(keys)
|
|
121
|
-
execute :send_alert_text, {}, {value: keys.
|
|
122
|
+
execute :send_alert_text, {}, {value: keys.chars, text: keys}
|
|
122
123
|
end
|
|
123
124
|
|
|
124
125
|
def alert_text
|
|
@@ -146,9 +147,7 @@ module Selenium
|
|
|
146
147
|
end
|
|
147
148
|
|
|
148
149
|
def page_source
|
|
149
|
-
|
|
150
|
-
'if (!source) { source = new XMLSerializer().serializeToString(document); }' \
|
|
151
|
-
'return source;')
|
|
150
|
+
execute :get_page_source
|
|
152
151
|
end
|
|
153
152
|
|
|
154
153
|
#
|
|
@@ -188,6 +187,7 @@ module Selenium
|
|
|
188
187
|
execute :delete_session
|
|
189
188
|
http.close
|
|
190
189
|
rescue *QUIT_ERRORS
|
|
190
|
+
nil
|
|
191
191
|
end
|
|
192
192
|
|
|
193
193
|
def close
|
|
@@ -369,21 +369,10 @@ module Selenium
|
|
|
369
369
|
# actions
|
|
370
370
|
#
|
|
371
371
|
|
|
372
|
-
def action(async
|
|
373
|
-
ActionBuilder.new self,
|
|
374
|
-
Interactions.pointer(:mouse, name: 'mouse'),
|
|
375
|
-
Interactions.key('keyboard'),
|
|
376
|
-
async
|
|
377
|
-
end
|
|
378
|
-
alias_method :actions, :action
|
|
379
|
-
|
|
380
|
-
def mouse
|
|
381
|
-
raise Error::UnsupportedOperationError, '#mouse is no longer supported, use #action instead'
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
def keyboard
|
|
385
|
-
raise Error::UnsupportedOperationError, '#keyboard is no longer supported, use #action instead'
|
|
372
|
+
def action(async: false, devices: [], duration: 250)
|
|
373
|
+
ActionBuilder.new self, async: async, devices: devices, duration: duration
|
|
386
374
|
end
|
|
375
|
+
alias actions action
|
|
387
376
|
|
|
388
377
|
def send_actions(data)
|
|
389
378
|
execute :actions, {}, {actions: data}
|
|
@@ -402,27 +391,9 @@ module Selenium
|
|
|
402
391
|
end
|
|
403
392
|
|
|
404
393
|
def send_keys_to_element(element, keys)
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if local_files.any?
|
|
409
|
-
keys = local_files.map { |local_file| upload(local_file) }
|
|
410
|
-
keys = Array(keys.join("\n"))
|
|
411
|
-
end
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
# Keep .split(//) for backward compatibility for now
|
|
415
|
-
text = keys.join('')
|
|
416
|
-
execute :element_send_keys, {id: element}, {value: text.split(//), text: text}
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
def upload(local_file)
|
|
420
|
-
unless File.file?(local_file)
|
|
421
|
-
WebDriver.logger.debug("File detector only works with files. #{local_file.inspect} isn`t a file!")
|
|
422
|
-
raise Error::WebDriverError, "You are trying to work with something that isn't a file."
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
execute :upload_file, {}, {file: Zipper.zip_file(local_file)}
|
|
394
|
+
keys = upload_if_necessary(keys) if @file_detector
|
|
395
|
+
text = keys.join
|
|
396
|
+
execute :element_send_keys, {id: element}, {value: text.chars, text: text}
|
|
426
397
|
end
|
|
427
398
|
|
|
428
399
|
def clear_element(element)
|
|
@@ -430,10 +401,19 @@ module Selenium
|
|
|
430
401
|
end
|
|
431
402
|
|
|
432
403
|
def submit_element(element)
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
404
|
+
script = "/* submitForm */ var form = arguments[0];\n" \
|
|
405
|
+
"while (form.nodeName != \"FORM\" && form.parentNode) {\n " \
|
|
406
|
+
"form = form.parentNode;\n" \
|
|
407
|
+
"}\n" \
|
|
408
|
+
"if (!form) { throw Error('Unable to find containing form element'); }\n" \
|
|
409
|
+
"if (!form.ownerDocument) { throw Error('Unable to find owning document'); }\n" \
|
|
410
|
+
"var e = form.ownerDocument.createEvent('Event');\n" \
|
|
411
|
+
"e.initEvent('submit', true, true);\n" \
|
|
412
|
+
"if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n"
|
|
413
|
+
|
|
414
|
+
execute_script(script, Element::ELEMENT_KEY => element)
|
|
415
|
+
rescue Error::JavascriptError
|
|
416
|
+
raise Error::UnsupportedOperationError, 'To submit an element, it must be nested inside a form element'
|
|
437
417
|
end
|
|
438
418
|
|
|
439
419
|
#
|
|
@@ -445,7 +425,7 @@ module Selenium
|
|
|
445
425
|
end
|
|
446
426
|
|
|
447
427
|
def element_attribute(element, name)
|
|
448
|
-
WebDriver.logger.
|
|
428
|
+
WebDriver.logger.debug "Using script for :getAttribute of #{name}", id: :script
|
|
449
429
|
execute_atom :getAttribute, element, name
|
|
450
430
|
end
|
|
451
431
|
|
|
@@ -505,7 +485,7 @@ module Selenium
|
|
|
505
485
|
end
|
|
506
486
|
|
|
507
487
|
def element_displayed?(element)
|
|
508
|
-
WebDriver.logger.
|
|
488
|
+
WebDriver.logger.debug 'Using script for :isDisplayed', id: :script
|
|
509
489
|
execute_atom :isDisplayed, element
|
|
510
490
|
end
|
|
511
491
|
|
|
@@ -521,7 +501,7 @@ module Selenium
|
|
|
521
501
|
Element.new self, element_id_from(execute(:get_active_element))
|
|
522
502
|
end
|
|
523
503
|
|
|
524
|
-
|
|
504
|
+
alias switch_to_active_element active_element
|
|
525
505
|
|
|
526
506
|
def find_element_by(how, what, parent_ref = [])
|
|
527
507
|
how, what = convert_locator(how, what)
|
|
@@ -564,6 +544,39 @@ module Selenium
|
|
|
564
544
|
ShadowRoot.new self, shadow_root_id_from(id)
|
|
565
545
|
end
|
|
566
546
|
|
|
547
|
+
#
|
|
548
|
+
# virtual-authenticator
|
|
549
|
+
#
|
|
550
|
+
|
|
551
|
+
def add_virtual_authenticator(options)
|
|
552
|
+
authenticator_id = execute :add_virtual_authenticator, {}, options.as_json
|
|
553
|
+
VirtualAuthenticator.new(self, authenticator_id, options)
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def remove_virtual_authenticator(id)
|
|
557
|
+
execute :remove_virtual_authenticator, {authenticatorId: id}
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def add_credential(credential, id)
|
|
561
|
+
execute :add_credential, {authenticatorId: id}, credential
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
def credentials(authenticator_id)
|
|
565
|
+
execute :get_credentials, {authenticatorId: authenticator_id}
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def remove_credential(credential_id, authenticator_id)
|
|
569
|
+
execute :remove_credential, {credentialId: credential_id, authenticatorId: authenticator_id}
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def remove_all_credentials(authenticator_id)
|
|
573
|
+
execute :remove_all_credentials, {authenticatorId: authenticator_id}
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def user_verified(verified, authenticator_id)
|
|
577
|
+
execute :set_user_verified, {authenticatorId: authenticator_id}, {isUserVerified: verified}
|
|
578
|
+
end
|
|
579
|
+
|
|
567
580
|
private
|
|
568
581
|
|
|
569
582
|
#
|
|
@@ -584,7 +597,7 @@ module Selenium
|
|
|
584
597
|
raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
|
|
585
598
|
end
|
|
586
599
|
|
|
587
|
-
WebDriver.logger.
|
|
600
|
+
WebDriver.logger.debug("-> #{verb.to_s.upcase} #{path}", id: :command)
|
|
588
601
|
http.call(verb, path, command_hash)['value']
|
|
589
602
|
end
|
|
590
603
|
|
|
@@ -639,8 +652,6 @@ module Selenium
|
|
|
639
652
|
when 'name'
|
|
640
653
|
how = 'css selector'
|
|
641
654
|
what = "*[name='#{escape_css(what.to_s)}']"
|
|
642
|
-
when 'tag name'
|
|
643
|
-
how = 'css selector'
|
|
644
655
|
end
|
|
645
656
|
|
|
646
657
|
if what.is_a?(Hash)
|
|
@@ -653,7 +664,7 @@ module Selenium
|
|
|
653
664
|
[how, what]
|
|
654
665
|
end
|
|
655
666
|
|
|
656
|
-
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])
|
|
667
|
+
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/
|
|
657
668
|
UNICODE_CODE_POINT = 30
|
|
658
669
|
|
|
659
670
|
# Escapes invalid characters in CSS selector.
|
|
@@ -20,14 +20,12 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module Remote
|
|
23
|
-
|
|
24
23
|
#
|
|
25
24
|
# Specification of the desired and/or actual capabilities of the browser that the
|
|
26
25
|
# server is being asked to create.
|
|
27
26
|
#
|
|
28
27
|
|
|
29
28
|
class Capabilities
|
|
30
|
-
|
|
31
29
|
KNOWN = [
|
|
32
30
|
:browser_name,
|
|
33
31
|
:browser_version,
|
|
@@ -55,60 +53,11 @@ module Selenium
|
|
|
55
53
|
end
|
|
56
54
|
end
|
|
57
55
|
|
|
58
|
-
#
|
|
59
|
-
# Backward compatibility
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
alias_method :version, :browser_version
|
|
63
|
-
alias_method :version=, :browser_version=
|
|
64
|
-
alias_method :platform, :platform_name
|
|
65
|
-
alias_method :platform=, :platform_name=
|
|
66
|
-
|
|
67
56
|
#
|
|
68
57
|
# Convenience methods for the common choices.
|
|
69
58
|
#
|
|
70
59
|
|
|
71
60
|
class << self
|
|
72
|
-
def chrome(opts = {})
|
|
73
|
-
new({
|
|
74
|
-
browser_name: 'chrome'
|
|
75
|
-
}.merge(opts))
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def edge(opts = {})
|
|
79
|
-
new({
|
|
80
|
-
browser_name: 'MicrosoftEdge'
|
|
81
|
-
}.merge(opts))
|
|
82
|
-
end
|
|
83
|
-
alias_method :microsoftedge, :edge
|
|
84
|
-
|
|
85
|
-
def firefox(opts = {})
|
|
86
|
-
new({
|
|
87
|
-
browser_name: 'firefox'
|
|
88
|
-
}.merge(opts))
|
|
89
|
-
end
|
|
90
|
-
alias_method :ff, :firefox
|
|
91
|
-
|
|
92
|
-
def safari(opts = {})
|
|
93
|
-
new({
|
|
94
|
-
browser_name: Selenium::WebDriver::Safari.technology_preview? ? "Safari Technology Preview" : 'safari'
|
|
95
|
-
}.merge(opts))
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def htmlunit(opts = {})
|
|
99
|
-
new({
|
|
100
|
-
browser_name: 'htmlunit'
|
|
101
|
-
}.merge(opts))
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def internet_explorer(opts = {})
|
|
105
|
-
new({
|
|
106
|
-
browser_name: 'internet explorer',
|
|
107
|
-
platform_name: :windows
|
|
108
|
-
}.merge(opts))
|
|
109
|
-
end
|
|
110
|
-
alias_method :ie, :internet_explorer
|
|
111
|
-
|
|
112
61
|
def always_match(capabilities)
|
|
113
62
|
new(always_match: capabilities)
|
|
114
63
|
end
|
|
@@ -134,7 +83,8 @@ module Selenium
|
|
|
134
83
|
|
|
135
84
|
# Remote Server Specific
|
|
136
85
|
if data.key?('webdriver.remote.sessionid')
|
|
137
|
-
caps[:remote_session_id] =
|
|
86
|
+
caps[:remote_session_id] =
|
|
87
|
+
data.delete('webdriver.remote.sessionid')
|
|
138
88
|
end
|
|
139
89
|
|
|
140
90
|
KNOWN.each do |cap|
|
|
@@ -269,7 +219,7 @@ module Selenium
|
|
|
269
219
|
as_json == other.as_json
|
|
270
220
|
end
|
|
271
221
|
|
|
272
|
-
|
|
222
|
+
alias eql? ==
|
|
273
223
|
|
|
274
224
|
protected
|
|
275
225
|
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module Remote
|
|
23
|
-
|
|
24
23
|
#
|
|
25
24
|
# Driver implementation for remote server.
|
|
26
25
|
# @api private
|
|
@@ -29,20 +28,18 @@ module Selenium
|
|
|
29
28
|
class Driver < WebDriver::Driver
|
|
30
29
|
include DriverExtensions::UploadsFiles
|
|
31
30
|
include DriverExtensions::HasSessionId
|
|
32
|
-
include DriverExtensions::
|
|
31
|
+
include DriverExtensions::HasFileDownloads
|
|
33
32
|
|
|
34
|
-
def initialize(
|
|
35
|
-
|
|
36
|
-
if desired_capabilities.is_a?(Symbol)
|
|
37
|
-
unless Remote::Capabilities.respond_to?(desired_capabilities)
|
|
38
|
-
raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
|
|
39
|
-
end
|
|
33
|
+
def initialize(capabilities: nil, options: nil, service: nil, url: nil, **opts)
|
|
34
|
+
raise ArgumentError, "Can not set :service object on #{self.class}" if service
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
super
|
|
36
|
+
url ||= "http://#{Platform.localhost}:4444/wd/hub"
|
|
37
|
+
caps = process_options(options, capabilities)
|
|
38
|
+
super(caps: caps, url: url, **opts)
|
|
45
39
|
@bridge.file_detector = ->((filename, *)) { File.exist?(filename) && filename.to_s }
|
|
40
|
+
command_list = @bridge.command_list
|
|
41
|
+
@bridge.extend(WebDriver::Remote::Features)
|
|
42
|
+
@bridge.add_commands(command_list)
|
|
46
43
|
end
|
|
47
44
|
|
|
48
45
|
private
|
|
@@ -52,8 +49,32 @@ module Selenium
|
|
|
52
49
|
end
|
|
53
50
|
|
|
54
51
|
def devtools_version
|
|
55
|
-
capabilities['se:cdpVersion']&.split('.')&.first
|
|
56
|
-
|
|
52
|
+
cdp_version = capabilities['se:cdpVersion']&.split('.')&.first
|
|
53
|
+
raise Error::WebDriverError, 'DevTools is not supported by the Remote Server' unless cdp_version
|
|
54
|
+
|
|
55
|
+
Integer(cdp_version)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def process_options(options, capabilities)
|
|
59
|
+
if options && capabilities
|
|
60
|
+
msg = "Don't use both :options and :capabilities when initializing #{self.class}, prefer :options"
|
|
61
|
+
raise ArgumentError, msg
|
|
62
|
+
elsif options.nil? && capabilities.nil?
|
|
63
|
+
raise ArgumentError, "#{self.class} needs :options to be set"
|
|
64
|
+
end
|
|
65
|
+
options ? options.as_json : generate_capabilities(capabilities)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def generate_capabilities(capabilities)
|
|
69
|
+
Array(capabilities).map { |cap|
|
|
70
|
+
if cap.is_a? Symbol
|
|
71
|
+
cap = WebDriver::Options.send(cap)
|
|
72
|
+
elsif !cap.respond_to? :as_json
|
|
73
|
+
msg = ":capabilities parameter only accepts objects responding to #as_json which #{cap.class} does not"
|
|
74
|
+
raise ArgumentError, msg
|
|
75
|
+
end
|
|
76
|
+
cap.as_json
|
|
77
|
+
}.inject(:merge)
|
|
57
78
|
end
|
|
58
79
|
end # Driver
|
|
59
80
|
end # Remote
|
|
@@ -0,0 +1,75 @@
|
|
|
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 Remote
|
|
23
|
+
module Features
|
|
24
|
+
REMOTE_COMMANDS = {
|
|
25
|
+
upload_file: [:post, 'session/:session_id/se/file'],
|
|
26
|
+
get_downloadable_files: [:get, 'session/:session_id/se/files'],
|
|
27
|
+
download_file: [:post, 'session/:session_id/se/files'],
|
|
28
|
+
delete_downloadable_files: [:delete, 'session/:session_id/se/files']
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
def add_commands(commands)
|
|
32
|
+
@command_list = command_list.merge(commands)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def command_list
|
|
36
|
+
@command_list ||= REMOTE_COMMANDS
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def commands(command)
|
|
40
|
+
command_list[command]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def upload(local_file)
|
|
44
|
+
unless File.file?(local_file)
|
|
45
|
+
WebDriver.logger.error("File detector only works with files. #{local_file.inspect} isn`t a file!",
|
|
46
|
+
id: :file_detector)
|
|
47
|
+
raise Error::WebDriverError, "You are trying to upload something that isn't a file."
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
execute :upload_file, {}, {file: Zipper.zip_file(local_file)}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def upload_if_necessary(keys)
|
|
54
|
+
local_files = keys.first&.split("\n")&.map { |key| @file_detector.call(Array(key)) }&.compact
|
|
55
|
+
return keys unless local_files&.any?
|
|
56
|
+
|
|
57
|
+
keys = local_files.map { |local_file| upload(local_file) }
|
|
58
|
+
Array(keys.join("\n"))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def downloadable_files
|
|
62
|
+
execute :get_downloadable_files
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def download_file(name)
|
|
66
|
+
execute :download_file, {}, {name: name}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def delete_downloadable_files
|
|
70
|
+
execute :delete_downloadable_files
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end # Remote
|
|
74
|
+
end # WebDriver
|
|
75
|
+
end # Selenium
|
|
@@ -49,8 +49,8 @@ module Selenium
|
|
|
49
49
|
payload = JSON.generate(command_hash)
|
|
50
50
|
headers['Content-Length'] = payload.bytesize.to_s if %i[post put].include?(verb)
|
|
51
51
|
|
|
52
|
-
WebDriver.logger.
|
|
53
|
-
WebDriver.logger.debug(" > #{headers.inspect}")
|
|
52
|
+
WebDriver.logger.debug(" >>> #{url} | #{payload}", id: :command)
|
|
53
|
+
WebDriver.logger.debug(" > #{headers.inspect}", id: :header)
|
|
54
54
|
elsif verb == :post
|
|
55
55
|
payload = '{}'
|
|
56
56
|
headers['Content-Length'] = '2'
|
|
@@ -75,7 +75,7 @@ module Selenium
|
|
|
75
75
|
code = code.to_i
|
|
76
76
|
body = body.to_s.strip
|
|
77
77
|
content_type = content_type.to_s
|
|
78
|
-
WebDriver.logger.
|
|
78
|
+
WebDriver.logger.debug("<- #{body}", id: :command)
|
|
79
79
|
|
|
80
80
|
if content_type.include? CONTENT_TYPE
|
|
81
81
|
raise Error::WebDriverError, "empty body: #{content_type.inspect} (#{code})\n#{body}" if body.empty?
|
|
@@ -22,7 +22,6 @@ require 'curb'
|
|
|
22
22
|
module Selenium
|
|
23
23
|
module WebDriver
|
|
24
24
|
module Remote
|
|
25
|
-
|
|
26
25
|
module Http
|
|
27
26
|
#
|
|
28
27
|
# An alternative to the default Net::HTTP client.
|
|
@@ -38,7 +37,6 @@ module Selenium
|
|
|
38
37
|
#
|
|
39
38
|
|
|
40
39
|
class Curb < Common
|
|
41
|
-
|
|
42
40
|
def quit_errors
|
|
43
41
|
[Curl::Err::RecvError] + super
|
|
44
42
|
end
|
|
@@ -85,7 +83,7 @@ module Selenium
|
|
|
85
83
|
c.max_redirects = MAX_REDIRECTS
|
|
86
84
|
c.follow_location = true
|
|
87
85
|
c.timeout = @timeout if @timeout
|
|
88
|
-
c.verbose = WebDriver.logger.
|
|
86
|
+
c.verbose = WebDriver.logger.debug?
|
|
89
87
|
|
|
90
88
|
c
|
|
91
89
|
end
|
|
@@ -16,8 +16,6 @@
|
|
|
16
16
|
# KIND, either express or implied. See the License for the
|
|
17
17
|
# specific language governing permissions and limitations
|
|
18
18
|
# under the License.
|
|
19
|
-
|
|
20
|
-
require 'net/https'
|
|
21
19
|
require 'ipaddr'
|
|
22
20
|
|
|
23
21
|
module Selenium
|
|
@@ -75,9 +73,10 @@ module Selenium
|
|
|
75
73
|
begin
|
|
76
74
|
request = new_request_for(verb, url, headers, payload)
|
|
77
75
|
response = response_for(request)
|
|
78
|
-
rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EADDRINUSE
|
|
79
|
-
# a retry is sometimes needed
|
|
80
|
-
# run out of ephemeral ports
|
|
76
|
+
rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EADDRINUSE, Errno::EADDRNOTAVAIL
|
|
77
|
+
# a retry is sometimes needed:
|
|
78
|
+
# on Windows XP where we may quickly run out of ephemeral ports
|
|
79
|
+
# when the port becomes temporarily unavailable
|
|
81
80
|
#
|
|
82
81
|
# A more robust solution is bumping the MaxUserPort setting
|
|
83
82
|
# as described here:
|
|
@@ -85,13 +84,6 @@ module Selenium
|
|
|
85
84
|
# http://msdn.microsoft.com/en-us/library/aa560610%28v=bts.20%29.aspx
|
|
86
85
|
raise if retries >= MAX_RETRIES
|
|
87
86
|
|
|
88
|
-
retries += 1
|
|
89
|
-
sleep 2
|
|
90
|
-
retry
|
|
91
|
-
rescue Errno::EADDRNOTAVAIL => e
|
|
92
|
-
# a retry is sometimes needed when the port becomes temporarily unavailable
|
|
93
|
-
raise if retries >= MAX_RETRIES
|
|
94
|
-
|
|
95
87
|
retries += 1
|
|
96
88
|
sleep 2
|
|
97
89
|
retry
|
|
@@ -102,10 +94,12 @@ module Selenium
|
|
|
102
94
|
end
|
|
103
95
|
|
|
104
96
|
if response.is_a? Net::HTTPRedirection
|
|
97
|
+
WebDriver.logger.debug("Redirect to #{response['Location']}; times: #{redirects}")
|
|
105
98
|
raise Error::WebDriverError, 'too many redirects' if redirects >= MAX_REDIRECTS
|
|
106
99
|
|
|
107
100
|
request(:get, URI.parse(response['Location']), DEFAULT_HEADERS.dup, nil, redirects + 1)
|
|
108
101
|
else
|
|
102
|
+
WebDriver.logger.debug(" <<< #{response.instance_variable_get(:@header).inspect}", id: :header)
|
|
109
103
|
create_response response.code, response.body, response.content_type
|
|
110
104
|
end
|
|
111
105
|
end
|
|
@@ -142,8 +136,8 @@ module Selenium
|
|
|
142
136
|
|
|
143
137
|
def proxy
|
|
144
138
|
@proxy ||= begin
|
|
145
|
-
proxy = ENV
|
|
146
|
-
no_proxy = ENV
|
|
139
|
+
proxy = ENV.fetch('http_proxy', nil) || ENV.fetch('HTTP_PROXY', nil)
|
|
140
|
+
no_proxy = ENV.fetch('no_proxy', nil) || ENV.fetch('NO_PROXY', nil)
|
|
147
141
|
|
|
148
142
|
if proxy
|
|
149
143
|
proxy = "http://#{proxy}" unless proxy.start_with?('http://')
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module Remote
|
|
23
|
-
|
|
24
23
|
#
|
|
25
24
|
# @api private
|
|
26
25
|
#
|
|
@@ -74,7 +73,7 @@ module Selenium
|
|
|
74
73
|
end
|
|
75
74
|
|
|
76
75
|
def backtrace_from_remote(server_trace)
|
|
77
|
-
server_trace.
|
|
76
|
+
server_trace.filter_map do |frame|
|
|
78
77
|
next unless frame.is_a?(Hash)
|
|
79
78
|
|
|
80
79
|
file = frame['fileName']
|
|
@@ -87,7 +86,7 @@ module Selenium
|
|
|
87
86
|
meth = 'unknown' if meth.nil? || meth.empty?
|
|
88
87
|
|
|
89
88
|
"[remote server] #{file}:#{line}:in `#{meth}'"
|
|
90
|
-
|
|
89
|
+
end
|
|
91
90
|
end
|
|
92
91
|
|
|
93
92
|
def process_error
|
|
@@ -23,11 +23,11 @@ require 'selenium/webdriver/remote/server_error'
|
|
|
23
23
|
module Selenium
|
|
24
24
|
module WebDriver
|
|
25
25
|
module Remote
|
|
26
|
+
autoload :Features, 'selenium/webdriver/remote/features'
|
|
26
27
|
autoload :Bridge, 'selenium/webdriver/remote/bridge'
|
|
27
28
|
autoload :Driver, 'selenium/webdriver/remote/driver'
|
|
28
29
|
autoload :Response, 'selenium/webdriver/remote/response'
|
|
29
30
|
autoload :Capabilities, 'selenium/webdriver/remote/capabilities'
|
|
30
|
-
autoload :COMMANDS, 'selenium/webdriver/remote/commands'
|
|
31
31
|
|
|
32
32
|
module Http
|
|
33
33
|
autoload :Common, 'selenium/webdriver/remote/http/common'
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module Safari
|
|
23
|
-
|
|
24
23
|
#
|
|
25
24
|
# Driver implementation for Safari.
|
|
26
25
|
# @api private
|
|
@@ -31,6 +30,13 @@ module Selenium
|
|
|
31
30
|
DriverExtensions::HasApplePermissions,
|
|
32
31
|
DriverExtensions::HasWebStorage].freeze
|
|
33
32
|
|
|
33
|
+
include LocalDriver
|
|
34
|
+
|
|
35
|
+
def initialize(options: nil, service: nil, url: nil, **opts)
|
|
36
|
+
caps, url = initialize_local_driver(options, service, url)
|
|
37
|
+
super(caps: caps, url: url, **opts)
|
|
38
|
+
end
|
|
39
|
+
|
|
34
40
|
def browser
|
|
35
41
|
:safari
|
|
36
42
|
end
|
|
@@ -21,7 +21,6 @@ module Selenium
|
|
|
21
21
|
module WebDriver
|
|
22
22
|
module Safari
|
|
23
23
|
module Features
|
|
24
|
-
|
|
25
24
|
# https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/WebDriverEndpointDoc/Commands/Commands.html
|
|
26
25
|
SAFARI_COMMANDS = {
|
|
27
26
|
get_permissions: [:get, 'session/:session_id/apple/permissions'],
|
|
@@ -29,8 +28,12 @@ module Selenium
|
|
|
29
28
|
attach_debugger: [:post, 'session/:session_id/apple/attach_debugger']
|
|
30
29
|
}.freeze
|
|
31
30
|
|
|
31
|
+
def command_list
|
|
32
|
+
SAFARI_COMMANDS.merge(self.class::COMMANDS)
|
|
33
|
+
end
|
|
34
|
+
|
|
32
35
|
def commands(command)
|
|
33
|
-
|
|
36
|
+
command_list[command]
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
def permissions
|
|
@@ -44,7 +47,6 @@ module Selenium
|
|
|
44
47
|
def attach_debugger
|
|
45
48
|
execute :attach_debugger, {}, {}
|
|
46
49
|
end
|
|
47
|
-
|
|
48
50
|
end # Bridge
|
|
49
51
|
end # Safari
|
|
50
52
|
end # WebDriver
|