selenium-webdriver 2.53.4 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +363 -10
  3. data/LICENSE +1 -1
  4. data/README.md +2 -3
  5. data/lib/selenium-webdriver.rb +0 -2
  6. data/lib/selenium/server.rb +69 -70
  7. data/lib/selenium/webdriver.rb +32 -23
  8. data/lib/selenium/webdriver/atoms.rb +18 -0
  9. data/lib/selenium/webdriver/atoms/getAttribute.js +8 -0
  10. data/lib/selenium/webdriver/chrome.rb +8 -6
  11. data/lib/selenium/webdriver/chrome/driver.rb +112 -0
  12. data/lib/selenium/webdriver/chrome/options.rb +168 -0
  13. data/lib/selenium/webdriver/chrome/profile.rb +17 -17
  14. data/lib/selenium/webdriver/chrome/service.rb +22 -89
  15. data/lib/selenium/webdriver/common.rb +13 -6
  16. data/lib/selenium/webdriver/common/action_builder.rb +49 -57
  17. data/lib/selenium/webdriver/common/alert.rb +5 -15
  18. data/lib/selenium/webdriver/common/bridge_helper.rb +10 -17
  19. data/lib/selenium/webdriver/common/driver.rb +53 -68
  20. data/lib/selenium/webdriver/common/driver_extensions/{has_input_devices.rb → has_addons.rb} +13 -23
  21. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +4 -8
  22. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +4 -7
  23. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +0 -4
  24. data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +0 -4
  25. data/lib/selenium/webdriver/common/driver_extensions/has_touch_screen.rb +1 -5
  26. data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +0 -5
  27. data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +4 -9
  28. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +7 -7
  29. data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +2 -7
  30. data/lib/selenium/webdriver/common/element.rb +57 -39
  31. data/lib/selenium/webdriver/common/error.rb +204 -106
  32. data/lib/selenium/webdriver/common/file_reaper.rb +3 -11
  33. data/lib/selenium/webdriver/common/html5/local_storage.rb +6 -10
  34. data/lib/selenium/webdriver/common/html5/session_storage.rb +6 -10
  35. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +7 -18
  36. data/lib/selenium/webdriver/{safari/options.rb → common/interactions/input_device.rb} +20 -31
  37. data/lib/selenium/webdriver/common/interactions/interaction.rb +50 -0
  38. data/lib/selenium/webdriver/{safari/browser.rb → common/interactions/interactions.rb} +16 -15
  39. data/lib/selenium/webdriver/common/interactions/key_actions.rb +143 -0
  40. data/lib/selenium/webdriver/common/interactions/key_input.rb +62 -0
  41. data/lib/selenium/webdriver/{android.rb → common/interactions/none_input.rb} +11 -6
  42. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +353 -0
  43. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +132 -0
  44. data/lib/selenium/webdriver/common/keyboard.rb +7 -14
  45. data/lib/selenium/webdriver/common/keys.rb +99 -82
  46. data/lib/selenium/webdriver/common/log_entry.rb +3 -6
  47. data/lib/selenium/webdriver/common/logger.rb +140 -0
  48. data/lib/selenium/webdriver/common/logs.rb +2 -6
  49. data/lib/selenium/webdriver/common/mouse.rb +9 -14
  50. data/lib/selenium/webdriver/common/navigation.rb +2 -6
  51. data/lib/selenium/webdriver/common/options.rb +20 -23
  52. data/lib/selenium/webdriver/common/platform.rb +70 -97
  53. data/lib/selenium/webdriver/common/port_prober.rb +3 -4
  54. data/lib/selenium/webdriver/common/profile_helper.rb +6 -11
  55. data/lib/selenium/webdriver/common/proxy.rb +58 -72
  56. data/lib/selenium/webdriver/common/search_context.rb +22 -29
  57. data/lib/selenium/webdriver/common/service.rb +161 -0
  58. data/lib/selenium/webdriver/common/socket_lock.rb +6 -14
  59. data/lib/selenium/webdriver/common/socket_poller.rb +5 -12
  60. data/lib/selenium/webdriver/common/target_locator.rb +11 -15
  61. data/lib/selenium/webdriver/common/timeouts.rb +4 -8
  62. data/lib/selenium/webdriver/common/touch_action_builder.rb +2 -6
  63. data/lib/selenium/webdriver/common/touch_screen.rb +19 -23
  64. data/lib/selenium/webdriver/common/w3c_action_builder.rb +209 -0
  65. data/lib/selenium/webdriver/{phantomjs.rb → common/w3c_options.rb} +16 -14
  66. data/lib/selenium/webdriver/common/wait.rb +6 -13
  67. data/lib/selenium/webdriver/common/window.rb +48 -17
  68. data/lib/selenium/webdriver/common/zipper.rb +6 -10
  69. data/lib/selenium/webdriver/edge.rb +5 -12
  70. data/lib/selenium/webdriver/edge/bridge.rb +32 -63
  71. data/lib/selenium/webdriver/edge/driver.rb +73 -0
  72. data/lib/selenium/webdriver/edge/service.rb +18 -87
  73. data/lib/selenium/webdriver/firefox.rb +20 -11
  74. data/lib/selenium/webdriver/firefox/binary.rb +40 -56
  75. data/lib/selenium/webdriver/firefox/driver.rb +48 -0
  76. data/lib/selenium/webdriver/firefox/extension.rb +18 -8
  77. data/lib/selenium/webdriver/firefox/extension/prefs.json +3 -11
  78. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  79. data/lib/selenium/webdriver/firefox/launcher.rb +13 -22
  80. data/lib/selenium/webdriver/firefox/legacy/driver.rb +79 -0
  81. data/lib/selenium/webdriver/{iphone.rb → firefox/marionette/bridge.rb} +25 -6
  82. data/lib/selenium/webdriver/firefox/marionette/driver.rb +96 -0
  83. data/lib/selenium/webdriver/firefox/options.rb +149 -0
  84. data/lib/selenium/webdriver/firefox/profile.rb +46 -46
  85. data/lib/selenium/webdriver/firefox/profiles_ini.rb +8 -18
  86. data/lib/selenium/webdriver/firefox/service.rb +23 -83
  87. data/lib/selenium/webdriver/firefox/util.rb +0 -4
  88. data/lib/selenium/webdriver/ie.rb +4 -8
  89. data/lib/selenium/webdriver/ie/driver.rb +90 -0
  90. data/lib/selenium/webdriver/ie/options.rb +136 -0
  91. data/lib/selenium/webdriver/ie/service.rb +58 -0
  92. data/lib/selenium/webdriver/remote.rb +8 -16
  93. data/lib/selenium/webdriver/remote/bridge.rb +96 -565
  94. data/lib/selenium/webdriver/remote/capabilities.rb +76 -94
  95. data/lib/selenium/webdriver/remote/driver.rb +49 -0
  96. data/lib/selenium/webdriver/remote/http/common.rb +22 -20
  97. data/lib/selenium/webdriver/remote/http/curb.rb +9 -12
  98. data/lib/selenium/webdriver/remote/http/default.rb +54 -41
  99. data/lib/selenium/webdriver/remote/http/persistent.rb +9 -8
  100. data/lib/selenium/webdriver/remote/oss/bridge.rb +586 -0
  101. data/lib/selenium/webdriver/remote/oss/commands.rb +221 -0
  102. data/lib/selenium/webdriver/remote/response.rb +39 -27
  103. data/lib/selenium/webdriver/remote/server_error.rb +1 -5
  104. data/lib/selenium/webdriver/remote/w3c/bridge.rb +573 -0
  105. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +290 -0
  106. data/lib/selenium/webdriver/remote/w3c/commands.rb +148 -0
  107. data/lib/selenium/webdriver/safari.rb +20 -29
  108. data/lib/selenium/webdriver/{firefox/w3c_bridge.rb → safari/driver.rb} +21 -30
  109. data/lib/selenium/webdriver/safari/service.rb +57 -0
  110. data/lib/selenium/webdriver/support.rb +1 -2
  111. data/lib/selenium/webdriver/support/abstract_event_listener.rb +17 -4
  112. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -5
  113. data/lib/selenium/webdriver/support/color.rb +57 -42
  114. data/lib/selenium/webdriver/support/escaper.rb +41 -0
  115. data/lib/selenium/webdriver/support/event_firing_bridge.rb +36 -40
  116. data/lib/selenium/webdriver/support/select.rb +33 -86
  117. data/selenium-webdriver.gemspec +22 -25
  118. metadata +254 -261
  119. data/lib/selenium-client.rb +0 -21
  120. data/lib/selenium/client.rb +0 -57
  121. data/lib/selenium/client/base.rb +0 -151
  122. data/lib/selenium/client/driver.rb +0 -29
  123. data/lib/selenium/client/errors.rb +0 -28
  124. data/lib/selenium/client/extensions.rb +0 -132
  125. data/lib/selenium/client/idiomatic.rb +0 -507
  126. data/lib/selenium/client/javascript_expression_builder.rb +0 -135
  127. data/lib/selenium/client/javascript_frameworks/jquery.rb +0 -32
  128. data/lib/selenium/client/javascript_frameworks/prototype.rb +0 -32
  129. data/lib/selenium/client/legacy_driver.rb +0 -1722
  130. data/lib/selenium/client/protocol.rb +0 -123
  131. data/lib/selenium/client/selenium_helper.rb +0 -49
  132. data/lib/selenium/rake/server_task.rb +0 -176
  133. data/lib/selenium/webdriver/android/bridge.rb +0 -68
  134. data/lib/selenium/webdriver/chrome/bridge.rb +0 -139
  135. data/lib/selenium/webdriver/common/core_ext/base64.rb +0 -28
  136. data/lib/selenium/webdriver/common/core_ext/dir.rb +0 -61
  137. data/lib/selenium/webdriver/common/html5/location.rb +0 -19
  138. data/lib/selenium/webdriver/common/w3c_error.rb +0 -194
  139. data/lib/selenium/webdriver/edge/legacy_support.rb +0 -117
  140. data/lib/selenium/webdriver/firefox/bridge.rb +0 -89
  141. data/lib/selenium/webdriver/ie/bridge.rb +0 -88
  142. data/lib/selenium/webdriver/ie/server.rb +0 -133
  143. data/lib/selenium/webdriver/iphone/bridge.rb +0 -64
  144. data/lib/selenium/webdriver/phantomjs/bridge.rb +0 -78
  145. data/lib/selenium/webdriver/phantomjs/service.rb +0 -130
  146. data/lib/selenium/webdriver/remote/commands.rb +0 -211
  147. data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -668
  148. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -236
  149. data/lib/selenium/webdriver/remote/w3c_commands.rb +0 -132
  150. data/lib/selenium/webdriver/safari/bridge.rb +0 -135
  151. data/lib/selenium/webdriver/safari/resources/client.js +0 -7255
  152. data/lib/selenium/webdriver/safari/server.rb +0 -187
