selenium-webdriver 3.142.7 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +432 -5
  3. data/Gemfile +3 -1
  4. data/LICENSE +1 -1
  5. data/NOTICE +2 -0
  6. data/README.md +4 -5
  7. data/lib/selenium/server.rb +75 -64
  8. data/lib/selenium/webdriver/atoms/findElements.js +122 -0
  9. data/lib/selenium/webdriver/atoms/getAttribute.js +100 -7
  10. data/lib/selenium/webdriver/atoms/isDisplayed.js +76 -78
  11. data/lib/selenium/webdriver/atoms/mutationListener.js +55 -0
  12. data/lib/selenium/webdriver/bidi/session.rb +38 -0
  13. data/lib/selenium/webdriver/bidi.rb +55 -0
  14. data/lib/selenium/webdriver/chrome/driver.rb +26 -83
  15. data/lib/selenium/webdriver/chrome/{bridge.rb → features.rb} +55 -12
  16. data/lib/selenium/webdriver/chrome/options.rb +138 -67
  17. data/lib/selenium/webdriver/chrome/profile.rb +6 -3
  18. data/lib/selenium/webdriver/chrome/service.rb +8 -15
  19. data/lib/selenium/webdriver/chrome.rb +5 -18
  20. data/lib/selenium/webdriver/common/action_builder.rb +171 -236
  21. data/lib/selenium/webdriver/common/driver.rb +76 -29
  22. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +43 -0
  23. data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +51 -0
  24. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +89 -0
  25. data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_bidi.rb} +10 -8
  26. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +87 -0
  27. data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +38 -0
  28. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +44 -0
  29. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +43 -0
  30. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +38 -0
  31. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +5 -8
  32. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +144 -0
  33. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_logs.rb} +4 -4
  34. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +17 -0
  35. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +6 -27
  36. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +136 -0
  37. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +11 -11
  38. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +77 -0
  39. data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
  40. data/lib/selenium/webdriver/common/element.rb +83 -23
  41. data/lib/selenium/webdriver/common/error.rb +32 -196
  42. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  43. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -22
  44. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  45. data/lib/selenium/webdriver/common/interactions/key_actions.rb +10 -6
  46. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  47. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  48. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  49. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +68 -78
  50. data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
  51. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  52. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  53. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  54. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  55. data/lib/selenium/webdriver/common/interactions/scroll.rb +57 -0
  56. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  57. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  58. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
  59. data/lib/selenium/webdriver/common/{w3c_manager.rb → interactions/wheel_input.rb} +14 -17
  60. data/lib/selenium/webdriver/common/keys.rb +1 -0
  61. data/lib/selenium/webdriver/common/log_entry.rb +2 -2
  62. data/lib/selenium/webdriver/common/logger.rb +50 -15
  63. data/lib/selenium/webdriver/common/manager.rb +11 -38
  64. data/lib/selenium/webdriver/common/options.rb +147 -23
  65. data/lib/selenium/webdriver/common/platform.rb +10 -5
  66. data/lib/selenium/webdriver/common/port_prober.rb +4 -6
  67. data/lib/selenium/webdriver/common/profile_helper.rb +11 -9
  68. data/lib/selenium/webdriver/common/proxy.rb +6 -3
  69. data/lib/selenium/webdriver/common/search_context.rb +7 -9
  70. data/lib/selenium/webdriver/common/service.rb +17 -125
  71. data/lib/selenium/webdriver/common/service_manager.rb +150 -0
  72. data/lib/selenium/webdriver/common/shadow_root.rb +87 -0
  73. data/lib/selenium/webdriver/common/socket_lock.rb +2 -2
  74. data/lib/selenium/webdriver/common/socket_poller.rb +3 -3
  75. data/lib/selenium/webdriver/common/takes_screenshot.rb +66 -0
  76. data/lib/selenium/webdriver/common/target_locator.rb +32 -4
  77. data/lib/selenium/webdriver/common/timeouts.rb +31 -4
  78. data/lib/selenium/webdriver/common/wait.rb +1 -1
  79. data/lib/selenium/webdriver/common/websocket_connection.rb +149 -0
  80. data/lib/selenium/webdriver/common/window.rb +0 -4
  81. data/lib/selenium/webdriver/common/zipper.rb +3 -9
  82. data/lib/selenium/webdriver/common.rb +35 -18
  83. data/lib/selenium/webdriver/devtools/console_event.rb +38 -0
  84. data/lib/selenium/webdriver/devtools/exception_event.rb +36 -0
  85. data/lib/selenium/webdriver/devtools/mutation_event.rb +37 -0
  86. data/lib/selenium/webdriver/devtools/pinned_script.rb +59 -0
  87. data/lib/selenium/webdriver/devtools/request.rb +67 -0
  88. data/lib/selenium/webdriver/devtools/response.rb +66 -0
  89. data/lib/selenium/webdriver/devtools.rb +86 -0
  90. data/lib/selenium/webdriver/edge/driver.rb +7 -29
  91. data/lib/selenium/webdriver/edge/features.rb +45 -0
  92. data/lib/selenium/webdriver/edge/options.rb +11 -48
  93. data/lib/selenium/webdriver/edge/profile.rb +33 -0
  94. data/lib/selenium/webdriver/edge/service.rb +10 -26
  95. data/lib/selenium/webdriver/edge.rb +11 -14
  96. data/lib/selenium/webdriver/firefox/driver.rb +32 -19
  97. data/lib/selenium/webdriver/firefox/extension.rb +8 -0
  98. data/lib/selenium/webdriver/firefox/features.rb +63 -0
  99. data/lib/selenium/webdriver/firefox/options.rb +73 -50
  100. data/lib/selenium/webdriver/firefox/profile.rb +16 -70
  101. data/lib/selenium/webdriver/firefox/service.rb +5 -9
  102. data/lib/selenium/webdriver/firefox/util.rb +1 -1
  103. data/lib/selenium/webdriver/firefox.rb +17 -28
  104. data/lib/selenium/webdriver/ie/driver.rb +1 -47
  105. data/lib/selenium/webdriver/ie/options.rb +15 -46
  106. data/lib/selenium/webdriver/ie/service.rb +13 -15
  107. data/lib/selenium/webdriver/ie.rb +3 -16
  108. data/lib/selenium/webdriver/remote/bridge.rb +563 -86
  109. data/lib/selenium/webdriver/remote/capabilities.rb +159 -123
  110. data/lib/selenium/webdriver/remote/commands.rb +158 -0
  111. data/lib/selenium/webdriver/remote/driver.rb +22 -13
  112. data/lib/selenium/webdriver/remote/http/common.rb +0 -5
  113. data/lib/selenium/webdriver/remote/http/default.rb +22 -31
  114. data/lib/selenium/webdriver/remote/response.rb +18 -49
  115. data/lib/selenium/webdriver/remote.rb +15 -12
  116. data/lib/selenium/webdriver/safari/driver.rb +3 -31
  117. data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +3 -3
  118. data/lib/selenium/webdriver/safari/options.rb +10 -29
  119. data/lib/selenium/webdriver/safari/service.rb +4 -8
  120. data/lib/selenium/webdriver/safari.rb +13 -19
  121. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
  122. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +63 -0
  123. data/lib/selenium/webdriver/support/cdp_client_generator.rb +108 -0
  124. data/lib/selenium/webdriver/support/color.rb +9 -9
  125. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  126. data/lib/selenium/webdriver/support/guards/guard.rb +89 -0
  127. data/lib/selenium/webdriver/{firefox/marionette/bridge.rb → support/guards/guard_condition.rb} +22 -19
  128. data/lib/selenium/webdriver/support/guards.rb +95 -0
  129. data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
  130. data/lib/selenium/webdriver/support/select.rb +3 -3
  131. data/lib/selenium/webdriver/support.rb +1 -0
  132. data/lib/selenium/webdriver/version.rb +1 -1
  133. data/lib/selenium/webdriver.rb +14 -13
  134. data/selenium-webdriver.gemspec +32 -13
  135. metadata +176 -69
  136. data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
  137. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +0 -64
  138. data/lib/selenium/webdriver/common/keyboard.rb +0 -70
  139. data/lib/selenium/webdriver/common/mouse.rb +0 -89
  140. data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -78
  141. data/lib/selenium/webdriver/common/touch_screen.rb +0 -123
  142. data/lib/selenium/webdriver/common/w3c_action_builder.rb +0 -212
  143. data/lib/selenium/webdriver/edge/bridge.rb +0 -76
  144. data/lib/selenium/webdriver/firefox/binary.rb +0 -187
  145. data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
  146. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  147. data/lib/selenium/webdriver/firefox/launcher.rb +0 -111
  148. data/lib/selenium/webdriver/firefox/legacy/driver.rb +0 -83
  149. data/lib/selenium/webdriver/firefox/marionette/driver.rb +0 -90
  150. data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
  151. data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
  152. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -60
  153. data/lib/selenium/webdriver/remote/oss/bridge.rb +0 -594
  154. data/lib/selenium/webdriver/remote/oss/commands.rb +0 -223
  155. data/lib/selenium/webdriver/remote/w3c/bridge.rb +0 -605
  156. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +0 -310
  157. data/lib/selenium/webdriver/remote/w3c/commands.rb +0 -157
