selenium-webdriver 2.53.4 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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