@@ -0,0 +1,90 @@
1
+ # Licensed to the Software Freedom Conservancy (SFC) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The SFC licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ module Selenium
19
+ module WebDriver
20
+ module IE
21
+
22
+ #
23
+ # Driver implementation for Internet Explorer supporting
24
+ # both OSS and W3C dialects of JSON wire protocol.
25
+ # @api private
26
+ #
27
+
28
+ class Driver < WebDriver::Driver
29
+ include DriverExtensions::TakesScreenshot
30
+
31
+ def initialize(opts = {})
32
+ opts[:desired_capabilities] = create_capabilities(opts)
33
+
34
+ unless opts.key?(:url)
35
+ driver_path = opts.delete(:driver_path) || IE.driver_path
36
+ driver_opts = opts.delete(:driver_opts) || {}
37
+ port = opts.delete(:port) || Service::DEFAULT_PORT
38
+
39
+ @service = Service.new(driver_path, port, driver_opts)
40
+ @service.start
41
+ opts[:url] = @service.uri
42
+ end
43
+
44
+ listener = opts.delete(:listener)
45
+ @bridge = Remote::Bridge.handshake(opts)
46
+ super(@bridge, listener: listener)
47
+ end
48
+
49
+ def browser
50
+ :internet_explorer
51
+ end
52
+
53
+ def quit
54
+ super
55
+ ensure
56
+ @service.stop if @service
57
+ end
58
+
59
+ private
60
+
61
+ def create_capabilities(opts)
62
+ caps = opts.delete(:desired_capabilities) { Remote::Capabilities.internet_explorer }
63
+ options = opts.delete(:options) { Options.new }
64
+
65
+ if opts.delete(:introduce_flakiness_by_ignoring_security_domains)
66
+ WebDriver.logger.deprecate ':introduce_flakiness_by_ignoring_security_domains',
67
+ 'Selenium::WebDriver::IE::Options#ignore_protected_mode_settings='
68
+ options.ignore_protected_mode_settings = true
69
+ end
70
+
71
+ native_events = opts.delete(:native_events)
72
+ unless native_events.nil?
73
+ WebDriver.logger.deprecate ':native_events', 'Selenium::WebDriver::IE::Options#native_events='
74
+ options.native_events = native_events
75
+ end
76
+
77
+ # Backward compatibility with older IEDriverServer versions
78
+ caps[:ignore_protected_mode_settings] = options.ignore_protected_mode_settings
79
+ caps[:native_events] = options.native_events
80
+
81
+ options = options.as_json
82
+ caps.merge!(options) unless options.empty?
83
+
84
+ caps
85
+ end
86
+
87
+ end # Driver
88
+ end # IE
89
+ end # WebDriver
90
+ end # Selenium
@@ -0,0 +1,136 @@
1
+ # Licensed to the Software Freedom Conservancy (SFC) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The SFC licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ module Selenium
19
+ module WebDriver
20
+ module IE
21
+ class Options
22
+ KEY = 'se:ieOptions'.freeze
23
+ SCROLL_TOP = 0
24
+ SCROLL_BOTTOM = 1
25
+ CAPABILITIES = {
26
+ browser_attach_timeout: 'browserAttachTimeout',
27
+ element_scroll_behavior: 'elementScrollBehavior',
28
+ full_page_screenshot: 'ie.enableFullPageScreenshot',
29
+ ensure_clean_session: 'ie.ensureCleanSession',
30
+ file_upload_dialog_timeout: 'ie.fileUploadDialogTimeout',
31
+ force_create_process_api: 'ie.forceCreateProcessApi',
32
+ force_shell_windows_api: 'ie.forceShellWindowsApi',
33
+ ignore_protected_mode_settings: 'ignoreProtectedModeSettings',
34
+ ignore_zoom_level: 'ignoreZoomSetting',
35
+ initial_browser_url: 'initialBrowserUrl',
36
+ native_events: 'nativeEvents',
37
+ persistent_hover: 'enablePersistentHover',
38
+ require_window_focus: 'requireWindowFocus',
39
+ use_per_process_proxy: 'ie.usePerProcessProxy',
40
+ validate_cookie_document_type: 'ie.validateCookieDocumentType'
41
+ }.freeze
42
+
43
+ CAPABILITIES.each_key do |key|
44
+ define_method key do
45
+ @options[key]
46
+ end
47
+
48
+ define_method "#{key}=" do |value|
49
+ @options[key] = value
50
+ end
51
+ end
52
+
53
+ attr_reader :args, :options
54
+
55
+ #
56
+ # Create a new Options instance
57
+ #
58
+ # @example
59
+ # options = Selenium::WebDriver::IE::Options.new(args: ['--host=127.0.0.1'])
60
+ # driver = Selenium::WebDriver.for(:ie, options: options)
61
+ #
62
+ # @example
63
+ # options = Selenium::WebDriver::IE::Options.new
64
+ # options.element_scroll_behavior = Selenium::WebDriver::IE::Options::SCROLL_BOTTOM
65
+ # driver = Selenium::WebDriver.for(:ie, options: options)
66
+ #
67
+ # @param [Hash] opts the pre-defined options
68
+ # @option opts [Array<String>] args
69
+ # @option opts [Integer] browser_attach_timeout
70
+ # @option opts [Integer] element_scroll_behavior Either SCROLL_TOP or SCROLL_BOTTOM
71
+ # @option opts [Boolean] full_page_screenshot
72
+ # @option opts [Boolean] ensure_clean_session
73
+ # @option opts [Integer] file_upload_dialog_timeout
74
+ # @option opts [Boolean] force_create_process_api
75
+ # @option opts [Boolean] force_shell_windows_api
76
+ # @option opts [Boolean] ignore_protected_mode_settings
77
+ # @option opts [Boolean] ignore_zoom_level
78
+ # @option opts [String] initial_browser_url
79
+ # @option opts [Boolean] native_events
80
+ # @option opts [Boolean] persistent_hover
81
+ # @option opts [Boolean] require_window_focus
82
+ # @option opts [Boolean] use_per_process_proxy
83
+ # @option opts [Boolean] validate_cookie_document_type
84
+ #
85
+
86
+ def initialize(**opts)
87
+ @args = opts.delete(:args) || []
88
+ @options = opts
89
+ @options[:native_events] ||= true
90
+ end
91
+
92
+ #
93
+ # Add a command-line argument to use when starting Internet Explorer.
94
+ #
95
+ # @param [String] arg The command-line argument to add
96
+ #
97
+
98
+ def add_argument(arg)
99
+ @args << arg
100
+ end
101
+
102
+ #
103
+ # Add a new option not yet handled by these bindings.
104
+ #
105
+ # @example
106
+ # options = Selenium::WebDriver::IE::Options.new
107
+ # options.add_option(:foo, 'bar')
108
+ #
109
+ # @param [String, Symbol] name Name of the option
110
+ # @param [Boolean, String, Integer] value Value of the option
111
+ #
112
+
113
+ def add_option(name, value)
114
+ @options[name] = value
115
+ end
116
+
117
+ #
118
+ # @api private
119
+ #
120
+
121
+ def as_json(*)
122
+ opts = {}
123
+
124
+ CAPABILITIES.each do |capability_alias, capability_name|
125
+ capability_value = @options.delete(capability_alias)
126
+ opts[capability_name] = capability_value unless capability_value.nil?
127
+ end
128
+ opts['ie.browserCommandLineSwitches'] = @args.join(' ') if @args.any?
129
+ opts.merge!(@options)
130
+
131
+ {KEY => opts}
132
+ end
133
+ end # Options
134
+ end # IE
135
+ end # WebDriver
136
+ end # Selenium
@@ -0,0 +1,58 @@
1
+ # Licensed to the Software Freedom Conservancy (SFC) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The SFC licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ module Selenium
19
+ module WebDriver
20
+ module IE
21
+ #
22
+ # @api private
23
+ #
24
+
25
+ class Service < WebDriver::Service
26
+ DEFAULT_PORT = 5555
27
+ @executable = 'IEDriverServer'.freeze
28
+ @missing_text = <<-ERROR.gsub(/\n +| {2,}/, ' ').freeze
29
+ Unable to find IEDriverServer. Please download the server from
30
+ http://selenium-release.storage.googleapis.com/index.html and place it somewhere on your PATH.
31
+ More info at https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver.
32
+ ERROR
33
+
34
+ private
35
+
36
+ def start_process
37
+ @process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
38
+ @process.start
39
+ end
40
+
41
+ def cannot_connect_error_text
42
+ "unable to connect to IE server #{@host}:#{@port}"
43
+ end
44
+
45
+ def extract_service_args(driver_opts)
46
+ driver_args = super
47
+ driver_args << "--log-level=#{driver_opts.delete(:log_level).to_s.upcase}" if driver_opts.key?(:log_level)
48
+ driver_args << "--log-file=#{driver_opts.delete(:log_file)}" if driver_opts.key?(:log_file)
49
+ driver_args << "--implementation=#{driver_opts.delete(:implementation).to_s.upcase}" if driver_opts.key?(:implementation)
50
+ driver_args << "--host=#{driver_opts.delete(:host)}" if driver_opts.key?(:host)
51
+ driver_args << "--extract_path=#{driver_opts.delete(:extract_path)}" if driver_opts.key?(:extract_path)
52
+ driver_args << "--silent" if driver_opts[:silent] == true
53
+ driver_args
54
+ end
55
+ end # Server
56
+ end # IE
57
+ end # WebDriver
58
+ end # Selenium
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
- #
3
1
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
2
  # or more contributor license agreements. See the NOTICE file