@@ -20,32 +20,35 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Remote
23
+
23
24
  #
24
25
  # Specification of the desired and/or actual capabilities of the browser that the
25
26
  # server is being asked to create.
26
27
  #
28
+
27
29
  class Capabilities
28
- DEFAULTS = {
29
- browser_name: '',
30
- version: '',
31
- platform: :any,
32
- javascript_enabled: false,
33
- css_selectors_enabled: false,
34
- takes_screenshot: false,
35
- native_events: false,
36
- rotatable: false,
37
- firefox_profile: nil,
38
- proxy: nil
39
- }.freeze
40
-
41
- DEFAULTS.each_key do |key|
42
- if key != :javascript_enabled
43
- define_method key do
44
- @capabilities.fetch(key)
45
- end
46
- end
47
30
 
48
- next if key == :proxy
31
+ KNOWN = [
32
+ :browser_name,
33
+ :browser_version,
34
+ :platform_name,
35
+ :accept_insecure_certs,
36
+ :page_load_strategy,
37
+ :proxy,
38
+ :set_window_rect,
39
+ :timeouts,
40
+ :unhandled_prompt_behavior,
41
+ :strict_file_interactability,
42
+ :web_socket_url,
43
+
44
+ # remote-specific (webdriver.remote.sessionid)
45
+ :remote_session_id
46
+ ].freeze
47
+
48
+ (KNOWN - %i[proxy timeouts]).each do |key|
49
+ define_method key do
50
+ @capabilities[key]
51
+ end
49
52
 
