selenium-webdriver 4.0.0.alpha6 → 4.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +137 -4
  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 +18 -26
  8. data/lib/selenium/webdriver.rb +1 -4
  9. data/lib/selenium/webdriver/atoms/findElements.js +93 -93
  10. data/lib/selenium/webdriver/atoms/getAttribute.js +75 -59
  11. data/lib/selenium/webdriver/atoms/isDisplayed.js +72 -72
  12. data/lib/selenium/webdriver/atoms/mutationListener.js +55 -0
  13. data/lib/selenium/webdriver/chrome.rb +1 -1
  14. data/lib/selenium/webdriver/chrome/driver.rb +25 -10
  15. data/lib/selenium/webdriver/chrome/{bridge.rb → features.rb} +3 -3
  16. data/lib/selenium/webdriver/chrome/options.rb +51 -39
  17. data/lib/selenium/webdriver/chrome/profile.rb +6 -3
  18. data/lib/selenium/webdriver/chrome/service.rb +4 -2
  19. data/lib/selenium/webdriver/common.rb +8 -2
  20. data/lib/selenium/webdriver/common/driver.rb +36 -8
  21. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +43 -0
  22. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +89 -0
  23. data/lib/selenium/webdriver/{edge_html/driver.rb → common/driver_extensions/has_cdp.rb} +12 -13
  24. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +6 -1
  25. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +5 -8
  26. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +149 -0
  27. data/lib/selenium/webdriver/common/driver_extensions/has_logs.rb +30 -0
  28. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +6 -27
  29. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +67 -0
  30. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +1 -0
  31. data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
  32. data/lib/selenium/webdriver/common/element.rb +66 -12
  33. data/lib/selenium/webdriver/common/interactions/interaction.rb +4 -1
  34. data/lib/selenium/webdriver/common/logger.rb +5 -2
  35. data/lib/selenium/webdriver/common/manager.rb +6 -1
  36. data/lib/selenium/webdriver/common/options.rb +76 -14
  37. data/lib/selenium/webdriver/common/platform.rb +3 -1
  38. data/lib/selenium/webdriver/common/proxy.rb +6 -3
  39. data/lib/selenium/webdriver/common/search_context.rb +4 -1
  40. data/lib/selenium/webdriver/common/service.rb +1 -8
  41. data/lib/selenium/webdriver/common/service_manager.rb +1 -1
  42. data/lib/selenium/webdriver/common/socket_poller.rb +19 -30
  43. data/lib/selenium/webdriver/common/takes_screenshot.rb +66 -0
  44. data/lib/selenium/webdriver/common/target_locator.rb +4 -4
  45. data/lib/selenium/webdriver/devtools.rb +44 -18
  46. data/lib/selenium/webdriver/devtools/console_event.rb +38 -0
  47. data/lib/selenium/webdriver/devtools/exception_event.rb +36 -0
  48. data/lib/selenium/webdriver/devtools/mutation_event.rb +37 -0
  49. data/lib/selenium/webdriver/devtools/{console.rb → request.rb} +22 -22
  50. data/lib/selenium/webdriver/edge.rb +7 -29
  51. data/lib/selenium/webdriver/{edge_chrome → edge}/driver.rb +8 -6
  52. data/lib/selenium/webdriver/{edge_chrome/bridge.rb → edge/features.rb} +9 -7
  53. data/lib/selenium/webdriver/{edge_chrome → edge}/options.rb +11 -4
  54. data/lib/selenium/webdriver/{edge_chrome → edge}/profile.rb +2 -2
  55. data/lib/selenium/webdriver/{edge_chrome → edge}/service.rb +2 -2
  56. data/lib/selenium/webdriver/firefox.rb +5 -1
  57. data/lib/selenium/webdriver/firefox/driver.rb +18 -5
  58. data/lib/selenium/webdriver/firefox/{bridge.rb → features.rb} +9 -4
  59. data/lib/selenium/webdriver/firefox/options.rb +20 -21
  60. data/lib/selenium/webdriver/firefox/profile.rb +12 -2
  61. data/lib/selenium/webdriver/firefox/service.rb +1 -1
  62. data/lib/selenium/webdriver/ie/driver.rb +1 -2
  63. data/lib/selenium/webdriver/ie/options.rb +1 -11
  64. data/lib/selenium/webdriver/ie/service.rb +4 -2
  65. data/lib/selenium/webdriver/remote/bridge.rb +48 -30
  66. data/lib/selenium/webdriver/remote/capabilities.rb +105 -64
  67. data/lib/selenium/webdriver/remote/commands.rb +3 -0
  68. data/lib/selenium/webdriver/remote/driver.rb +10 -3
  69. data/lib/selenium/webdriver/remote/http/common.rb +0 -5
  70. data/lib/selenium/webdriver/remote/http/default.rb +8 -7
  71. data/lib/selenium/webdriver/remote/http/persistent.rb +6 -0
  72. data/lib/selenium/webdriver/safari.rb +8 -1
  73. data/lib/selenium/webdriver/safari/driver.rb +3 -8
  74. data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +3 -3
  75. data/lib/selenium/webdriver/safari/options.rb +0 -25
  76. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
  77. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +63 -0
  78. data/lib/selenium/webdriver/support/cdp_client_generator.rb +44 -13
  79. data/lib/selenium/webdriver/support/event_firing_bridge.rb +1 -1
  80. data/lib/selenium/webdriver/support/guards.rb +95 -0
  81. data/lib/selenium/webdriver/support/guards/guard.rb +89 -0
  82. data/lib/selenium/webdriver/support/guards/guard_condition.rb +52 -0
  83. data/lib/selenium/webdriver/support/select.rb +2 -2
  84. data/lib/selenium/webdriver/version.rb +1 -1
  85. data/selenium-webdriver.gemspec +26 -12
  86. metadata +91 -108
  87. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +0 -65
  88. data/lib/selenium/webdriver/devtools/accessibility.rb +0 -62
  89. data/lib/selenium/webdriver/devtools/animation.rb +0 -98
  90. data/lib/selenium/webdriver/devtools/application_cache.rb +0 -64
  91. data/lib/selenium/webdriver/devtools/audits.rb +0 -61
  92. data/lib/selenium/webdriver/devtools/background_service.rb +0 -67
  93. data/lib/selenium/webdriver/devtools/browser.rb +0 -123
  94. data/lib/selenium/webdriver/devtools/cache_storage.rb +0 -73
  95. data/lib/selenium/webdriver/devtools/cast.rb +0 -70
  96. data/lib/selenium/webdriver/devtools/css.rb +0 -165
  97. data/lib/selenium/webdriver/devtools/database.rb +0 -64
  98. data/lib/selenium/webdriver/devtools/debugger.rb +0 -229
  99. data/lib/selenium/webdriver/devtools/device_orientation.rb +0 -53
  100. data/lib/selenium/webdriver/devtools/dom.rb +0 -320
  101. data/lib/selenium/webdriver/devtools/domdebugger.rb +0 -93
  102. data/lib/selenium/webdriver/devtools/domsnapshot.rb +0 -65
  103. data/lib/selenium/webdriver/devtools/domstorage.rb +0 -79
  104. data/lib/selenium/webdriver/devtools/emulation.rb +0 -180
  105. data/lib/selenium/webdriver/devtools/fetch.rb +0 -97
  106. data/lib/selenium/webdriver/devtools/headless_experimental.rb +0 -61
  107. data/lib/selenium/webdriver/devtools/heap_profiler.rb +0 -107
  108. data/lib/selenium/webdriver/devtools/indexed_db.rb +0 -100
  109. data/lib/selenium/webdriver/devtools/input.rb +0 -140
  110. data/lib/selenium/webdriver/devtools/inspector.rb +0 -55
  111. data/lib/selenium/webdriver/devtools/io.rb +0 -59
  112. data/lib/selenium/webdriver/devtools/layer_tree.rb +0 -95
  113. data/lib/selenium/webdriver/devtools/log.rb +0 -66
  114. data/lib/selenium/webdriver/devtools/media.rb +0 -57
  115. data/lib/selenium/webdriver/devtools/memory.rb +0 -86
  116. data/lib/selenium/webdriver/devtools/network.rb +0 -228
  117. data/lib/selenium/webdriver/devtools/overlay.rb +0 -157
  118. data/lib/selenium/webdriver/devtools/page.rb +0 -374
  119. data/lib/selenium/webdriver/devtools/performance.rb +0 -63
  120. data/lib/selenium/webdriver/devtools/profiler.rb +0 -111
  121. data/lib/selenium/webdriver/devtools/runtime.rb +0 -193
  122. data/lib/selenium/webdriver/devtools/schema.rb +0 -46
  123. data/lib/selenium/webdriver/devtools/security.rb +0 -71
  124. data/lib/selenium/webdriver/devtools/service_worker.rb +0 -116
  125. data/lib/selenium/webdriver/devtools/storage.rb +0 -95
  126. data/lib/selenium/webdriver/devtools/system_info.rb +0 -50
  127. data/lib/selenium/webdriver/devtools/target.rb +0 -141
  128. data/lib/selenium/webdriver/devtools/tethering.rb +0 -55
  129. data/lib/selenium/webdriver/devtools/tracing.rb +0 -76
  130. data/lib/selenium/webdriver/devtools/web_audio.rb +0 -70
  131. data/lib/selenium/webdriver/devtools/web_authn.rb +0 -94
  132. data/lib/selenium/webdriver/edge_html/options.rb +0 -84
  133. data/lib/selenium/webdriver/edge_html/service.rb +0 -47