5
3
  # distributed with this work for additional information
@@ -19,23 +17,17 @@
19
17
 
20
18
  require 'uri'
21
19
 
22
- require 'selenium/webdriver/remote/capabilities'
23
- require 'selenium/webdriver/remote/w3c_capabilities'
24
20
  require 'selenium/webdriver/remote/bridge'
25
- require 'selenium/webdriver/remote/w3c_bridge'
26
- require 'selenium/webdriver/remote/server_error'
21
+ require 'selenium/webdriver/remote/driver'
27
22
  require 'selenium/webdriver/remote/response'
28
- require 'selenium/webdriver/remote/commands'
29
- require 'selenium/webdriver/remote/w3c_commands'
23
+ require 'selenium/webdriver/remote/server_error'
30
24
  require 'selenium/webdriver/remote/http/common'
31
25
  require 'selenium/webdriver/remote/http/default'
32
26
 
33
- module Selenium
34
- module WebDriver
35
-
36
- # @api private
37
- module Remote
27
+ require 'selenium/webdriver/remote/capabilities'
28
+ require 'selenium/webdriver/remote/oss/bridge'
29
+ require 'selenium/webdriver/remote/oss/commands'
38
30
 
39
- end # Remote
40
- end # WebDriver
41
- end # Selenium
31
+ require 'selenium/webdriver/remote/w3c/bridge'
32
+ require 'selenium/webdriver/remote/w3c/capabilities'
33
+ require 'selenium/webdriver/remote/w3c/commands'
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
- #
3
1
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
2
  # or more contributor license agreements. See the NOTICE file