50
53
  define_method "#{key}=" do |value|
51
54
  @capabilities[key] = value
@@ -53,19 +56,13 @@ module Selenium
53
56
  end
54
57
 
55
58
  #
56
- # Returns javascript_enabled capability.
57
- # It is true if not set explicitly.
59
+ # Backward compatibility
58
60
  #
59
- def javascript_enabled
60
- javascript_enabled = @capabilities.fetch(:javascript_enabled)
61
- javascript_enabled.nil? ? true : javascript_enabled
62
- end
63
61
 
64
- alias_method :css_selectors_enabled?, :css_selectors_enabled
65
- alias_method :javascript_enabled?, :javascript_enabled
66
- alias_method :native_events?, :native_events
67
- alias_method :takes_screenshot?, :takes_screenshot
68
- alias_method :rotatable?, :rotatable
62
+ alias_method :version, :browser_version
63
+ alias_method :version=, :browser_version=
64
+ alias_method :platform, :platform_name
65
+ alias_method :platform=, :platform_name=
69
66
 
70
67
  #
71
68
  # Convenience methods for the common choices.
@@ -74,80 +71,50 @@ module Selenium
74
71
  class << self
75
72
  def chrome(opts = {})
76
73
  new({
77
- browser_name: 'chrome',
78
- javascript_enabled: true,
79
- css_selectors_enabled: true
74
+ browser_name: 'chrome'
80
75
  }.merge(opts))