@@ -32,7 +32,7 @@ module Selenium
32
32
 
33
33
  private
34
34
 
35
- # Note: This processing is deprecated
35
+ # NOTE: This processing is deprecated
36
36
  def extract_service_args(driver_opts)
37
37
  driver_args = super
38
38
  driver_opts = driver_opts.dup
@@ -28,8 +28,7 @@ module Selenium
28
28
  #
29
29
 
30
30
  class Driver < WebDriver::Driver
31
- include DriverExtensions::HasWebStorage
32
- include DriverExtensions::TakesScreenshot
31
+ EXTENSIONS = [DriverExtensions::HasWebStorage].freeze
33
32
 
34
33
  def browser
35
34
  :internet_explorer
@@ -41,17 +41,7 @@ module Selenium
41
41
  use_per_process_proxy: 'ie.usePerProcessProxy',
42
42
  validate_cookie_document_type: 'ie.validateCookieDocumentType'
43
43
  }.freeze
44
- BROWSER = 'internet_explorer'
45
-
46
- CAPABILITIES.each_key do |key|
47
- define_method key do
48
- @options[key]
49
- end
50
-
51
- define_method "#{key}=" do |value|
52
- @options[key] = value
53
- end
54
- end
44
+ BROWSER = 'internet explorer'
55
45
 