5
3
  # distributed with this work for additional information
@@ -20,624 +18,141 @@
20
18
  module Selenium
21
19
  module WebDriver
22
20
  module Remote
23
-
24
- #
25
- # Low level bridge to the remote server, through which the rest of the API works.
26
- #
27
- # @api private
28
- #
29
-
30
21
  class Bridge
22
+ include Atoms
31
23
  include BridgeHelper
32
24
 
33
- COMMANDS = {}
25
+ PORT = 4444
26
+ COMMANDS = {
27
+ new_session: [:post, 'session'.freeze]
28
+ }.freeze
29
+
30
+ attr_accessor :context, :http, :file_detector
31
+ attr_reader :capabilities, :dialect
34
32
 
35
33
  #
36
- # Defines a wrapper method for a command, which ultimately calls #execute.
34
+ # Implements protocol handshake which:
37
35
  #
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
36
+ # 1. Creates session with driver.
37
+ # 2. Sniffs response.
38
+ # 3. Based on the response, understands which dialect we should use.
45
39
  #
40
+ # @return [OSS:Bridge, W3C::Bridge]
41
+ #
42
+ def self.handshake(**opts)
43
+ desired_capabilities = opts.delete(:desired_capabilities)
46
44
 
47
- def self.command(name, verb, url)
48
- COMMANDS[name] = [verb, url.freeze]
49
- end
45
+ if desired_capabilities.is_a?(Symbol)
46
+ unless Remote::Capabilities.respond_to?(desired_capabilities)
47
+ raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
48
+ end
49
+ desired_capabilities = Remote::Capabilities.__send__(desired_capabilities)
50
+ end
50
51
 