81
76
  end
82
77
 
83
78
  def edge(opts = {})
84
79
  new({
85
- browser_name: 'MicrosoftEdge',
86
- platform: :windows
80
+ browser_name: 'MicrosoftEdge'
87
81
  }.merge(opts))
88
82
  end
83
+ alias_method :microsoftedge, :edge
89
84
 
90
85
  def firefox(opts = {})
91
- opts[:browser_version] = opts.delete(:version) if opts.key?(:version)
92
- opts[:platform_name] = opts.delete(:platform) if opts.key?(:platform)
93
- opts[:timeouts] = {}
94
- opts[:timeouts]['implicit'] = opts.delete(:implicit_timeout) if opts.key?(:implicit_timeout)
95
- opts[:timeouts]['pageLoad'] = opts.delete(:page_load_timeout) if opts.key?(:page_load_timeout)
96
- opts[:timeouts]['script'] = opts.delete(:script_timeout) if opts.key?(:script_timeout)
97
- new({browser_name: 'firefox', marionette: true}.merge(opts))
98
- end
99
-
100
- def firefox_legacy(opts = {})
101
86
  new({
102
- browser_name: 'firefox',
103
- javascript_enabled: true,
104
- takes_screenshot: true,
105
- css_selectors_enabled: true
87
+ browser_name: 'firefox'
106
88
  }.merge(opts))
107
89
  end
90
+ alias_method :ff, :firefox
108
91
 
109
- def htmlunit(opts = {})
92
+ def safari(opts = {})
110
93
  new({
111
- browser_name: 'htmlunit'
94
+ browser_name: Selenium::WebDriver::Safari.technology_preview? ? "Safari Technology Preview" : 'safari'
112
95
  }.merge(opts))
113
96
  end
114
97
 
115
- def htmlunitwithjs(opts = {})
98
+ def htmlunit(opts = {})
116
99
  new({
117
- browser_name: 'htmlunit',
118
- javascript_enabled: true
100
+ browser_name: 'htmlunit'
119
101
  }.merge(opts))
120
102
  end
121
103
 
122
104
  def internet_explorer(opts = {})
123
105
  new({
124
106
  browser_name: 'internet explorer',
125
- platform: :windows,
126
- takes_screenshot: true,
127
- css_selectors_enabled: true,
128
- native_events: true
107
+ platform_name: :windows
129
108
  }.merge(opts))
130
109
  end
131
110
  alias_method :ie, :internet_explorer
132
111
 
133
- def phantomjs(opts = {})
134
- WebDriver.logger.deprecate 'Selenium support for PhantomJS', 'headless Chrome/Firefox or HTMLUnit'
135
- new({
136
- browser_name: 'phantomjs',
137
- javascript_enabled: true,
138
- takes_screenshot: true,
139
- css_selectors_enabled: true
140
- }.merge(opts))
112
+ def always_match(capabilities)
113
+ new(always_match: capabilities)
141
114
  end
142
115
 
143
- def safari(opts = {})
144
- new({
145
- browser_name: 'safari',
146
- platform: :mac,
147
- javascript_enabled: true,
148
- takes_screenshot: true,
149
- css_selectors_enabled: true
150
- }.merge(opts))
116
+ def first_match(*capabilities)
117
+ new(first_match: capabilities)
151
118
  end
152
119
 
153
120
  #
@@ -156,41 +123,61 @@ module Selenium
156
123
 
157
124
  def json_create(data)
158
125
  data = data.dup
159
-
160
126
  caps = new