56
46
  attr_reader :args
57
47
 
@@ -32,13 +32,15 @@ module Selenium
32
32
 
33
33
  private
34
34
 
35
- # Note: This processing is deprecated
35
+ # NOTE: This processing is deprecated
36
36
  def extract_service_args(driver_opts)
37
37
  driver_args = super
38
38
  driver_opts = driver_opts.dup
39
39
  driver_args << "--log-level=#{driver_opts[:log_level].to_s.upcase}" if driver_opts.key?(:log_level)
40
40
  driver_args << "--log-file=#{driver_opts[:log_file]}" if driver_opts.key?(:log_file)
41
- driver_args << "--implementation=#{driver_opts[:implementation].to_s.upcase}" if driver_opts.key?(:implementation)
41
+ if driver_opts.key?(:implementation)
42
+ driver_args << "--implementation=#{driver_opts[:implementation].to_s.upcase}"
43
+ end
42
44
  driver_args << "--host=#{driver_opts[:host]}" if driver_opts.key?(:host)
43
45
  driver_args << "--extract_path=#{driver_opts[:extract_path]}" if driver_opts.key?(:extract_path)
44
46
  driver_args << "--silent" if driver_opts[:silent] == true
@@ -35,7 +35,7 @@ module Selenium
35
35
  # @api private