51
- attr_accessor :context, :http, :file_detector
52
- attr_reader :capabilities
52
+ bridge = new(opts)
53
+ capabilities = bridge.create_session(desired_capabilities)
54
+
55
+ case bridge.dialect
56
+ when :oss
57
+ Remote::OSS::Bridge.new(capabilities, bridge.session_id, opts)
58
+ when :w3c
59
+ Remote::W3C::Bridge.new(capabilities, bridge.session_id, opts)
60
+ else
61
+ raise WebDriverError, 'cannot understand dialect'
62
+ end
63
+ end
53
64
 
54
65
  #
55
- # Initializes the bridge with the given server URL.
56
- #
57
- # @param url [String] url for the remote server
58
- # @param http_client [Object] an HTTP client instance that implements the same protocol as Http::Default
59
- # @param desired_capabilities [Capabilities] an instance of Remote::Capabilities describing the capabilities you want
66
+ # Initializes the bridge with the given server URL
67
+ # @param [Hash] opts options for the driver
68
+ # @option opts [String] :url url for the remote server
69
+ # @option opts [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
70
+ # @option opts [Capabilities] :desired_capabilities an instance of Remote::Capabilities describing the capabilities you want
71
+ # @api private
60
72
  #
61
73
 
62
74
  def initialize(opts = {})
63
75
  opts = opts.dup
64
-
65
- http_client = opts.delete(:http_client) { Http::Default.new }
66
- desired_capabilities = opts.delete(:desired_capabilities) { Capabilities.firefox }
67
- url = opts.delete(:url) { "http://#{Platform.localhost}:4444/wd/hub" }
76
+ http_client = opts.delete(:http_client) { Http::Default.new }
77
+ url = opts.delete(:url) { "http://#{Platform.localhost}:#{PORT}/wd/hub" }
68
78
 
69
79
  unless opts.empty?
70
80
  raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
71
81
  end
72
82
 
73
- if desired_capabilities.kind_of?(Symbol)
74
- unless Capabilities.respond_to?(desired_capabilities)
75
- raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
76
- end
77
-
78
- desired_capabilities = Capabilities.send(desired_capabilities)
79
- end
80
-
81
- uri = url.kind_of?(URI) ? url : URI.parse(url)
82
- uri.path += "/" unless uri.path =~ /\/$/
83
+ uri = url.is_a?(URI) ? url : URI.parse(url)
84
+ uri.path += '/' unless uri.path =~ %r{\/$}
83
85
 
84
86
  http_client.server_url = uri
85
87
 
86
- @http = http_client
87
- @capabilities = create_session(desired_capabilities)
88
-
88
+ @http = http_client
89
89
  @file_detector = nil
90
90
  end
91
91
 
92
- def browser
93
- @browser ||= (
94
- name = @capabilities.browser_name
95
- name ? name.gsub(" ", "_").to_sym : 'unknown'
96
- )
97
- end
98
-
99
- def driver_extensions
100
- [
101
- DriverExtensions::HasInputDevices,
102
- DriverExtensions::UploadsFiles,
103
- DriverExtensions::TakesScreenshot,
104
- DriverExtensions::HasSessionId,
105
- DriverExtensions::Rotatable,
106
- DriverExtensions::HasTouchScreen,
107
- DriverExtensions::HasLocation,
108
- DriverExtensions::HasNetworkConnection,
109
- DriverExtensions::HasRemoteStatus,
110
- DriverExtensions::HasWebStorage
111
- ]
112
- end
113
-
114
92
  #
115
- # Returns the current session ID.
93
+ # Creates session handling both OSS and W3C dialects.
116
94
  #
117
95
 
118
- def session_id
119
- @session_id || raise(Error::WebDriverError, "no current session exists")
120
- end
121
-
122
96
  def create_session(desired_capabilities)
