selenium-webdriver 3.0.0.beta4.0 → 4.0.0.alpha5
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 +7 -0
- data/CHANGES +679 -0
- data/Gemfile +2 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/selenium-webdriver.rb +2 -2
- data/lib/selenium/server.rb +23 -16
- data/lib/selenium/webdriver.rb +43 -25
- data/lib/selenium/webdriver/atoms.rb +20 -1
- data/lib/selenium/webdriver/atoms/findElements.js +122 -0
- data/lib/selenium/webdriver/atoms/getAttribute.js +84 -11
- data/lib/selenium/webdriver/atoms/isDisplayed.js +100 -0
- data/lib/selenium/webdriver/chrome.rb +15 -20
- data/lib/selenium/webdriver/chrome/bridge.rb +29 -66
- data/lib/selenium/webdriver/{edge/service.rb → chrome/driver.rb} +19 -22
- data/lib/selenium/webdriver/chrome/options.rb +221 -0
- data/lib/selenium/webdriver/chrome/profile.rb +6 -7
- data/lib/selenium/webdriver/chrome/service.rb +20 -20
- data/lib/selenium/webdriver/common.rb +19 -11
- data/lib/selenium/webdriver/common/action_builder.rb +98 -246
- data/lib/selenium/webdriver/common/alert.rb +2 -7
- data/lib/selenium/webdriver/common/driver.rb +89 -51
- data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +45 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +50 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_input_devices.rb → has_debugger.rb} +12 -25
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +38 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +3 -5
- data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +51 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +3 -3
- data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +51 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +2 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +2 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +2 -2
- data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +4 -4
- data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +9 -3
- data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +3 -5
- data/lib/selenium/webdriver/common/element.rb +32 -10
- data/lib/selenium/webdriver/common/error.rb +103 -121
- data/lib/selenium/webdriver/common/file_reaper.rb +3 -5
- data/lib/selenium/webdriver/common/html5/local_storage.rb +2 -2
- data/lib/selenium/webdriver/common/html5/session_storage.rb +2 -2
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +3 -2
- data/lib/selenium/webdriver/common/interactions/input_device.rb +54 -0
- data/lib/selenium/webdriver/common/interactions/interaction.rb +53 -0
- data/lib/selenium/webdriver/{phantomjs.rb → common/interactions/interactions.rb} +17 -20
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +145 -0
- data/lib/selenium/webdriver/common/interactions/key_input.rb +66 -0
- data/lib/selenium/webdriver/common/interactions/none_input.rb +36 -0
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +363 -0
- data/lib/selenium/webdriver/common/interactions/pointer_input.rb +139 -0
- data/lib/selenium/webdriver/common/keys.rb +37 -15
- data/lib/selenium/webdriver/common/log_entry.rb +4 -4
- data/lib/selenium/webdriver/common/logger.rb +147 -0
- data/lib/selenium/webdriver/common/logs.rb +2 -2
- data/lib/selenium/webdriver/common/manager.rb +177 -0
- data/lib/selenium/webdriver/common/navigation.rb +2 -2
- data/lib/selenium/webdriver/common/options.rb +47 -105
- data/lib/selenium/webdriver/common/platform.rb +44 -38
- data/lib/selenium/webdriver/common/port_prober.rb +8 -19
- data/lib/selenium/webdriver/common/profile_helper.rb +13 -5
- data/lib/selenium/webdriver/common/proxy.rb +14 -8
- data/lib/selenium/webdriver/common/search_context.rb +16 -20
- data/lib/selenium/webdriver/common/service.rb +115 -30
- data/lib/selenium/webdriver/common/socket_lock.rb +12 -7
- data/lib/selenium/webdriver/common/socket_poller.rb +29 -22
- data/lib/selenium/webdriver/common/target_locator.rb +6 -6
- data/lib/selenium/webdriver/common/timeouts.rb +2 -2
- data/lib/selenium/webdriver/common/wait.rb +14 -8
- data/lib/selenium/webdriver/common/window.rb +38 -2
- data/lib/selenium/webdriver/common/zipper.rb +3 -5
- data/lib/selenium/webdriver/edge.rb +33 -20
- data/lib/selenium/webdriver/edge_chrome/bridge.rb +30 -0
- data/lib/selenium/webdriver/edge_chrome/driver.rb +38 -0
- data/lib/selenium/webdriver/{common/driver_extensions/has_touch_screen.rb → edge_chrome/options.rb} +10 -12
- data/lib/selenium/webdriver/edge_chrome/profile.rb +33 -0
- data/lib/selenium/webdriver/edge_chrome/service.rb +36 -0
- data/lib/selenium/webdriver/edge_html/driver.rb +39 -0
- data/lib/selenium/webdriver/edge_html/options.rb +91 -0
- data/lib/selenium/webdriver/edge_html/service.rb +47 -0
- data/lib/selenium/webdriver/firefox.rb +24 -29
- data/lib/selenium/webdriver/firefox/bridge.rb +15 -39
- data/lib/selenium/webdriver/firefox/{util.rb → driver.rb} +13 -19
- data/lib/selenium/webdriver/firefox/extension.rb +28 -8
- data/lib/selenium/webdriver/firefox/options.rb +162 -0
- data/lib/selenium/webdriver/firefox/profile.rb +23 -82
- data/lib/selenium/webdriver/firefox/profiles_ini.rb +5 -5
- data/lib/selenium/webdriver/firefox/service.rb +18 -37
- data/lib/selenium/webdriver/ie.rb +13 -19
- data/lib/selenium/webdriver/ie/driver.rb +40 -0
- data/lib/selenium/webdriver/ie/options.rb +118 -0
- data/lib/selenium/webdriver/ie/service.rb +20 -20
- data/lib/selenium/webdriver/remote.rb +15 -17
- data/lib/selenium/webdriver/remote/bridge.rb +281 -300
- data/lib/selenium/webdriver/remote/capabilities.rb +102 -83
- data/lib/selenium/webdriver/remote/commands.rb +132 -192
- data/lib/selenium/webdriver/remote/driver.rb +52 -0
- data/lib/selenium/webdriver/remote/http/common.rb +23 -13
- data/lib/selenium/webdriver/remote/http/curb.rb +10 -7
- data/lib/selenium/webdriver/remote/http/default.rb +52 -32
- data/lib/selenium/webdriver/remote/http/persistent.rb +8 -4
- data/lib/selenium/webdriver/remote/response.rb +32 -35
- data/lib/selenium/webdriver/remote/server_error.rb +2 -2
- data/lib/selenium/webdriver/safari.rb +24 -22
- data/lib/selenium/webdriver/safari/bridge.rb +21 -21
- data/lib/selenium/webdriver/safari/driver.rb +41 -0
- data/lib/selenium/webdriver/safari/options.rb +66 -0
- data/lib/selenium/webdriver/safari/service.rb +8 -24
- data/lib/selenium/webdriver/support.rb +3 -2
- data/lib/selenium/webdriver/support/abstract_event_listener.rb +2 -2
- data/lib/selenium/webdriver/support/block_event_listener.rb +3 -3
- data/lib/selenium/webdriver/support/color.rb +13 -13
- data/lib/selenium/webdriver/support/escaper.rb +2 -2
- data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
- data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
- data/lib/selenium/webdriver/support/select.rb +20 -21
- data/lib/selenium/webdriver/version.rb +24 -0
- data/selenium-webdriver.gemspec +31 -17
- metadata +331 -248
- data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
- data/lib/selenium/webdriver/common/keyboard.rb +0 -69
- data/lib/selenium/webdriver/common/mouse.rb +0 -88
- data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -81
- data/lib/selenium/webdriver/common/touch_screen.rb +0 -121
- data/lib/selenium/webdriver/common/w3c_error.rb +0 -191
- data/lib/selenium/webdriver/edge/bridge.rb +0 -76
- data/lib/selenium/webdriver/edge/legacy_support.rb +0 -117
- data/lib/selenium/webdriver/firefox/binary.rb +0 -186
- data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
- data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
- data/lib/selenium/webdriver/firefox/launcher.rb +0 -114
- data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
- data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
- data/lib/selenium/webdriver/firefox/w3c_bridge.rb +0 -79
- data/lib/selenium/webdriver/ie/bridge.rb +0 -76
- data/lib/selenium/webdriver/phantomjs/bridge.rb +0 -65
- data/lib/selenium/webdriver/phantomjs/service.rb +0 -66
- data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -682
- data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -228
- data/lib/selenium/webdriver/remote/w3c_commands.rb +0 -135
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
3
|
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
4
|
# or more contributor license agreements. See the NOTICE file
|
|
5
5
|
# distributed with this work for additional information
|
|
@@ -20,46 +20,27 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module Firefox
|
|
23
|
-
#
|
|
24
|
-
# @api private
|
|
25
|
-
#
|
|
26
|
-
|
|
27
23
|
class Service < WebDriver::Service
|
|
28
24
|
DEFAULT_PORT = 4444
|
|
25
|
+
EXECUTABLE = 'geckodriver'
|
|
26
|
+
MISSING_TEXT = <<~ERROR
|
|
27
|
+
Unable to find Mozilla geckodriver. Please download the server from
|
|
28
|
+
https://github.com/mozilla/geckodriver/releases and place it somewhere on your PATH.
|
|
29
|
+
More info at https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/WebDriver.
|
|
30
|
+
ERROR
|
|
31
|
+
SHUTDOWN_SUPPORTED = false
|
|
29
32
|
|
|
30
33
|
private
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
@process.start
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def stop_process
|
|
48
|
-
super
|
|
49
|
-
return unless Platform.windows? && !$DEBUG
|
|
50
|
-
begin
|
|
51
|
-
@process.io.close
|
|
52
|
-
rescue
|
|
53
|
-
nil
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def stop_server
|
|
58
|
-
connect_to_server { |http| http.head('/shutdown') }
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def cannot_connect_error_text
|
|
62
|
-
"unable to connect to Mozilla geckodriver #{@host}:#{@port}"
|
|
35
|
+
# Note: This processing is deprecated
|
|
36
|
+
def extract_service_args(driver_opts)
|
|
37
|
+
driver_args = super
|
|
38
|
+
driver_opts = driver_opts.dup
|
|
39
|
+
driver_args << "--binary=#{driver_opts[:binary]}" if driver_opts.key?(:binary)
|
|
40
|
+
driver_args << "--log=#{driver_opts[:log]}" if driver_opts.key?(:log)
|
|
41
|
+
driver_args << "--marionette-port=#{driver_opts[:marionette_port]}" if driver_opts.key?(:marionette_port)
|
|
42
|
+
driver_args << "--host=#{driver_opts[:host]}" if driver_opts.key?(:host)
|
|
43
|
+
driver_args
|
|
63
44
|
end
|
|
64
45
|
end # Service
|
|
65
46
|
end # Firefox
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
3
|
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
4
|
# or more contributor license agreements. See the NOTICE file
|
|
5
5
|
# distributed with this work for additional information
|
|
@@ -17,31 +17,25 @@
|
|
|
17
17
|
# specific language governing permissions and limitations
|
|
18
18
|
# under the License.
|
|
19
19
|
|
|
20
|
-
require 'selenium/webdriver/ie/bridge'
|
|
21
|
-
require 'selenium/webdriver/ie/service'
|
|
22
|
-
|
|
23
20
|
module Selenium
|
|
24
21
|
module WebDriver
|
|
25
22
|
module IE
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
somewhere on your PATH. More info at https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver.
|
|
30
|
-
ERROR
|
|
23
|
+
autoload :Driver, 'selenium/webdriver/ie/driver'
|
|
24
|
+
autoload :Options, 'selenium/webdriver/ie/options'
|
|
25
|
+
autoload :Service, 'selenium/webdriver/ie/service'
|
|
31
26
|
|
|
32
27
|
def self.driver_path=(path)
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
WebDriver.logger.deprecate 'Selenium::WebDriver::IE#driver_path=',
|
|
29
|
+
'Selenium::WebDriver::IE::Service#driver_path=',
|
|
30
|
+
id: :driver_path
|
|
31
|
+
Selenium::WebDriver::IE::Service.driver_path = path
|
|
35
32
|
end
|
|
36
33
|
|
|
37
34
|
def self.driver_path
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
path
|
|
44
|
-
end
|
|
35
|
+
WebDriver.logger.deprecate 'Selenium::WebDriver::IE#driver_path',
|
|
36
|
+
'Selenium::WebDriver::IE::Service#driver_path',
|
|
37
|
+
id: :driver_path
|
|
38
|
+
Selenium::WebDriver::IE::Service.driver_path
|
|
45
39
|
end
|
|
46
40
|
end # IE
|
|
47
41
|
end # WebDriver
|
|
@@ -0,0 +1,40 @@
|
|
|
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 IE
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
# Driver implementation for Internet Explorer supporting
|
|
26
|
+
# both OSS and W3C dialects of JSON wire protocol.
|
|
27
|
+
# @api private
|
|
28
|
+
#
|
|
29
|
+
|
|
30
|
+
class Driver < WebDriver::Driver
|
|
31
|
+
include DriverExtensions::HasWebStorage
|
|
32
|
+
include DriverExtensions::TakesScreenshot
|
|
33
|
+
|
|
34
|
+
def browser
|
|
35
|
+
:internet_explorer
|
|
36
|
+
end
|
|
37
|
+
end # Driver
|
|
38
|
+
end # IE
|
|
39
|
+
end # WebDriver
|
|
40
|
+
end # Selenium
|
|
@@ -0,0 +1,118 @@
|
|
|
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 IE
|
|
23
|
+
class Options < WebDriver::Options
|
|
24
|
+
KEY = 'se:ieOptions'
|
|
25
|
+
SCROLL_TOP = 0
|
|
26
|
+
SCROLL_BOTTOM = 1
|
|
27
|
+
CAPABILITIES = {
|
|
28
|
+
browser_attach_timeout: 'browserAttachTimeout',
|
|
29
|
+
element_scroll_behavior: 'elementScrollBehavior',
|
|
30
|
+
full_page_screenshot: 'ie.enableFullPageScreenshot',
|
|
31
|
+
ensure_clean_session: 'ie.ensureCleanSession',
|
|
32
|
+
file_upload_dialog_timeout: 'ie.fileUploadDialogTimeout',
|
|
33
|
+
force_create_process_api: 'ie.forceCreateProcessApi',
|
|
34
|
+
force_shell_windows_api: 'ie.forceShellWindowsApi',
|
|
35
|
+
ignore_protected_mode_settings: 'ignoreProtectedModeSettings',
|
|
36
|
+
ignore_zoom_level: 'ignoreZoomSetting',
|
|
37
|
+
initial_browser_url: 'initialBrowserUrl',
|
|
38
|
+
native_events: 'nativeEvents',
|
|
39
|
+
persistent_hover: 'enablePersistentHover',
|
|
40
|
+
require_window_focus: 'requireWindowFocus',
|
|
41
|
+
use_per_process_proxy: 'ie.usePerProcessProxy',
|
|
42
|
+
validate_cookie_document_type: 'ie.validateCookieDocumentType'
|
|
43
|
+
}.freeze
|
|
44
|
+
|
|
45
|
+
CAPABILITIES.each_key do |key|
|
|
46
|
+
define_method key do
|
|
47
|
+
@options[key]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
define_method "#{key}=" do |value|
|
|
51
|
+
@options[key] = value
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
attr_reader :args
|
|
56
|
+
|
|
57
|
+
#
|
|
58
|
+
# Create a new Options instance
|
|
59
|
+
#
|
|
60
|
+
# @example
|
|
61
|
+
# options = Selenium::WebDriver::IE::Options.new(args: ['--host=127.0.0.1'])
|
|
62
|
+
# driver = Selenium::WebDriver.for(:ie, options: options)
|
|
63
|
+
#
|
|
64
|
+
# @example
|
|
65
|
+
# options = Selenium::WebDriver::IE::Options.new
|
|
66
|
+
# options.element_scroll_behavior = Selenium::WebDriver::IE::Options::SCROLL_BOTTOM
|
|
67
|
+
# driver = Selenium::WebDriver.for(:ie, options: options)
|
|
68
|
+
#
|
|
69
|
+
# @param [Hash] opts the pre-defined options
|
|
70
|
+
# @option opts [Array<String>] args
|
|
71
|
+
# @option opts [Integer] browser_attach_timeout
|
|
72
|
+
# @option opts [Integer] element_scroll_behavior Either SCROLL_TOP or SCROLL_BOTTOM
|
|
73
|
+
# @option opts [Boolean] full_page_screenshot
|
|
74
|
+
# @option opts [Boolean] ensure_clean_session
|
|
75
|
+
# @option opts [Integer] file_upload_dialog_timeout
|
|
76
|
+
# @option opts [Boolean] force_create_process_api
|
|
77
|
+
# @option opts [Boolean] force_shell_windows_api
|
|
78
|
+
# @option opts [Boolean] ignore_protected_mode_settings
|
|
79
|
+
# @option opts [Boolean] ignore_zoom_level
|
|
80
|
+
# @option opts [String] initial_browser_url
|
|
81
|
+
# @option opts [Boolean] native_events
|
|
82
|
+
# @option opts [Boolean] persistent_hover
|
|
83
|
+
# @option opts [Boolean] require_window_focus
|
|
84
|
+
# @option opts [Boolean] use_per_process_proxy
|
|
85
|
+
# @option opts [Boolean] validate_cookie_document_type
|
|
86
|
+
#
|
|
87
|
+
|
|
88
|
+
def initialize(args: nil, **opts)
|
|
89
|
+
super(opts)
|
|
90
|
+
|
|
91
|
+
@args = (args || []).to_set
|
|
92
|
+
@options[:native_events] = true if @options[:native_events].nil?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
#
|
|
96
|
+
# Add a command-line argument to use when starting Internet Explorer.
|
|
97
|
+
#
|
|
98
|
+
# @param [String] arg The command-line argument to add
|
|
99
|
+
#
|
|
100
|
+
|
|
101
|
+
def add_argument(arg)
|
|
102
|
+
@args << arg
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
#
|
|
106
|
+
# @api private
|
|
107
|
+
#
|
|
108
|
+
|
|
109
|
+
def as_json(*)
|
|
110
|
+
options = super
|
|
111
|
+
options['ie.browserCommandLineSwitches'] = @args.to_a.join(' ') if @args.any?
|
|
112
|
+
|
|
113
|
+
{KEY => generate_as_json(options)}
|
|
114
|
+
end
|
|
115
|
+
end # Options
|
|
116
|
+
end # IE
|
|
117
|
+
end # WebDriver
|
|
118
|
+
end # Selenium
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
3
|
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
4
|
# or more contributor license agreements. See the NOTICE file
|
|
5
5
|
# distributed with this work for additional information
|
|
@@ -20,29 +20,29 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module IE
|
|
23
|
-
#
|
|
24
|
-
# @api private
|
|
25
|
-
#
|
|
26
|
-
|
|
27
23
|
class Service < WebDriver::Service
|
|
28
24
|
DEFAULT_PORT = 5555
|
|
25
|
+
EXECUTABLE = 'IEDriverServer'
|
|
26
|
+
MISSING_TEXT = <<~ERROR
|
|
27
|
+
Unable to find IEDriverServer. Please download the server from
|
|
28
|
+
http://selenium-release.storage.googleapis.com/index.html and place it somewhere on your PATH.
|
|
29
|
+
More info at https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver.
|
|
30
|
+
ERROR
|
|
31
|
+
SHUTDOWN_SUPPORTED = true
|
|
29
32
|
|
|
30
33
|
private
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def cannot_connect_error_text
|
|
45
|
-
"unable to connect to IE server #{@host}:#{@port}"
|
|
35
|
+
# Note: This processing is deprecated
|
|
36
|
+
def extract_service_args(driver_opts)
|
|
37
|
+
driver_args = super
|
|
38
|
+
driver_opts = driver_opts.dup
|
|
39
|
+
driver_args << "--log-level=#{driver_opts[:log_level].to_s.upcase}" if driver_opts.key?(:log_level)
|
|
40
|
+
driver_args << "--log-file=#{driver_opts[:log_file]}" if driver_opts.key?(:log_file)
|
|
41
|
+
driver_args << "--implementation=#{driver_opts[:implementation].to_s.upcase}" if driver_opts.key?(:implementation)
|
|
42
|
+
driver_args << "--host=#{driver_opts[:host]}" if driver_opts.key?(:host)
|
|
43
|
+
driver_args << "--extract_path=#{driver_opts[:extract_path]}" if driver_opts.key?(:extract_path)
|
|
44
|
+
driver_args << "--silent" if driver_opts[:silent] == true
|
|
45
|
+
driver_args
|
|
46
46
|
end
|
|
47
47
|
end # Server
|
|
48
48
|
end # IE
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
3
|
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
4
|
# or more contributor license agreements. See the NOTICE file
|
|
5
5
|
# distributed with this work for additional information
|
|
@@ -19,21 +19,19 @@
|
|
|
19
19
|
|
|
20
20
|
require 'uri'
|
|
21
21
|
|
|
22
|
-
require 'selenium/webdriver/remote/capabilities'
|
|
23
|
-
require 'selenium/webdriver/remote/w3c_capabilities'
|
|
24
|
-
require 'selenium/webdriver/remote/bridge'
|
|
25
|
-
require 'selenium/webdriver/remote/w3c_bridge'
|
|
26
|
-
require 'selenium/webdriver/remote/server_error'
|
|
27
|
-
require 'selenium/webdriver/remote/response'
|
|
28
|
-
require 'selenium/webdriver/remote/commands'
|
|
29
|
-
require 'selenium/webdriver/remote/w3c_commands'
|
|
30
|
-
require 'selenium/webdriver/remote/http/common'
|
|
31
|
-
require 'selenium/webdriver/remote/http/default'
|
|
32
|
-
|
|
33
22
|
module Selenium
|
|
34
23
|
module WebDriver
|
|
35
|
-
# @api private
|
|
36
24
|
module Remote
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
autoload :Bridge, 'selenium/webdriver/remote/bridge'
|
|
26
|
+
autoload :Driver, 'selenium/webdriver/remote/driver'
|
|
27
|
+
autoload :Response, 'selenium/webdriver/remote/response'
|
|
28
|
+
autoload :ServerError, 'selenium/webdriver/remote/server_error'
|
|
29
|
+
autoload :Capabilities, 'selenium/webdriver/remote/capabilities'
|
|
30
|
+
autoload :COMMANDS, 'selenium/webdriver/remote/commands'
|
|
31
|
+
module Http
|
|
32
|
+
autoload :Common, 'selenium/webdriver/remote/http/common'
|
|
33
|
+
autoload :Default, 'selenium/webdriver/remote/http/default'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
3
|
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
4
|
# or more contributor license agreements. See the NOTICE file
|
|
5
5
|
# distributed with this work for additional information
|
|
@@ -20,96 +20,43 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module Remote
|
|
23
|
-
#
|
|
24
|
-
# Low level bridge to the remote server, through which the rest of the API works.
|
|
25
|
-
#
|
|
26
|
-
# @api private
|
|
27
|
-
#
|
|
28
|
-
|
|
29
23
|
class Bridge
|
|
30
|
-
include
|
|
31
|
-
|
|
32
|
-
# TODO: constant shouldn't be modified in class
|
|
33
|
-
COMMANDS = {}
|
|
34
|
-
|
|
35
|
-
#
|
|
36
|
-
# Defines a wrapper method for a command, which ultimately calls #execute.
|
|
37
|
-
#
|
|
38
|
-
# @param name [Symbol]
|
|
39
|
-
# name of the resulting method
|
|
40
|
-
# @param url [String]
|
|
41
|
-
# a URL template, which can include some arguments, much like the definitions on the server.
|
|
42
|
-
# the :session_id parameter is implicitly handled, but the remainder will become required method arguments.
|
|
43
|
-
# @param verb [Symbol]
|
|
44
|
-
# the appropriate http verb, such as :get, :post, or :delete
|
|
45
|
-
#
|
|
24
|
+
include Atoms
|
|
46
25
|
|
|
47
|
-
|
|
48
|
-
COMMANDS[name] = [verb, url.freeze]
|
|
49
|
-
end
|
|
26
|
+
PORT = 4444
|
|
50
27
|
|
|
51
28
|
attr_accessor :context, :http, :file_detector
|
|
52
29
|
attr_reader :capabilities
|
|
53
30
|
|
|
54
31
|
#
|
|
55
|
-
# Initializes the bridge with the given server URL
|
|
56
|
-
#
|
|
57
|
-
# @param
|
|
58
|
-
# @
|
|
59
|
-
# @param desired_capabilities [Capabilities] an instance of Remote::Capabilities describing the capabilities you want
|
|
32
|
+
# Initializes the bridge with the given server URL
|
|
33
|
+
# @param [String, URI] :url url for the remote server
|
|
34
|
+
# @param [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
|
|
35
|
+
# @api private
|
|
60
36
|
#
|
|
61
37
|
|
|
62
|
-
def initialize(
|
|
63
|
-
opts = opts.dup
|
|
64
|
-
|
|
65
|
-
port = opts.delete(:port) || 4444
|
|
66
|
-
http_client = opts.delete(:http_client) { Http::Default.new }
|
|
67
|
-
desired_capabilities = opts.delete(:desired_capabilities) { Capabilities.firefox }
|
|
68
|
-
url = opts.delete(:url) { "http://#{Platform.localhost}:#{port}/wd/hub" }
|
|
69
|
-
|
|
70
|
-
unless opts.empty?
|
|
71
|
-
raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
if desired_capabilities.is_a?(Symbol)
|
|
75
|
-
unless Capabilities.respond_to?(desired_capabilities)
|
|
76
|
-
raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
desired_capabilities = Capabilities.send(desired_capabilities)
|
|
80
|
-
end
|
|
81
|
-
|
|
38
|
+
def initialize(http_client: nil, url:)
|
|
82
39
|
uri = url.is_a?(URI) ? url : URI.parse(url)
|
|
83
|
-
uri.path += '/' unless
|
|
84
|
-
|
|
85
|
-
http_client.server_url = uri
|
|
86
|
-
|
|
87
|
-
@http = http_client
|
|
88
|
-
@capabilities = create_session(desired_capabilities)
|
|
40
|
+
uri.path += '/' unless %r{\/$}.match?(uri.path)
|
|
89
41
|
|
|
42
|
+
@http = http_client || Http::Default.new
|
|
43
|
+
@http.server_url = uri
|
|
90
44
|
@file_detector = nil
|
|
91
45
|
end
|
|
92
46
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
47
|
+
#
|
|
48
|
+
# Creates session.
|
|
49
|
+
#
|
|
50
|
+
|
|
51
|
+
def create_session(desired_capabilities, options = nil)
|
|
52
|
+
response = execute(:new_session, {}, merged_capabilities(desired_capabilities, options))
|
|
53
|
+
|
|
54
|
+
@session_id = response['sessionId']
|
|
55
|
+
capabilities = response['capabilities']
|
|
99
56
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
DriverExtensions::UploadsFiles,
|
|
104
|
-
DriverExtensions::TakesScreenshot,
|
|
105
|
-
DriverExtensions::HasSessionId,
|
|
106
|
-
DriverExtensions::Rotatable,
|
|
107
|
-
DriverExtensions::HasTouchScreen,
|
|
108
|
-
DriverExtensions::HasLocation,
|
|
109
|
-
DriverExtensions::HasNetworkConnection,
|
|
110
|
-
DriverExtensions::HasRemoteStatus,
|
|
111
|
-
DriverExtensions::HasWebStorage
|
|
112
|
-
]
|
|
57
|
+
raise Error::WebDriverError, 'no sessionId in returned payload' unless @session_id
|
|
58
|
+
|
|
59
|
+
@capabilities = Capabilities.json_create(capabilities)
|
|
113
60
|
end
|
|
114
61
|
|
|
115
62
|
#
|
|
@@ -120,12 +67,11 @@ module Selenium
|
|
|
120
67
|
@session_id || raise(Error::WebDriverError, 'no current session exists')
|
|
121
68
|
end
|
|
122
69
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
raise Error::WebDriverError, 'no sessionId in returned payload'
|
|
70
|
+
def browser
|
|
71
|
+
@browser ||= begin
|
|
72
|
+
name = @capabilities.browser_name
|
|
73
|
+
name ? name.tr(' ', '_').to_sym : 'unknown'
|
|
74
|
+
end
|
|
129
75
|
end
|
|
130
76
|
|
|
131
77
|
def status
|
|
@@ -136,20 +82,17 @@ module Selenium
|
|
|
136
82
|
execute :get, {}, {url: url}
|
|
137
83
|
end
|
|
138
84
|
|
|
139
|
-
def session_capabilities
|
|
140
|
-
Capabilities.json_create execute(:getCapabilities)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
85
|
def implicit_wait_timeout=(milliseconds)
|
|
144
|
-
|
|
86
|
+
timeout('implicit', milliseconds)
|
|
145
87
|
end
|
|
146
88
|
|
|
147
89
|
def script_timeout=(milliseconds)
|
|
148
|
-
|
|
90
|
+
timeout('script', milliseconds)
|
|
149
91
|
end
|
|
150
92
|
|
|
151
93
|
def timeout(type, milliseconds)
|
|
152
|
-
|
|
94
|
+
type = 'pageLoad' if type == 'page load'
|
|
95
|
+
execute :set_timeout, {}, {type => milliseconds}
|
|
153
96
|
end
|
|
154
97
|
|
|
155
98
|
#
|
|
@@ -157,23 +100,19 @@ module Selenium
|
|
|
157
100
|
#
|
|
158
101
|
|
|
159
102
|
def accept_alert
|
|
160
|
-
execute :
|
|
103
|
+
execute :accept_alert
|
|
161
104
|
end
|
|
162
105
|
|
|
163
106
|
def dismiss_alert
|
|
164
|
-
execute :
|
|
107
|
+
execute :dismiss_alert
|
|
165
108
|
end
|
|
166
109
|
|
|
167
110
|
def alert=(keys)
|
|
168
|
-
execute :
|
|
111
|
+
execute :send_alert_text, {}, {value: keys.split(//), text: keys}
|
|
169
112
|
end
|
|
170
113
|
|
|
171
114
|
def alert_text
|
|
172
|
-
execute :
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
def authentication(credentials)
|
|
176
|
-
execute :setAuthentication, {}, credentials
|
|
115
|
+
execute :get_alert_text
|
|
177
116
|
end
|
|
178
117
|
|
|
179
118
|
#
|
|
@@ -181,51 +120,68 @@ module Selenium
|
|
|
181
120
|
#
|
|
182
121
|
|
|
183
122
|
def go_back
|
|
184
|
-
execute :
|
|
123
|
+
execute :back
|
|
185
124
|
end
|
|
186
125
|
|
|
187
126
|
def go_forward
|
|
188
|
-
execute :
|
|
127
|
+
execute :forward
|
|
189
128
|
end
|
|
190
129
|
|
|
191
130
|
def url
|
|
192
|
-
execute :
|
|
131
|
+
execute :get_current_url
|
|
193
132
|
end
|
|
194
133
|
|
|
195
134
|
def title
|
|
196
|
-
execute :
|
|
135
|
+
execute :get_title
|
|
197
136
|
end
|
|
198
137
|
|
|
199
138
|
def page_source
|
|
200
|
-
|
|
139
|
+
execute_script('var source = document.documentElement.outerHTML;' \
|
|
140
|
+
'if (!source) { source = new XMLSerializer().serializeToString(document); }' \
|
|
141
|
+
'return source;')
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
#
|
|
145
|
+
# Create a new top-level browsing context
|
|
146
|
+
# https://w3c.github.io/webdriver/#new-window
|
|
147
|
+
# @param type [String] Supports two values: 'tab' and 'window'.
|
|
148
|
+
# Use 'tab' if you'd like the new window to share an OS-level window
|
|
149
|
+
# with the current browsing context.
|
|
150
|
+
# Use 'window' otherwise
|
|
151
|
+
# @return [Hash] Containing 'handle' with the value of the window handle
|
|
152
|
+
# and 'type' with the value of the created window type
|
|
153
|
+
#
|
|
154
|
+
def new_window(type)
|
|
155
|
+
execute :new_window, {}, {type: type}
|
|
201
156
|
end
|
|
202
157
|
|
|
203
158
|
def switch_to_window(name)
|
|
204
|
-
execute :
|
|
159
|
+
execute :switch_to_window, {}, {handle: name}
|
|
205
160
|
end
|
|
206
161
|
|
|
207
162
|
def switch_to_frame(id)
|
|
208
|
-
|
|
163
|
+
id = find_element_by('id', id) if id.is_a? String
|
|
164
|
+
execute :switch_to_frame, {}, {id: id}
|
|
209
165
|
end
|
|
210
166
|
|
|
211
167
|
def switch_to_parent_frame
|
|
212
|
-
execute :
|
|
168
|
+
execute :switch_to_parent_frame
|
|
213
169
|
end
|
|
214
170
|
|
|
215
171
|
def switch_to_default_content
|
|
216
|
-
switch_to_frame
|
|
172
|
+
switch_to_frame nil
|
|
217
173
|
end
|
|
218
174
|
|
|
219
175
|
QUIT_ERRORS = [IOError].freeze
|
|
220
176
|
|
|
221
177
|
def quit
|
|
222
|
-
execute :
|
|
178
|
+
execute :delete_session
|
|
223
179
|
http.close
|
|
224
180
|
rescue *QUIT_ERRORS
|
|
225
181
|
end
|
|
226
182
|
|
|
227
183
|
def close
|
|
228
|
-
execute :
|
|
184
|
+
execute :close_window
|
|
229
185
|
end
|
|
230
186
|
|
|
231
187
|
def refresh
|
|
@@ -237,42 +193,62 @@ module Selenium
|
|
|
237
193
|
#
|
|
238
194
|
|
|
239
195
|
def window_handles
|
|
240
|
-
execute :
|
|
196
|
+
execute :get_window_handles
|
|
241
197
|
end
|
|
242
198
|
|
|
243
199
|
def window_handle
|
|
244
|
-
execute :
|
|
200
|
+
execute :get_window_handle
|
|
245
201
|
end
|
|
246
202
|
|
|
247
203
|
def resize_window(width, height, handle = :current)
|
|
248
|
-
|
|
249
|
-
{width: width,
|
|
250
|
-
height: height}
|
|
251
|
-
end
|
|
204
|
+
raise Error::WebDriverError, 'Switch to desired window before changing its size' unless handle == :current
|
|
252
205
|
|
|
253
|
-
|
|
254
|
-
execute :maximizeWindow, window_handle: handle
|
|
206
|
+
set_window_rect(width: width, height: height)
|
|
255
207
|
end
|
|
256
208
|
|
|
257
209
|
def window_size(handle = :current)
|
|
258
|
-
|
|
210
|
+
raise Error::UnsupportedOperationError, 'Switch to desired window before getting its size' unless handle == :current
|
|
259
211
|
|
|
212
|
+
data = execute :get_window_rect
|
|
260
213
|
Dimension.new data['width'], data['height']
|
|
261
214
|
end
|
|
262
215
|
|
|
263
|
-
def
|
|
264
|
-
execute :
|
|
265
|
-
{x: x, y: y}
|
|
216
|
+
def minimize_window
|
|
217
|
+
execute :minimize_window
|
|
266
218
|
end
|
|
267
219
|
|
|
268
|
-
def
|
|
269
|
-
|
|
220
|
+
def maximize_window(handle = :current)
|
|
221
|
+
raise Error::UnsupportedOperationError, 'Switch to desired window before changing its size' unless handle == :current
|
|
222
|
+
|
|
223
|
+
execute :maximize_window
|
|
224
|
+
end
|
|
270
225
|
|
|
226
|
+
def full_screen_window
|
|
227
|
+
execute :fullscreen_window
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def reposition_window(x, y)
|
|
231
|
+
set_window_rect(x: x, y: y)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def window_position
|
|
235
|
+
data = execute :get_window_rect
|
|
271
236
|
Point.new data['x'], data['y']
|
|
272
237
|
end
|
|
273
238
|
|
|
239
|
+
def set_window_rect(x: nil, y: nil, width: nil, height: nil)
|
|
240
|
+
params = {x: x, y: y, width: width, height: height}
|
|
241
|
+
params.update(params) { |_k, v| Integer(v) unless v.nil? }
|
|
242
|
+
execute :set_window_rect, {}, params
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def window_rect
|
|
246
|
+
data = execute :get_window_rect
|
|
247
|
+
Rectangle.new data['x'], data['y'], data['width'], data['height']
|
|
248
|
+
end
|
|
249
|
+
|
|
274
250
|
def screenshot
|
|
275
|
-
execute :
|
|
251
|
+
execute :take_screenshot
|
|
276
252
|
end
|
|
277
253
|
|
|
278
254
|
#
|
|
@@ -281,68 +257,66 @@ module Selenium
|
|
|
281
257
|
|
|
282
258
|
def local_storage_item(key, value = nil)
|
|
283
259
|
if value
|
|
284
|
-
|
|
260
|
+
execute_script("localStorage.setItem('#{key}', '#{value}')")
|
|
285
261
|
else
|
|
286
|
-
|
|
262
|
+
execute_script("return localStorage.getItem('#{key}')")
|
|
287
263
|
end
|
|
288
264
|
end
|
|
289
265
|
|
|
290
266
|
def remove_local_storage_item(key)
|
|
291
|
-
|
|
267
|
+
execute_script("localStorage.removeItem('#{key}')")
|
|
292
268
|
end
|
|
293
269
|
|
|
294
270
|
def local_storage_keys
|
|
295
|
-
|
|
271
|
+
execute_script('return Object.keys(localStorage)')
|
|
296
272
|
end
|
|
297
273
|
|
|
298
274
|
def clear_local_storage
|
|
299
|
-
|
|
275
|
+
execute_script('localStorage.clear()')
|
|
300
276
|
end
|
|
301
277
|
|
|
302
278
|
def local_storage_size
|
|
303
|
-
|
|
279
|
+
execute_script('return localStorage.length')
|
|
304
280
|
end
|
|
305
281
|
|
|
306
282
|
def session_storage_item(key, value = nil)
|
|
307
283
|
if value
|
|
308
|
-
|
|
284
|
+
execute_script("sessionStorage.setItem('#{key}', '#{value}')")
|
|
309
285
|
else
|
|
310
|
-
|
|
286
|
+
execute_script("return sessionStorage.getItem('#{key}')")
|
|
311
287
|
end
|
|
312
288
|
end
|
|
313
289
|
|
|
314
290
|
def remove_session_storage_item(key)
|
|
315
|
-
|
|
291
|
+
execute_script("sessionStorage.removeItem('#{key}')")
|
|
316
292
|
end
|
|
317
293
|
|
|
318
294
|
def session_storage_keys
|
|
319
|
-
|
|
295
|
+
execute_script('return Object.keys(sessionStorage)')
|
|
320
296
|
end
|
|
321
297
|
|
|
322
298
|
def clear_session_storage
|
|
323
|
-
|
|
299
|
+
execute_script('sessionStorage.clear()')
|
|
324
300
|
end
|
|
325
301
|
|
|
326
302
|
def session_storage_size
|
|
327
|
-
|
|
303
|
+
execute_script('return sessionStorage.length')
|
|
328
304
|
end
|
|
329
305
|
|
|
330
306
|
def location
|
|
331
|
-
|
|
332
|
-
Location.new obj['latitude'], obj['longitude'], obj['altitude']
|
|
307
|
+
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting location'
|
|
333
308
|
end
|
|
334
309
|
|
|
335
|
-
def set_location(
|
|
336
|
-
|
|
337
|
-
execute :setLocation, {}, {location: loc}
|
|
310
|
+
def set_location(_lat, _lon, _alt)
|
|
311
|
+
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting location'
|
|
338
312
|
end
|
|
339
313
|
|
|
340
314
|
def network_connection
|
|
341
|
-
|
|
315
|
+
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting network connection'
|
|
342
316
|
end
|
|
343
317
|
|
|
344
|
-
def network_connection=(
|
|
345
|
-
|
|
318
|
+
def network_connection=(_type)
|
|
319
|
+
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting network connection'
|
|
346
320
|
end
|
|
347
321
|
|
|
348
322
|
#
|
|
@@ -350,16 +324,12 @@ module Selenium
|
|
|
350
324
|
#
|
|
351
325
|
|
|
352
326
|
def execute_script(script, *args)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
result = execute :executeScript, {}, {script: script, args: args}
|
|
327
|
+
result = execute :execute_script, {}, {script: script, args: args}
|
|
356
328
|
unwrap_script_result result
|
|
357
329
|
end
|
|
358
330
|
|
|
359
331
|
def execute_async_script(script, *args)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
result = execute :executeAsyncScript, {}, {script: script, args: args}
|
|
332
|
+
result = execute :execute_async_script, {}, {script: script, args: args}
|
|
363
333
|
unwrap_script_result result
|
|
364
334
|
end
|
|
365
335
|
|
|
@@ -367,166 +337,103 @@ module Selenium
|
|
|
367
337
|
# cookies
|
|
368
338
|
#
|
|
369
339
|
|
|
340
|
+
def manage
|
|
341
|
+
@manage ||= WebDriver::Manager.new(self)
|
|
342
|
+
end
|
|
343
|
+
|
|
370
344
|
def add_cookie(cookie)
|
|
371
|
-
execute :
|
|
345
|
+
execute :add_cookie, {}, {cookie: cookie}
|
|
372
346
|
end
|
|
373
347
|
|
|
374
348
|
def delete_cookie(name)
|
|
375
|
-
execute :
|
|
349
|
+
execute :delete_cookie, name: name
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def cookie(name)
|
|
353
|
+
execute :get_cookie, name: name
|
|
376
354
|
end
|
|
377
355
|
|
|
378
356
|
def cookies
|
|
379
|
-
execute :
|
|
357
|
+
execute :get_all_cookies
|
|
380
358
|
end
|
|
381
359
|
|
|
382
360
|
def delete_all_cookies
|
|
383
|
-
execute :
|
|
361
|
+
execute :delete_all_cookies
|
|
384
362
|
end
|
|
385
363
|
|
|
386
364
|
#
|
|
387
365
|
# actions
|
|
388
366
|
#
|
|
389
367
|
|
|
390
|
-
def
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
execute :click, {}, {button: 0}
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
def double_click
|
|
399
|
-
execute :doubleClick
|
|
368
|
+
def action(async = false)
|
|
369
|
+
ActionBuilder.new self,
|
|
370
|
+
Interactions.pointer(:mouse, name: 'mouse'),
|
|
371
|
+
Interactions.key('keyboard'),
|
|
372
|
+
async
|
|
400
373
|
end
|
|
374
|
+
alias_method :actions, :action
|
|
401
375
|
|
|
402
|
-
def
|
|
403
|
-
|
|
376
|
+
def mouse
|
|
377
|
+
raise Error::UnsupportedOperationError, '#mouse is no longer supported, use #action instead'
|
|
404
378
|
end
|
|
405
379
|
|
|
406
|
-
def
|
|
407
|
-
|
|
380
|
+
def keyboard
|
|
381
|
+
raise Error::UnsupportedOperationError, '#keyboard is no longer supported, use #action instead'
|
|
408
382
|
end
|
|
409
383
|
|
|
410
|
-
def
|
|
411
|
-
execute :
|
|
384
|
+
def send_actions(data)
|
|
385
|
+
execute :actions, {}, {actions: data}
|
|
412
386
|
end
|
|
413
387
|
|
|
414
|
-
def
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if x && y
|
|
418
|
-
params[:xoffset] = x
|
|
419
|
-
params[:yoffset] = y
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
execute :mouseMoveTo, {}, params
|
|
388
|
+
def release_actions
|
|
389
|
+
execute :release_actions
|
|
423
390
|
end
|
|
424
391
|
|
|
425
|
-
def
|
|
426
|
-
execute :
|
|
392
|
+
def click_element(element)
|
|
393
|
+
execute :element_click, id: element
|
|
427
394
|
end
|
|
428
395
|
|
|
429
396
|
def send_keys_to_element(element, keys)
|
|
397
|
+
# TODO: rework file detectors before Selenium 4.0
|
|
430
398
|
if @file_detector
|
|
431
|
-
|
|
432
|
-
|
|
399
|
+
local_files = keys.first.split("\n").map { |key| @file_detector.call(Array(key)) }.compact
|
|
400
|
+
if local_files.any?
|
|
401
|
+
keys = local_files.map { |local_file| upload(local_file) }
|
|
402
|
+
keys = Array(keys.join("\n"))
|
|
403
|
+
end
|
|
433
404
|
end
|
|
434
405
|
|
|
435
|
-
|
|
406
|
+
# Keep .split(//) for backward compatibility for now
|
|
407
|
+
text = keys.join('')
|
|
408
|
+
execute :element_send_keys, {id: element}, {value: text.split(//), text: text}
|
|
436
409
|
end
|
|
437
410
|
|
|
438
411
|
def upload(local_file)
|
|
439
412
|
unless File.file?(local_file)
|
|
440
|
-
|
|
413
|
+
WebDriver.logger.debug("File detector only works with files. #{local_file.inspect} isn`t a file!")
|
|
414
|
+
raise Error::WebDriverError, "You are trying to work with something that isn't a file."
|
|
441
415
|
end
|
|
442
416
|
|
|
443
|
-
execute :
|
|
417
|
+
execute :upload_file, {}, {file: Zipper.zip_file(local_file)}
|
|
444
418
|
end
|
|
445
419
|
|
|
446
420
|
def clear_element(element)
|
|
447
|
-
execute :
|
|
421
|
+
execute :element_clear, id: element
|
|
448
422
|
end
|
|
449
423
|
|
|
450
424
|
def submit_element(element)
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
execute :dragElement, {id: element}, {x: right_by, y: down_by}
|
|
456
|
-
end
|
|
457
|
-
|
|
458
|
-
def touch_single_tap(element)
|
|
459
|
-
execute :touchSingleTap, {}, {element: element}
|
|
460
|
-
end
|
|
461
|
-
|
|
462
|
-
def touch_double_tap(element)
|
|
463
|
-
execute :touchDoubleTap, {}, {element: element}
|
|
464
|
-
end
|
|
465
|
-
|
|
466
|
-
def touch_long_press(element)
|
|
467
|
-
execute :touchLongPress, {}, {element: element}
|
|
468
|
-
end
|
|
469
|
-
|
|
470
|
-
def touch_down(x, y)
|
|
471
|
-
execute :touchDown, {}, {x: x, y: y}
|
|
472
|
-
end
|
|
473
|
-
|
|
474
|
-
def touch_up(x, y)
|
|
475
|
-
execute :touchUp, {}, {x: x, y: y}
|
|
476
|
-
end
|
|
477
|
-
|
|
478
|
-
def touch_move(x, y)
|
|
479
|
-
execute :touchMove, {}, {x: x, y: y}
|
|
480
|
-
end
|
|
481
|
-
|
|
482
|
-
def touch_scroll(element, x, y)
|
|
483
|
-
if element
|
|
484
|
-
execute :touchScroll, {}, {element: element,
|
|
485
|
-
xoffset: x,
|
|
486
|
-
yoffset: y}
|
|
487
|
-
else
|
|
488
|
-
execute :touchScroll, {}, {xoffset: x, yoffset: y}
|
|
489
|
-
end
|
|
490
|
-
end
|
|
491
|
-
|
|
492
|
-
def touch_flick(xspeed, yspeed)
|
|
493
|
-
execute :touchFlick, {}, {xspeed: xspeed, yspeed: yspeed}
|
|
494
|
-
end
|
|
495
|
-
|
|
496
|
-
def touch_element_flick(element, right_by, down_by, speed)
|
|
497
|
-
execute :touchFlick, {}, {element: element,
|
|
498
|
-
xoffset: right_by,
|
|
499
|
-
yoffset: down_by,
|
|
500
|
-
speed: speed}
|
|
425
|
+
form = find_element_by('xpath', "./ancestor-or-self::form", element)
|
|
426
|
+
execute_script("var e = arguments[0].ownerDocument.createEvent('Event');" \
|
|
427
|
+
"e.initEvent('submit', true, true);" \
|
|
428
|
+
'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }', form.as_json)
|
|
501
429
|
end
|
|
502
430
|
|
|
503
431
|
def screen_orientation=(orientation)
|
|
504
|
-
execute :
|
|
432
|
+
execute :set_screen_orientation, {}, {orientation: orientation}
|
|
505
433
|
end
|
|
506
434
|
|
|
507
435
|
def screen_orientation
|
|
508
|
-
execute :
|
|
509
|
-
end
|
|
510
|
-
|
|
511
|
-
#
|
|
512
|
-
# logs
|
|
513
|
-
#
|
|
514
|
-
|
|
515
|
-
def available_log_types
|
|
516
|
-
types = execute :getAvailableLogTypes
|
|
517
|
-
Array(types).map(&:to_sym)
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
def log(type)
|
|
521
|
-
data = execute :getLog, {}, {type: type.to_s}
|
|
522
|
-
|
|
523
|
-
Array(data).map do |l|
|
|
524
|
-
begin
|
|
525
|
-
LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
|
|
526
|
-
rescue KeyError
|
|
527
|
-
next
|
|
528
|
-
end
|
|
529
|
-
end
|
|
436
|
+
execute :get_screen_orientation
|
|
530
437
|
end
|
|
531
438
|
|
|
532
439
|
#
|
|
@@ -534,53 +441,64 @@ module Selenium
|
|
|
534
441
|
#
|
|
535
442
|
|
|
536
443
|
def element_tag_name(element)
|
|
537
|
-
execute :
|
|
444
|
+
execute :get_element_tag_name, id: element
|
|
538
445
|
end
|
|
539
446
|
|
|
540
447
|
def element_attribute(element, name)
|
|
541
|
-
|
|
448
|
+
WebDriver.logger.info "Using script for :getAttribute of #{name}"
|
|
449
|
+
execute_atom :getAttribute, element, name
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def element_property(element, name)
|
|
453
|
+
execute :get_element_property, id: element.ref, name: name
|
|
542
454
|
end
|
|
543
455
|
|
|
544
456
|
def element_value(element)
|
|
545
|
-
|
|
457
|
+
element_property element, 'value'
|
|
546
458
|
end
|
|
547
459
|
|
|
548
460
|
def element_text(element)
|
|
549
|
-
execute :
|
|
461
|
+
execute :get_element_text, id: element
|
|
550
462
|
end
|
|
551
463
|
|
|
552
464
|
def element_location(element)
|
|
553
|
-
data = execute :
|
|
465
|
+
data = execute :get_element_rect, id: element
|
|
554
466
|
|
|
555
467
|
Point.new data['x'], data['y']
|
|
556
468
|
end
|
|
557
469
|
|
|
558
|
-
def
|
|
559
|
-
data = execute :
|
|
470
|
+
def element_rect(element)
|
|
471
|
+
data = execute :get_element_rect, id: element
|
|
560
472
|
|
|
561
|
-
|
|
473
|
+
Rectangle.new data['x'], data['y'], data['width'], data['height']
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def element_location_once_scrolled_into_view(element)
|
|
477
|
+
send_keys_to_element(element, [''])
|
|
478
|
+
element_location(element)
|
|
562
479
|
end
|
|
563
480
|
|
|
564
481
|
def element_size(element)
|
|
565
|
-
data = execute :
|
|
482
|
+
data = execute :get_element_rect, id: element
|
|
566
483
|
|
|
567
484
|
Dimension.new data['width'], data['height']
|
|
568
485
|
end
|
|
569
486
|
|
|
570
487
|
def element_enabled?(element)
|
|
571
|
-
execute :
|
|
488
|
+
execute :is_element_enabled, id: element
|
|
572
489
|
end
|
|
573
490
|
|
|
574
491
|
def element_selected?(element)
|
|
575
|
-
execute :
|
|
492
|
+
execute :is_element_selected, id: element
|
|
576
493
|
end
|
|
577
494
|
|
|
578
495
|
def element_displayed?(element)
|
|
579
|
-
|
|
496
|
+
WebDriver.logger.info 'Using script for :isDisplayed'
|
|
497
|
+
execute_atom :isDisplayed, element
|
|
580
498
|
end
|
|
581
499
|
|
|
582
500
|
def element_value_of_css_property(element, prop)
|
|
583
|
-
execute :
|
|
501
|
+
execute :get_element_css_value, id: element, property_name: prop
|
|
584
502
|
end
|
|
585
503
|
|
|
586
504
|
#
|
|
@@ -588,26 +506,34 @@ module Selenium
|
|
|
588
506
|
#
|
|
589
507
|
|
|
590
508
|
def active_element
|
|
591
|
-
Element.new self, element_id_from(execute(:
|
|
509
|
+
Element.new self, element_id_from(execute(:get_active_element))
|
|
592
510
|
end
|
|
593
511
|
|
|
594
512
|
alias_method :switch_to_active_element, :active_element
|
|
595
513
|
|
|
596
514
|
def find_element_by(how, what, parent = nil)
|
|
515
|
+
how, what = convert_locator(how, what)
|
|
516
|
+
|
|
517
|
+
return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
|
|
518
|
+
|
|
597
519
|
id = if parent
|
|
598
|
-
execute :
|
|
520
|
+
execute :find_child_element, {id: parent}, {using: how, value: what.to_s}
|
|
599
521
|
else
|
|
600
|
-
execute :
|
|
522
|
+
execute :find_element, {}, {using: how, value: what.to_s}
|
|
601
523
|
end
|
|
602
524
|
|
|
603
525
|
Element.new self, element_id_from(id)
|
|
604
526
|
end
|
|
605
527
|
|
|
606
528
|
def find_elements_by(how, what, parent = nil)
|
|
529
|
+
how, what = convert_locator(how, what)
|
|
530
|
+
|
|
531
|
+
return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
|
|
532
|
+
|
|
607
533
|
ids = if parent
|
|
608
|
-
execute :
|
|
534
|
+
execute :find_child_elements, {id: parent}, {using: how, value: what.to_s}
|
|
609
535
|
else
|
|
610
|
-
execute :
|
|
536
|
+
execute :find_elements, {}, {using: how, value: what.to_s}
|
|
611
537
|
end
|
|
612
538
|
|
|
613
539
|
ids.map { |id| Element.new self, element_id_from(id) }
|
|
@@ -615,33 +541,17 @@ module Selenium
|
|
|
615
541
|
|
|
616
542
|
private
|
|
617
543
|
|
|
618
|
-
def assert_javascript_enabled
|
|
619
|
-
return if capabilities.javascript_enabled?
|
|
620
|
-
raise Error::UnsupportedOperationError, 'underlying webdriver instance does not support javascript'
|
|
621
|
-
end
|
|
622
|
-
|
|
623
|
-
#
|
|
624
|
-
# executes a command on the remote server.
|
|
625
|
-
#
|
|
626
|
-
#
|
|
627
|
-
# Returns the 'value' of the returned payload
|
|
628
|
-
#
|
|
629
|
-
|
|
630
|
-
def execute(*args)
|
|
631
|
-
raw_execute(*args)['value']
|
|
632
|
-
end
|
|
633
|
-
|
|
634
544
|
#
|
|
635
545
|
# executes a command on the remote server.
|
|
636
546
|
#
|
|
637
547
|
# @return [WebDriver::Remote::Response]
|
|
638
548
|
#
|
|
639
549
|
|
|
640
|
-
def
|
|
641
|
-
verb, path =
|
|
550
|
+
def execute(command, opts = {}, command_hash = nil)
|
|
551
|
+
verb, path = commands(command) || raise(ArgumentError, "unknown command: #{command.inspect}")
|
|
642
552
|
path = path.dup
|
|
643
553
|
|
|
644
|
-
path[':session_id'] =
|
|
554
|
+
path[':session_id'] = session_id if path.include?(':session_id')
|
|
645
555
|
|
|
646
556
|
begin
|
|
647
557
|
opts.each { |key, value| path[key.inspect] = escaper.escape(value.to_s) }
|
|
@@ -649,12 +559,83 @@ module Selenium
|
|
|
649
559
|
raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
|
|
650
560
|
end
|
|
651
561
|
|
|
652
|
-
|
|
653
|
-
http.call
|
|
562
|
+
WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
|
|
563
|
+
http.call(verb, path, command_hash)['value']
|
|
654
564
|
end
|
|
655
565
|
|
|
656
566
|
def escaper
|
|
657
|
-
@escaper ||= defined?(URI::Parser) ? URI::
|
|
567
|
+
@escaper ||= defined?(URI::Parser) ? URI::DEFAULT_PARSER : URI
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def commands(command)
|
|
571
|
+
COMMANDS[command]
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def merged_capabilities(capabilities, options = nil)
|
|
575
|
+
capabilities.merge!(options.as_json) if options
|
|
576
|
+
|
|
577
|
+
{
|
|
578
|
+
capabilities: {
|
|
579
|
+
firstMatch: [capabilities]
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def unwrap_script_result(arg)
|
|
585
|
+
case arg
|
|
586
|
+
when Array
|
|
587
|
+
arg.map { |e| unwrap_script_result(e) }
|
|
588
|
+
when Hash
|
|
589
|
+
element_id = element_id_from(arg)
|
|
590
|
+
return Element.new(self, element_id) if element_id
|
|
591
|
+
|
|
592
|
+
arg.each { |k, v| arg[k] = unwrap_script_result(v) }
|
|
593
|
+
else
|
|
594
|
+
arg
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
def element_id_from(id)
|
|
599
|
+
id['ELEMENT'] || id['element-6066-11e4-a52e-4f735466cecf']
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
def convert_locator(how, what)
|
|
603
|
+
how = SearchContext::FINDERS[how.to_sym] || how
|
|
604
|
+
|
|
605
|
+
case how
|
|
606
|
+
when 'class name'
|
|
607
|
+
how = 'css selector'
|
|
608
|
+
what = ".#{escape_css(what.to_s)}"
|
|
609
|
+
when 'id'
|
|
610
|
+
how = 'css selector'
|
|
611
|
+
what = "##{escape_css(what.to_s)}"
|
|
612
|
+
when 'name'
|
|
613
|
+
how = 'css selector'
|
|
614
|
+
what = "*[name='#{escape_css(what.to_s)}']"
|
|
615
|
+
when 'tag name'
|
|
616
|
+
how = 'css selector'
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
if what.is_a?(Hash)
|
|
620
|
+
what = what.each_with_object({}) do |(h, w), hash|
|
|
621
|
+
h, w = convert_locator(h.to_s, w)
|
|
622
|
+
hash[h] = w
|
|
623
|
+
end
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
[how, what]
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]\(\)])/.freeze
|
|
630
|
+
UNICODE_CODE_POINT = 30
|
|
631
|
+
|
|
632
|
+
# Escapes invalid characters in CSS selector.
|
|
633
|
+
# @see https://mathiasbynens.be/notes/css-escapes
|
|
634
|
+
def escape_css(string)
|
|
635
|
+
string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
|
|
636
|
+
string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..-1]}" if string[0]&.match?(/[[:digit:]]/)
|
|
637
|
+
|
|
638
|
+
string
|
|
658
639
|
end
|
|
659
640
|
end # Bridge
|
|
660
641
|
end # Remote
|