161
- caps.browser_name = data.delete('browserName')
162
- caps.version = data.delete('version')
163
- caps.platform = data.delete('platform').downcase.tr(' ', '_').to_sym if data.key?('platform')
164
- caps.javascript_enabled = data.delete('javascriptEnabled')
165
- caps.css_selectors_enabled = data.delete('cssSelectorsEnabled')
166
- caps.takes_screenshot = data.delete('takesScreenshot')
167
- caps.native_events = data.delete('nativeEvents')
168
- caps.rotatable = data.delete('rotatable')
169
- caps.proxy = Proxy.json_create(data['proxy']) if data.key?('proxy') && !data['proxy'].empty?
127
+
128
+ process_timeouts(caps, data.delete('timeouts'))
129
+
130
+ if data.key?('proxy')
131
+ proxy = data.delete('proxy')
132
+ caps.proxy = Proxy.json_create(proxy) unless proxy.nil? || proxy.empty?
133
+ end
134
+
135
+ # Remote Server Specific
136
+ if data.key?('webdriver.remote.sessionid')
137
+ caps[:remote_session_id] = data.delete('webdriver.remote.sessionid')
138
+ end
139
+
140
+ KNOWN.each do |cap|
141
+ data_value = camel_case(cap)
142
+ caps[cap] = data.delete(data_value) if data.key?(data_value)
143
+ end
170
144
 
171
145
  # any remaining pairs will be added as is, with no conversion
172
146
  caps.merge!(data)
173
147
 
174
148
  caps
175
149
  end
150
+
151
+ def camel_case(str_or_sym)
152
+ str_or_sym.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
153
+ end
154
+
155
+ private
156
+
157
+ def process_timeouts(caps, timeouts)
158
+ return if timeouts.nil?
159
+
160
+ caps.implicit_timeout = timeouts['implicit']
161
+ caps.page_load_timeout = timeouts['pageLoad']
162
+ caps.script_timeout = timeouts['script']
163
+ end
176
164
  end
177
165
 
178
166
  #
179
- # @option :browser_name [String] required browser name
180
- # @option :version [String] required browser version number
181
- # @option :platform [Symbol] one of :any, :win, :mac, or :x
182
- # @option :javascript_enabled [Boolean] does the driver have javascript enabled?
183
- # @option :css_selectors_enabled [Boolean] does the driver support CSS selectors?
184
- # @option :takes_screenshot [Boolean] can this driver take screenshots?
185
- # @option :native_events [Boolean] does this driver use native events?
186
- # @option :proxy [Selenium::WebDriver::Proxy, Hash] proxy configuration
167
+ # @param [Hash] opts
168
+ # @option :browser_name [String] required browser name
169
+ # @option :browser_version [String] required browser version number
170
+ # @option :platform_name [Symbol] one of :any, :win, :mac, or :x
171
+ # @option :accept_insecure_certs [Boolean] does the driver accept insecure SSL certifications?
172
+ # @option :proxy [Selenium::WebDriver::Proxy, Hash] proxy configuration
187
173
  #
188
174
  # @api public
189
175
  #
190
176
 
191
177
  def initialize(opts = {})
192
- @capabilities = DEFAULTS.merge(opts)
193
- self.proxy = opts.delete(:proxy)
178
+ @capabilities = {}
179
+ self.proxy = opts.delete(:proxy) if opts[:proxy]
180
+ @capabilities.merge!(opts)
194
181
  end
195
182
 
196
183
  #
@@ -215,6 +202,10 @@ module Selenium
215
202
  end
216
203
  end
217
204
 
205
+ def proxy
206
+ @capabilities[:proxy]
207
+ end
208
+
218
209
  def proxy=(proxy)
219
210
  case proxy
220
211
  when Hash
@@ -226,37 +217,46 @@ module Selenium
226
217
  end
227
218
  end
228
219
 