123
- resp = raw_execute :newSession, {}, :desiredCapabilities => desired_capabilities
124
- @session_id = resp['sessionId'] or raise Error::WebDriverError, 'no sessionId in returned payload'
125
-
126
- Capabilities.json_create resp['value']
127
- end
128
-
129
- def status
130
- execute :status
131
- end
132
-
133
- def get(url)
134
- execute :get, {}, :url => url
135
- end
136
-
137
- def getCapabilities
138
- Capabilities.json_create execute(:getCapabilities)
139
- end
140
-
141
- def setImplicitWaitTimeout(milliseconds)
142
- execute :implicitlyWait, {}, :ms => milliseconds
143
- end
144
-
145
- def setScriptTimeout(milliseconds)
146
- execute :setScriptTimeout, {}, :ms => milliseconds
147
- end
148
-
149
- def setTimeout(type, milliseconds)
150
- execute :setTimeout, {}, :type => type, :ms => milliseconds
151
- end
152
-
153
- #
154
- # alerts
155
- #
156
-
157
- def acceptAlert
158
- execute :acceptAlert
159
- end
160
-
161
- def dismissAlert
162
- execute :dismissAlert
163
- end
164
-
165
- def setAlertValue(keys)
166
- execute :setAlertValue, {}, :text => keys.to_s
167
- end
168
-
169
- def getAlertText
170
- execute :getAlertText
171
- end
172
-
173
- def setAuthentication(credentials)
174
- execute :setAuthentication, {}, credentials
175
- end
176
-
177
- #
178
- # navigation
179
- #
180
-
181
- def goBack
182
- execute :goBack
183
- end
184
-
185
- def goForward
186
- execute :goForward
187
- end
188
-
189
- def getCurrentUrl
190
- execute :getCurrentUrl
191
- end
192
-
193
- def getTitle
194
- execute :getTitle
195
- end
97
+ response = execute(:new_session, {}, merged_capabilities(desired_capabilities))
196
98
 
197
- def getPageSource
198
- execute :getPageSource
199
- end
200
-
201
- def switchToWindow(name)
202
- execute :switchToWindow, {}, :name => name
203
- end
204
-
205
- def switchToFrame(id)
206
- execute :switchToFrame, {}, :id => id
207
- end
208
-
209
- def switchToParentFrame
210
- execute :switchToParentFrame
211
- end
212
-
213
- def switchToDefaultContent
214
- execute :switchToFrame, {}, :id => nil
215
- end
216
-
217
- QUIT_ERRORS = [IOError]
218
-
219
- def quit
220
- execute :quit
221
- http.close
222
- rescue *QUIT_ERRORS
223
- end
224
-
225
- def close
226
- execute :close
227
- end
228
-
229
- def refresh
230
- execute :refresh
231
- end
232
-
233
- #
234
- # window handling
235
- #
236
-
237
- def getWindowHandles
238
- execute :getWindowHandles
239
- end
240
-
241
- def getCurrentWindowHandle
242
- execute :getCurrentWindowHandle
243
- end
244
-
245
- def setWindowSize(width, height, handle = :current)
246
- execute :setWindowSize, {:window_handle => handle},
247
- :width => width,
248
- :height => height
249
- end
250
-
251
- def maximizeWindow(handle = :current)
252
- execute :maximizeWindow, :window_handle => handle
253
- end
254
-
255
- def getWindowSize(handle = :current)
256
- data = execute :getWindowSize, :window_handle => handle
257
-
258
- Dimension.new data['width'], data['height']
259
- end
260
-
261
- def setWindowPosition(x, y, handle = :current)
262
- execute :setWindowPosition, {:window_handle => handle},
263
- :x => x, :y => y
264
- end
265
-
266
- def getWindowPosition(handle = :current)
267
- data = execute :getWindowPosition, :window_handle => handle
268
-
269
- Point.new data['x'], data['y']
270
- end
99
+ @session_id = response['sessionId']
100
+ oss_status = response['status']
101
+ value = response['value']
271
102
 
272
- def getScreenshot
273
- execute :screenshot
274
- end
275
-
276
- #
277
- # HTML 5
278
- #
279
-
280
- def getLocalStorageItem(key)
281
- execute :getLocalStorageItem, :key => key
282
- end
283
-
284
- def removeLocalStorageItem(key)
285
- execute :removeLocalStorageItem, :key => key
286
- end
287
-
288
- def getLocalStorageKeys
289
- execute :getLocalStorageKeys
290
- end
291
-
292
- def setLocalStorageItem(key, value)
293
- execute :setLocalStorageItem, {}, :key => key, :value => value
294
- end
295
-
296
- def clearLocalStorage
297
- execute :clearLocalStorage
298
- end
299
-
300
- def getLocalStorageSize
301
- execute :getLocalStorageSize
302
- end
303
-
304
- def getSessionStorageItem(key)
305
- execute :getSessionStorageItem, :key => key
306
- end
103
+ if value.is_a?(Hash)
104
+ @session_id = value['sessionId'] if value.key?('sessionId')
307
105
 