36
36
  #
37
37
 
38
- def initialize(http_client: nil, url:)
38
+ def initialize(url:, http_client: nil)
39
39
  uri = url.is_a?(URI) ? url : URI.parse(url)
40
40
  uri.path += '/' unless uri.path.end_with?('/')
41
41
 
@@ -49,7 +49,7 @@ module Selenium
49
49
  #
50
50
 
51
51
  def create_session(capabilities)
52
- response = execute(:new_session, {}, {capabilities: {firstMatch: [capabilities]}})
52
+ response = execute(:new_session, {}, prepare_capabilities_payload(capabilities))
53
53
 
54
54
  @session_id = response['sessionId']
55
55
  capabilities = response['capabilities']
@@ -57,6 +57,17 @@ module Selenium
57
57
  raise Error::WebDriverError, 'no sessionId in returned payload' unless @session_id
58
58
 
59
59
  @capabilities = Capabilities.json_create(capabilities)
60
+
61
+ case @capabilities[:browser_name]
62
+ when 'chrome'
63
+ extend(WebDriver::Chrome::Features)
64
+ when 'firefox'
65
+ extend(WebDriver::Firefox::Features)
66
+ when 'msedge'
67
+ extend(WebDriver::Edge::Features)
68
+ when 'Safari', 'Safari Technology Preview'
69
+ extend(WebDriver::Safari::Features)
70
+ end
60
71
  end
61
72
 
62
73
  #
@@ -70,7 +81,7 @@ module Selenium
70
81
  def browser
71
82
  @browser ||= begin
72
83
  name = @capabilities.browser_name
73
- name ? name.tr(' ', '_').to_sym : 'unknown'
84
+ name ? name.tr(' ', '_').downcase.to_sym : 'unknown'
74
85
  end
75
86
  end
76
87
 
@@ -207,7 +218,10 @@ module Selenium
207
218
  end
208
219
 
209
220
  def window_size(handle = :current)
210
- raise Error::UnsupportedOperationError, 'Switch to desired window before getting its size' unless handle == :current
221
+ unless handle == :current
222
+ raise Error::UnsupportedOperationError,
223
+ 'Switch to desired window before getting its size'
224
+ end
211
225
 
212
226
  data = execute :get_window_rect
213
227
  Dimension.new data['width'], data['height']
@@ -218,7 +232,10 @@ module Selenium
218
232
  end
219
233
 
220
234
  def maximize_window(handle = :current)
221
- raise Error::UnsupportedOperationError, 'Switch to desired window before changing its size' unless handle == :current
235
+ unless handle == :current
236
+ raise Error::UnsupportedOperationError,
237
+ 'Switch to desired window before changing its size'
238
+ end
222
239
 
223
240
  execute :maximize_window
224
241
  end
@@ -251,6 +268,10 @@ module Selenium
251
268
  execute :take_screenshot
252
269
  end
253
270
 
271
+ def element_screenshot(element)
272
+ execute :take_element_screenshot, id: element.ref
273
+ end
274
+
254
275
  #
255
276
  # HTML 5
256
277
  #
@@ -303,22 +324,6 @@ module Selenium
303
324
  execute_script('return sessionStorage.length')
304
325
  end
305
326
 