220
+ def timeouts
221
+ @capabilities[:timeouts] ||= {}
222
+ end
223
+
224
+ def timeouts=(timeouts)
225
+ @capabilities[:timeouts] = timeouts
226
+ end
227
+
228
+ def implicit_timeout
229
+ timeouts[:implicit]
230
+ end
231
+
232
+ def implicit_timeout=(timeout)
233
+ timeouts[:implicit] = timeout
234
+ end
235
+
236
+ def page_load_timeout
237
+ timeouts[:page_load] || timeouts[:pageLoad]
238
+ end
239
+
240
+ def page_load_timeout=(timeout)
241
+ timeouts[:page_load] = timeout
242
+ end
243
+
244
+ def script_timeout
245
+ timeouts[:script]
246
+ end
247
+
248
+ def script_timeout=(timeout)
249
+ timeouts[:script] = timeout
250
+ end
251
+
229
252
  #
230
253
  # @api private
231
254
  #
232
255
 
233
- def as_json(*) # rubocop:disable Metrics/CyclomaticComplexity
234
- hash = {}
235
-
236
- @capabilities.each do |key, value|
237
- case key
238
- when :platform
239
- hash['platform'] = value.to_s.upcase
240
- when :firefox_profile
241
- if value
242
- WebDriver.logger.deprecate(':firefox_profile capabilitiy', 'Selenium::WebDriver::Firefox::Options#profile')
243
- hash['firefox_profile'] = value.as_json['zip']
244
- end
245
- when :proxy
246
- hash['proxy'] = value.as_json if value
247
- when String, :firefox_binary
248
- if key == :firefox_binary && value
249
- WebDriver.logger.deprecate(':firefox_binary capabilitiy', 'Selenium::WebDriver::Firefox::Options#binary')
250
- end
251
- hash[key.to_s] = value
252
- when Symbol
253
- hash[camel_case(key.to_s)] = value
254
- else
255
- raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class} / #{value.inspect}"
256
- end
256
+ def as_json(*)
257
+ @capabilities.each_with_object({}) do |(key, value), hash|
258
+ hash[convert_key(key)] = process_capabilities(key, value, hash)
257
259
  end
258
-
259
- hash
260
260
  end
261
261
 
262
262
  def to_json(*)
@@ -268,6 +268,7 @@ module Selenium
268
268
 
269
269
  as_json == other.as_json
270
270
  end
271
+
271
272
  alias_method :eql?, :==
272
273
 
273
274
  protected
@@ -276,8 +277,43 @@ module Selenium
276
277
 
277
278
  private
278
279
 
279
- def camel_case(str)
280
- str.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
280
+ def process_capabilities(key, value, hash)
281
+ case value
282
+ when Array
283
+ value.map { |v| process_capabilities(key, v, hash) }
284
+ when Hash
285
+ value.each_with_object({}) do |(k, v), h|
286
+ h[convert_key(k)] = process_capabilities(k, v, h)
287
+ end
288
+ when Capabilities, Options
289
+ value.as_json
290
+ else
291
+ convert_value(key, value)
292
+ end
293
+ end
294
+
295
+ def convert_key(key)
296
+ case key
297
+ when String
298
+ key.to_s
299
+ when Symbol
300
+ self.class.camel_case(key)
301
+ else
302
+ raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"
303
+ end
304
+ end
305
+
306
+ def convert_value(key, value)
307
+ case key
308
+ when :platform
309
+ value.to_s.upcase
310
+ when :proxy
311
+ value&.as_json
312
+ when :unhandled_prompt_behavior
313
+ value.is_a?(Symbol) ? value.to_s.tr('_', ' ') : value
314
+ else
315
+ value
316
+ end
281
317
  end
282
318
  end # Capabilities