308
- def removeSessionStorageItem(key)
309
- execute :removeSessionStorageItem, :key => key
310
- end
311
-
312
- def getSessionStorageKeys
313
- execute :getSessionStorageKeys
314
- end
315
-
316
- def setSessionStorageItem(key, value)
317
- execute :setSessionStorageItem, {}, :key => key, :value => value
318
- end
319
-
320
- def clearSessionStorage
321
- execute :clearSessionStorage
322
- end
323
-
324
- def getSessionStorageSize
325
- execute :getSessionStorageSize
326
- end
327
-
328
- def getLocation
329
- obj = execute(:getLocation) || {} # android returns null
330
- Location.new obj['latitude'], obj['longitude'], obj['altitude']
331
- end
332
-
333
- def setLocation(lat, lon, alt)
334
- loc = {:latitude => lat, :longitude => lon, :altitude => alt}
335
- execute :setLocation, {}, :location => loc
336
- end
337
-
338
- def getNetworkConnection
339
- execute :getNetworkConnection
340
- end
341
-
342
- def setNetworkConnection(type)
343
- execute :setNetworkConnection, {}, :parameters => {:type => type}
344
- end
345
-
346
- #
347
- # javascript execution
348
- #
349
-
350
- def executeScript(script, *args)
351
- assert_javascript_enabled
352
-
353
- result = execute :executeScript, {}, :script => script, :args => args
354
- unwrap_script_result result
355
- end
356
-
357
- def executeAsyncScript(script, *args)
358
- assert_javascript_enabled
359
-
360
- result = execute :executeAsyncScript, {}, :script => script, :args => args
361
- unwrap_script_result result
362
- end
363
-
364
- #
365
- # cookies
366
- #
367
-
368
- def addCookie(cookie)
369
- execute :addCookie, {}, :cookie => cookie
370
- end
371
-
372
- def deleteCookie(name)
373
- execute :deleteCookie, :name => name
374
- end
375
-
376
- def getAllCookies
377
- execute :getCookies
378
- end
379
-
380
- def deleteAllCookies
381
- execute :deleteAllCookies
382
- end
383
-
384
- #
385
- # actions
386
- #
387
-
388
- def clickElement(element)
389
- execute :clickElement, :id => element
390
- end
391
-
392
- def click
393
- execute :click, {}, :button => 0
394
- end
395
-
396
- def doubleClick
397
- execute :doubleClick
398
- end
399
-
400
- def contextClick
401
- execute :click, {}, :button => 2
402
- end
403
-
404
- def mouseDown
405
- execute :mouseDown
406
- end
407
-
408
- def mouseUp
409
- execute :mouseUp
410
- end
411
-
412
- def mouseMoveTo(element, x = nil, y = nil)
413
- params = { :element => element }
414
-
415
- if x && y
416
- params.merge! :xoffset => x, :yoffset => y
417
- end
418
-
419
- execute :mouseMoveTo, {}, params
420
- end
421
-
422
- def sendKeysToActiveElement(key)
423
- execute :sendKeysToActiveElement, {}, :value => key
424
- end
425
-
426
- def sendKeysToElement(element, keys)
427
- if @file_detector && local_file = @file_detector.call(keys)
428
- keys = upload(local_file)
106
+ if value.key?('capabilities')
107
+ value = value['capabilities']
108
+ elsif value.key?('value')
109
+ value = value['value']
110
+ end
429
111
  end
430
112
 
431
- execute :sendKeysToElement, {:id => element}, {:value => Array(keys)}
432
- end
433
-
434
- def upload(local_file)
435
- unless File.file?(local_file)
436
- raise Error::WebDriverError, "you may only upload files: #{local_file.inspect}"
113
+ unless @session_id
114
+ raise Error::WebDriverError, 'no sessionId in returned payload'
437
115
  end
438
116
 
439
- execute :uploadFile, {}, :file => Zipper.zip_file(local_file)
440
- end
441
-
442
- def clearElement(element)
443
- execute :clearElement, :id => element
444
- end
445
-
446
- def submitElement(element)
447
- execute :submitElement, :id => element
448
- end
449
-
450
- def dragElement(element, right_by, down_by)
451
- execute :dragElement, {:id => element}, :x => right_by, :y => down_by
452
- end
453
-
454
- def touchSingleTap(element)
455
- execute :touchSingleTap, {}, :element => element
456
- end
457
-
458
- def touchDoubleTap(element)
459
- execute :touchDoubleTap, {}, :element => element
460
- end
461
-
462
- def touchLongPress(element)
463
- execute :touchLongPress, {}, :element => element
464
- end
465
-
466
- def touchDown(x, y)
467
- execute :touchDown, {}, :x => x, :y => y
468
- end
469
-
470
- def touchUp(x, y)
471
- execute :touchUp, {}, :x => x, :y => y
472
- end
473
-
474
- def touchMove(x, y)
475
- execute :touchMove, {}, :x => x, :y => y
476
- end
477
-
478
- def touchScroll(element, x, y)
479
- if element
480
- execute :touchScroll, {}, :element => element,
481
- :xoffset => x,
482
- :yoffset => y
117
+ if oss_status
118
+ WebDriver.logger.info 'Detected OSS dialect.'
119
+ @dialect = :oss
120
+ Capabilities.json_create(value)
483
121
  else
484
- execute :touchScroll, {}, :xoffset => x, :yoffset => y
122
+ WebDriver.logger.info 'Detected W3C dialect.'
123
+ @dialect = :w3c
124
+ W3C::Capabilities.json_create(value)
485
125
  end
486
126
  end
487
127
 
488
- def touchFlick(xspeed, yspeed)
489
- execute :touchFlick, {}, :xspeed => xspeed, :yspeed => yspeed
490
- end
491
-
492
- def touchElementFlick(element, right_by, down_by, speed)
493
- execute :touchFlick, {}, :element => element,
494
- :xoffset => right_by,
495
- :yoffset => down_by,
496
- :speed => speed
497
-
498
- end
499
-
500
- def setScreenOrientation(orientation)
501
- execute :setScreenOrientation, {}, :orientation => orientation
502
- end
503
-
504
- def getScreenOrientation
505
- execute :getScreenOrientation
506
- end
507
-
508
- #
509
- # logs
510
- #
511
-
512
- def getAvailableLogTypes
513
- types = execute :getAvailableLogTypes
514
- Array(types).map { |e| e.to_sym }
515
- end
516
-
517
- def getLog(type)
518
- data = execute :getLog, {}, :type => type.to_s
519
-
520
- Array(data).map do |l|
521
- begin
522
- LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
523
- rescue KeyError
524
- next
525
- end
526
- end
527
- end
528
-
529
- #
530
- # element properties
531
128
  #