306
- def location
307
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting location'
308
- end
309
-
310
- def set_location(_lat, _lon, _alt)
311
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting location'
312
- end
313
-
314
- def network_connection
315
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting network connection'
316
- end
317
-
318
- def network_connection=(_type)
319
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting network connection'
320
- end
321
-
322
327
  #
323
328
  # javascript execution
324
329
  #
@@ -389,6 +394,10 @@ module Selenium
389
394
  execute :release_actions
390
395
  end
391
396
 
397
+ def print_page(options = {})
398
+ execute :print_page, {}, {options: options}
399
+ end
400
+
392
401
  def click_element(element)
393
402
  execute :element_click, id: element
394
403
  end
@@ -428,14 +437,6 @@ module Selenium
428
437
  'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }', form.as_json)
429
438
  end
430
439
 
431
- def screen_orientation=(orientation)
432
- execute :set_screen_orientation, {}, {orientation: orientation}
433
- end
434
-
435
- def screen_orientation
436
- execute :get_screen_orientation
437
- end
438
-
439
440
  #
440
441
  # element properties
441
442
  #
@@ -449,10 +450,22 @@ module Selenium
449
450
  execute_atom :getAttribute, element, name
450
451
  end
451
452
 
453
+ def element_dom_attribute(element, name)
454
+ execute :get_element_attribute, id: element.ref, name: name
455
+ end
456
+
452
457
  def element_property(element, name)
453
458
  execute :get_element_property, id: element.ref, name: name
454
459
  end
455
460
 
461
+ def element_aria_role(element)
462
+ execute :get_element_aria_role, id: element.ref
463
+ end
464
+
465
+ def element_aria_label(element)
466
+ execute :get_element_aria_label, id: element.ref
467
+ end
468
+
456
469
  def element_value(element)
457
470
  element_property element, 'value'
458
471
  end
@@ -589,6 +602,11 @@ module Selenium
589
602
  id['ELEMENT'] || id['element-6066-11e4-a52e-4f735466cecf']
590
603
  end
591
604
 
605
+ def prepare_capabilities_payload(capabilities)
606
+ capabilities = {alwaysMatch: capabilities} if !capabilities['alwaysMatch'] && !capabilities['firstMatch']
607
+ {capabilities: capabilities}
608
+ end
609
+
592
610
  def convert_locator(how, what)
593
611
  how = SearchContext::FINDERS[how.to_sym] || how
594
612
 
@@ -616,7 +634,7 @@ module Selenium
616
634
  [how, what]
617
635
  end
618
636
 
619
- ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]\(\)])/.freeze
637
+ ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/.freeze
620
638
  UNICODE_CODE_POINT = 30
621
639
 
622
640
  # Escapes invalid characters in CSS selector.
@@ -40,22 +40,15 @@ module Selenium
40
40
  :unhandled_prompt_behavior,
41
41
  :strict_file_interactability,
42
42
 
43
- # remote-specific
44
- :remote_session_id,
45
-
46
- # TODO: (AR) deprecate compatibility with OSS-capabilities
47
- :implicit_timeout,
48
- :page_load_timeout,
49
- :script_timeout
43
+ # remote-specific (webdriver.remote.sessionid)
44
+ :remote_session_id
50
45
  ].freeze
51
46
 
52
- KNOWN.each do |key|
47
+ (KNOWN - %i[proxy timeouts]).each do |key|
53
48
  define_method key do
54
49
  @capabilities.fetch(key)
55
50
  end
56
51
 
57
- next if key == :proxy
58
-
59
52
  define_method "#{key}=" do |value|
60
53
  @capabilities[key] = value
61
54
  end
@@ -82,39 +75,22 @@ module Selenium
82
75
  end
83
76
 
84
77
  def edge(opts = {})
85
- edge_html(opts)
86
- end
87
-
88
- def edge_html(opts = {})
89
- new({
90
- browser_name: 'MicrosoftEdge',
91
- platform_name: :windows
92
- }.merge(opts))
93
- end
94
-
95
- def edge_chrome(opts = {})
96
78
  new({
97
79
  browser_name: 'MicrosoftEdge'
98
80
  }.merge(opts))