283
319
  end # Remote
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Licensed to the Software Freedom Conservancy (SFC) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The SFC licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+
20
+ module Selenium
21
+ module WebDriver
22
+ module Remote
23
+
24
+ #
25
+ # https://w3c.github.io/webdriver/#endpoints
26
+ # @api private
27
+ #
28
+
29
+ class Bridge
30
+ COMMANDS = {
31
+ status: [:get, 'status'],
32
+
33
+ #
34
+ # session handling
35
+ #
36
+
37
+ new_session: [:post, 'session'],
38
+ delete_session: [:delete, 'session/:session_id'],
39
+
40
+ #
41
+ # basic driver
42
+ #
43
+
44
+ get: [:post, 'session/:session_id/url'],
45
+ get_current_url: [:get, 'session/:session_id/url'],
46
+ back: [:post, 'session/:session_id/back'],
47
+ forward: [:post, 'session/:session_id/forward'],
48
+ refresh: [:post, 'session/:session_id/refresh'],
49
+ get_title: [:get, 'session/:session_id/title'],
50
+
51
+ #
52
+ # window and Frame handling
53
+ #
54
+
55
+ get_window_handle: [:get, 'session/:session_id/window'],
56
+ new_window: [:post, 'session/:session_id/window/new'],
57
+ close_window: [:delete, 'session/:session_id/window'],
58
+ switch_to_window: [:post, 'session/:session_id/window'],
59
+ get_window_handles: [:get, 'session/:session_id/window/handles'],
60
+ fullscreen_window: [:post, 'session/:session_id/window/fullscreen'],
61
+ minimize_window: [:post, 'session/:session_id/window/minimize'],
62
+ maximize_window: [:post, 'session/:session_id/window/maximize'],
63
+ set_window_rect: [:post, 'session/:session_id/window/rect'],
64
+ get_window_rect: [:get, 'session/:session_id/window/rect'],
65
+ switch_to_frame: [:post, 'session/:session_id/frame'],
66
+ switch_to_parent_frame: [:post, 'session/:session_id/frame/parent'],
67
+
68
+ #
69
+ # element
70
+ #
71
+
72
+ find_element: [:post, 'session/:session_id/element'],
73
+ find_elements: [:post, 'session/:session_id/elements'],
74
+ find_child_element: [:post, 'session/:session_id/element/:id/element'],
75
+ find_child_elements: [:post, 'session/:session_id/element/:id/elements'],
76
+ find_shadow_child_element: [:post, 'session/:session_id/shadow/:id/element'],
77
+ find_shadow_child_elements: [:post, 'session/:session_id/shadow/:id/elements'],
78
+ get_active_element: [:get, 'session/:session_id/element/active'],
79
+ get_element_shadow_root: [:get, 'session/:session_id/element/:id/shadow'],
80
+ is_element_selected: [:get, 'session/:session_id/element/:id/selected'],
81
+ get_element_attribute: [:get, 'session/:session_id/element/:id/attribute/:name'],
82
+ get_element_property: [:get, 'session/:session_id/element/:id/property/:name'],
83
+ get_element_css_value: [:get, 'session/:session_id/element/:id/css/:property_name'],
84
+ get_element_aria_role: [:get, 'session/:session_id/element/:id/computedrole'],
85
+ get_element_aria_label: [:get, 'session/:session_id/element/:id/computedlabel'],
86
+ get_element_text: [:get, 'session/:session_id/element/:id/text'],
87
+ get_element_tag_name: [:get, 'session/:session_id/element/:id/name'],
88
+ get_element_rect: [:get, 'session/:session_id/element/:id/rect'],
89
+ is_element_enabled: [:get, 'session/:session_id/element/:id/enabled'],
90
+
91
+ #
92
+ # document handling
93
+ #
94
+
95
+ get_page_source: [:get, 'session/:session_id/source'],
96
+ execute_script: [:post, 'session/:session_id/execute/sync'],
97
+ execute_async_script: [:post, 'session/:session_id/execute/async'],
98
+
99
+ #
100
+ # cookies
101
+ #
102
+
103
+ get_all_cookies: [:get, 'session/:session_id/cookie'],
104
+ get_cookie: [:get, 'session/:session_id/cookie/:name'],
105
+ add_cookie: [:post, 'session/:session_id/cookie'],
106
+ delete_cookie: [:delete, 'session/:session_id/cookie/:name'],
107
+ delete_all_cookies: [:delete, 'session/:session_id/cookie'],
108
+
109
+ #
110
+ # timeouts
111
+ #
112
+
113
+ get_timeouts: [:get, 'session/:session_id/timeouts'],
114
+ set_timeout: [:post, 'session/:session_id/timeouts'],
115
+
116
+ #
117
+ # actions
118
+ #
119
+
120
+ actions: [:post, 'session/:session_id/actions'],
121
+ release_actions: [:delete, 'session/:session_id/actions'],
122
+ print_page: [:post, 'session/:session_id/print'],
123
+
124
+ #
125
+ # Element Operations
126
+ #
127
+
128
+ element_click: [:post, 'session/:session_id/element/:id/click'],
129
+ element_clear: [:post, 'session/:session_id/element/:id/clear'],
130
+ element_send_keys: [:post, 'session/:session_id/element/:id/value'],
131
+
132
+ #
133
+ # alerts
134
+ #
135
+
136
+ dismiss_alert: [:post, 'session/:session_id/alert/dismiss'],
137
+ accept_alert: [:post, 'session/:session_id/alert/accept'],
138
+ get_alert_text: [:get, 'session/:session_id/alert/text'],
139
+ send_alert_text: [:post, 'session/:session_id/alert/text'],
140
+
141
+ #
142
+ # screenshot
143
+ #
144
+
145
+ take_screenshot: [:get, 'session/:session_id/screenshot'],
146
+ take_element_screenshot: [:get, 'session/:session_id/element/:id/screenshot'],
147
+
148
+ #
149
+ # server extensions
150
+ #
151
+
152
+ upload_file: [:post, 'session/:session_id/se/file']
153
+ }.freeze
154
+
155
+ end # Bridge
156
+ end # Remote
157
+ end # WebDriver
158
+ end # Selenium
@@ -28,23 +28,32 @@ module Selenium
28
28
 