532
-
533
- def getElementTagName(element)
534
- execute :getElementTagName, :id => element
535
- end
536
-
537
- def getElementAttribute(element, name)
538
- execute :getElementAttribute, :id => element, :name => name
539
- end
540
-
541
- def getElementValue(element)
542
- execute :getElementValue, :id => element
543
- end
544
-
545
- def getElementText(element)
546
- execute :getElementText, :id => element
547
- end
548
-
549
- def getElementLocation(element)
550
- data = execute :getElementLocation, :id => element
551
-
552
- Point.new data['x'], data['y']
553
- end
554
-
555
- def getElementLocationOnceScrolledIntoView(element)
556
- data = execute :getElementLocationOnceScrolledIntoView, :id => element
557
-
558
- Point.new data['x'], data['y']
559
- end
560
-
561
- def getElementSize(element)
562
- data = execute :getElementSize, :id => element
563
-
564
- Dimension.new data['width'], data['height']
565
- end
566
-
567
- def isElementEnabled(element)
568
- execute :isElementEnabled, :id => element
569
- end
570
-
571
- def isElementSelected(element)
572
- execute :isElementSelected, :id => element
573
- end
574
-
575
- def isElementDisplayed(element)
576
- execute :isElementDisplayed, :id => element
577
- end
578
-
579
- def getElementValueOfCssProperty(element, prop)
580
- execute :getElementValueOfCssProperty, :id => element, :property_name => prop
581
- end
582
-
583
- #
584
- # finding elements
129
+ # Returns the current session ID.
585
130
  #
586
131
 
587
- def getActiveElement
588
- Element.new self, element_id_from(execute(:getActiveElement))
589
- end
590
- alias_method :switchToActiveElement, :getActiveElement
591
-
592
- def find_element_by(how, what, parent = nil)
593
- if parent
594
- id = execute :findChildElement, {:id => parent}, {:using => how, :value => what}
595
- else
596
- id = execute :findElement, {}, {:using => how, :value => what}
597
- end
598
-
599
- Element.new self, element_id_from(id)
132
+ def session_id
133
+ @session_id || raise(Error::WebDriverError, 'no current session exists')
600
134
  end
601
135
 
602
- def find_elements_by(how, what, parent = nil)
603
- if parent
604
- ids = execute :findChildElements, {:id => parent}, {:using => how, :value => what}
605
- else
606
- ids = execute :findElements, {}, {:using => how, :value => what}
136
+ def browser
137
+ @browser ||= begin
138
+ name = @capabilities.browser_name
139
+ name ? name.tr(' ', '_').to_sym : 'unknown'
607
140
  end
608
-
609
- ids.map { |id| Element.new self, element_id_from(id) }
610
141
  end
611
142
 
612
143
  private
613
144
 
614
- def assert_javascript_enabled
615
- return if capabilities.javascript_enabled?
616
- raise Error::UnsupportedOperationError, "underlying webdriver instance does not support javascript"
617
- end
618
-
619
- #
620
- # executes a command on the remote server.
621
- #
622
- #
623
- # Returns the 'value' of the returned payload
624
- #
625
-
626
- def execute(*args)
627
- raw_execute(*args)['value']
628
- end
629
-
630
145
  #
631
146
  # executes a command on the remote server.
632
147
  #
633
148
  # @return [WebDriver::Remote::Response]
634
149
  #
635
150
 
636
- def raw_execute(command, opts = {}, command_hash = nil)
637
- verb, path = COMMANDS[command] || raise(ArgumentError, "unknown command: #{command.inspect}")
638
- path = path.dup
151
+ def execute(command, opts = {}, command_hash = nil)
152
+ verb, path = commands(command) || raise(ArgumentError, "unknown command: #{command.inspect}")
153
+ path = path.dup
639
154
 
640
- path[':session_id'] = @session_id if path.include?(":session_id")
155
+ path[':session_id'] = session_id if path.include?(':session_id')
641
156
 
642
157
  begin
643
158
  opts.each { |key, value| path[key.inspect] = escaper.escape(value.to_s) }
@@ -645,12 +160,28 @@ module Selenium
645
160
  raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
646
161
  end
647
162
 
648
- puts "-> #{verb.to_s.upcase} #{path}" if $DEBUG
649
- http.call verb, path, command_hash
163
+ WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
164
+ http.call(verb, path, command_hash)
650
165
  end
651
166
 
652
167
  def escaper
653
- @escaper ||= defined?(URI::Parser) ? URI::Parser.new : URI
168
+ @escaper ||= defined?(URI::Parser) ? URI::DEFAULT_PARSER : URI
169
+ end
170
+
171
+ def commands(command)
172
+ raise NotImplementedError unless command == :new_session
173
+ COMMANDS[command]
174
+ end
175
+
176
+ def merged_capabilities(oss_capabilities)
177
+ w3c_capabilities = W3C::Capabilities.from_oss(oss_capabilities)
178
+
179
+ {
180
+ desiredCapabilities: oss_capabilities,
181
+ capabilities: {
182
+ firstMatch: [w3c_capabilities]
183
+ }
184
+ }
654
185
  end
655
186
 
656
187
  end # Bridge