99
81
  end
82
+ alias_method :microsoftedge, :edge
100
83
 
101
84
  def firefox(opts = {})
102
- opts[:browser_version] = opts.delete(:version) if opts.key?(:version)
103
- opts[:platform_name] = opts.delete(:platform) if opts.key?(:platform)
104
- opts[:timeouts] = {}
105
- opts[:timeouts]['implicit'] = opts.delete(:implicit_timeout) if opts.key?(:implicit_timeout)
106
- opts[:timeouts]['pageLoad'] = opts.delete(:page_load_timeout) if opts.key?(:page_load_timeout)
107
- opts[:timeouts]['script'] = opts.delete(:script_timeout) if opts.key?(:script_timeout)
108
- opts.delete(:timeouts) if opts[:timeouts].empty?
109
- new({browser_name: 'firefox'}.merge(opts))
85
+ new({
86
+ browser_name: 'firefox'
87
+ }.merge(opts))
110
88
  end
111
-
112
89
  alias_method :ff, :firefox
113
90
 
114
91
  def safari(opts = {})
115
92
  new({
116
- browser_name: 'safari',
117
- platform_name: :mac
93
+ browser_name: Selenium::WebDriver::Safari.technology_preview? ? "Safari Technology Preview" : 'safari'
118
94
  }.merge(opts))
119
95
  end
120
96
 
@@ -132,19 +108,21 @@ module Selenium
132
108
  end
133
109
  alias_method :ie, :internet_explorer
134
110
 
111
+ def always_match(capabilities)
112
+ new(always_match: capabilities)
113
+ end
114
+
115
+ def first_match(*capabilities)
116
+ new(first_match: capabilities)
117
+ end
118
+
135
119
  #
136
120
  # @api private
137
121
  #
138
122
 
139
123
  def json_create(data)
140
124
  data = data.dup
141
-
142
125
  caps = new
143
- caps.browser_name = data.delete('browserName') if data.key?('browserName')
144
- caps.browser_version = data.delete('browserVersion') if data.key?('browserVersion')
145
- caps.platform_name = data.delete('platformName') if data.key?('platformName')
146
- caps.accept_insecure_certs = data.delete('acceptInsecureCerts') if data.key?('acceptInsecureCerts')
147
- caps.page_load_strategy = data.delete('pageLoadStrategy') if data.key?('pageLoadStrategy')
148
126
 
149
127
  process_timeouts(caps, data.delete('timeouts'))
150
128
 
@@ -154,7 +132,14 @@ module Selenium
154
132
  end
155
133
 
156
134
  # Remote Server Specific
157
- caps[:remote_session_id] = data.delete('webdriver.remote.sessionid') if data.key?('webdriver.remote.sessionid')
135
+ if data.key?('webdriver.remote.sessionid')
136
+ caps[:remote_session_id] = data.delete('webdriver.remote.sessionid')
137
+ end
138
+
139
+ KNOWN.each do |cap|
140
+ data_value = camel_case(cap)
141
+ caps[cap] = data.delete(data_value) if data.key?(data_value)
142
+ end
158
143
 
159
144
  # any remaining pairs will be added as is, with no conversion
160
145
  caps.merge!(data)
@@ -162,6 +147,10 @@ module Selenium
162
147
  caps
163
148
  end
164
149
 
150
+ def camel_case(str_or_sym)
151
+ str_or_sym.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
152
+ end
153
+
165
154
  private
166
155
 
167
156
  def process_timeouts(caps, timeouts)
@@ -185,8 +174,9 @@ module Selenium
185
174
  #
186
175
 
187
176
  def initialize(opts = {})