29
29
  class Driver < WebDriver::Driver
30
30
  include DriverExtensions::UploadsFiles
31
- include DriverExtensions::TakesScreenshot
32
31
  include DriverExtensions::HasSessionId
33
- include DriverExtensions::Rotatable
34
- include DriverExtensions::HasRemoteStatus
35
- include DriverExtensions::HasWebStorage
36
-
37
- def initialize(opts = {})
38
- listener = opts.delete(:listener)
39
- @bridge = Bridge.handshake(opts)
40
- if @bridge.dialect == :oss
41
- extend DriverExtensions::HasTouchScreen
42
- extend DriverExtensions::HasLocation
43
- extend DriverExtensions::HasNetworkConnection
32
+
33
+ def initialize(bridge: nil, listener: nil, **opts)
34
+ desired_capabilities = opts[:desired_capabilities]
35
+ if desired_capabilities.is_a?(Symbol)
36
+ unless Remote::Capabilities.respond_to?(desired_capabilities)
37
+ raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
38
+ end
39
+
40
+ opts[:desired_capabilities] = Remote::Capabilities.__send__(desired_capabilities)
44
41
  end
45
- super(@bridge, listener: listener)
42
+ opts[:url] ||= "http://#{Platform.localhost}:4444/wd/hub"
43
+ super
44
+ @bridge.file_detector = ->((filename, *)) { File.exist?(filename) && filename.to_s }
45
+ end
46
+
47
+ private
48
+
49
+ def devtools_url
50
+ capabilities['se:cdp']
46
51
  end
47
52
 
53
+ def devtools_version
54
+ capabilities['se:cdpVersion']&.split('.')&.first ||
55
+ raise(Error::WebDriverError, "DevTools is not supported by the Remote Server")
56
+ end
48
57
  end # Driver
49
58
  end # Remote
50
59
  end # WebDriver
@@ -30,13 +30,8 @@ module Selenium
30
30
  'User-Agent' => "selenium/#{WebDriver::VERSION} (ruby #{Platform.os})"
31
31
  }.freeze
32
32
 
33
- attr_accessor :timeout
34
33
  attr_writer :server_url
35
34
 
36
- def initialize
37
- @timeout = nil
38
- end
39
-
40
35
  def quit_errors
41
36
  [IOError]
42
37
  end