188
- @capabilities = opts
189
- self.proxy = opts.delete(:proxy)
177
+ @capabilities = {}
178
+ self.proxy = opts.delete(:proxy) if opts[:proxy]
179
+ @capabilities.merge!(opts)
190
180
  end
191
181
 
192
182
  #
@@ -211,6 +201,10 @@ module Selenium
211
201
  end
212
202
  end
213
203
 
204
+ def proxy
205
+ @capabilities.fetch(:proxy)
206
+ end
207
+
214
208
  def proxy=(proxy)
215
209
  case proxy
216
210
  when Hash
@@ -222,33 +216,46 @@ module Selenium
222
216
  end
223
217
  end
224
218
 
219
+ def timeouts
220
+ @capabilities[:timeouts] ||= {}
221
+ end
222
+
223
+ def timeouts=(timeouts)
224
+ @capabilities[:timeouts] = timeouts
225
+ end
226
+
227
+ def implicit_timeout
228
+ timeouts[:implicit]
229
+ end
230
+
231
+ def implicit_timeout=(timeout)
232
+ timeouts[:implicit] = timeout
233
+ end
234
+
235
+ def page_load_timeout
236
+ timeouts[:page_load] || timeouts[:pageLoad]
237
+ end
238
+
239
+ def page_load_timeout=(timeout)
240
+ timeouts[:page_load] = timeout
241
+ end
242
+
243
+ def script_timeout
244
+ timeouts[:script]
245
+ end
246
+
247
+ def script_timeout=(timeout)
248
+ timeouts[:script] = timeout
249
+ end
250
+
225
251
  #
226
252
  # @api private
227
253
  #
228
254
 
229
255
  def as_json(*)
230
- hash = {}
231
-
232
- @capabilities.each do |key, value|
233
- case key
234
- when :platform
235
- hash['platform'] = value.to_s.upcase
236
- when :proxy
237
- if value
238
- hash['proxy'] = value.as_json
239
- hash['proxy']['proxyType'] &&= hash['proxy']['proxyType'].downcase
240
- hash['proxy']['noProxy'] = hash['proxy']['noProxy'].split(', ') if hash['proxy']['noProxy'].is_a?(String)
241
- end
242
- when String
243
- hash[key.to_s] = value
244
- when Symbol
245
- hash[camel_case(key.to_s)] = value
246
- else
247
- raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class} / #{value.inspect}"
248
- end
256
+ @capabilities.each_with_object({}) do |(key, value), hash|
257
+ hash[convert_key(key)] = process_capabilities(key, value, hash)
249
258
  end
250
-
251
- hash
252
259
  end
253
260
 
254
261
  def to_json(*)
@@ -269,10 +276,44 @@ module Selenium
269
276
 
270
277
  private
271
278
 
272
- def camel_case(str)
273
- str.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
279
+ def process_capabilities(key, value, hash)
280
+ case value
281
+ when Array
282
+ value.map { |v| process_capabilities(key, v, hash) }
283
+ when Hash
284
+ value.each_with_object({}) do |(k, v), h|
285
+ h[convert_key(k)] = process_capabilities(k, v, h)
286
+ end
287
+ when Capabilities, Options
288
+ value.as_json
289
+ else
290
+ convert_value(key, value)
291
+ end
274
292
  end
275
293
 
294
+ def convert_key(key)
295
+ case key
296
+ when String
297
+ key.to_s
298
+ when Symbol
299
+ self.class.camel_case(key)
300
+ else
301
+ raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"
302
+ end
303
+ end
304
+
305
+ def convert_value(key, value)
306
+ case key
307
+ when :platform
308
+ value.to_s.upcase
309
+ when :proxy
310
+ value&.as_json
311
+ when :unhandled_prompt_behavior
312
+ value.is_a?(Symbol) ? value.to_s.tr('_', ' ') : value
313
+ else
314
+ value
315
+ end
316
+ end
276
317
  end # Capabilities
277
318
  end # Remote
278
319
  end # WebDriver