selenium-webdriver 4.0.0.alpha6 → 4.0.0.beta4

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 (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
@@ -0,0 +1,30 @@
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 DriverExtensions
23
+ module HasLogs
24
+ def logs
25
+ @logs ||= Logs.new(@bridge)
26
+ end
27
+ end # HasLogs
28
+ end # DriverExtensions
29
+ end # WebDriver
30
+ end # Selenium
@@ -17,40 +17,19 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
 
20
+ # TODO: Deprecated; Delete after 4.0 release
20
21
  module Selenium
21
22
  module WebDriver
22
23
  module DriverExtensions
23
24
  module HasNetworkConnection
24
25
  def network_connection_type
25
- connection_value = @bridge.network_connection
26
-
27
- connection_type = values_to_type[connection_value]
28
-
29
- # In case the connection type is not recognized return the
30
- # connection value.
31
- connection_type || connection_value
32
- end
33
-
34
- def network_connection_type=(connection_type)
35
- raise ArgumentError, 'Invalid connection type' unless valid_type? connection_type
36
-
37
- connection_value = type_to_values[connection_type]
38
-
39
- @bridge.network_connection = connection_value
40
- end
41
-
42
- private
43
-
44
- def type_to_values
45
- {airplane_mode: 1, wifi: 2, data: 4, all: 6, none: 0}
46
- end
47
-
48
- def values_to_type
49
- type_to_values.invert
26
+ raise Error::UnsupportedOperationError,
27
+ 'The W3C standard does not currently support getting network connection'
50
28
  end
51
29
 
52
- def valid_type?(type)
53
- type_to_values.key? type
30
+ def network_connection_type=(*)
31
+ raise Error::UnsupportedOperationError,
32
+ 'The W3C standard does not currently support setting network connection'
54
33
  end
55
34
  end # HasNetworkConnection
56
35
  end # DriverExtensions
@@ -0,0 +1,67 @@
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 DriverExtensions
23
+ module HasNetworkInterception
24
+
25
+ #
26
+ # Intercepts requests coming from browser allowing
27
+ # to either pass them through like proxy or provide
28
+ # a stubbed response instead.
29
+ #
30
+ # @example Log requests and pass through
31
+ # driver.intercept do |request|
32
+ # puts "#{request.method} #{request.url}"
33
+ # request.continue
34
+ # end
35
+ #
36
+ # @example Stub response for image requests
37
+ # driver.intercept do |request|
38
+ # if request.url.match?(/\.png$/)
39
+ # request.respond(body: File.read('myfile.png'))
40
+ # else
41
+ # request.continue
42
+ # end
43
+ # end
44
+ #
45
+ # @param [#call] block which is called when request is interecepted
46
+ # @yieldparam [DevTools::Request]
47
+ #
48
+
49
+ def intercept
50
+ devtools.network.set_cache_disabled(cache_disabled: true)
51
+ devtools.fetch.on(:request_paused) do |params|
52
+ request = DevTools::Request.new(
53
+ devtools: devtools,
54
+ id: params['requestId'],
55
+ url: params.dig('request', 'url'),
56
+ method: params.dig('request', 'method'),
57
+ headers: params.dig('request', 'headers')
58
+ )
59
+ yield request
60
+ end
61
+ devtools.fetch.enable
62
+ end
63
+
64
+ end # HasNetworkInterception
65
+ end # DriverExtensions
66
+ end # WebDriver
67
+ end # Selenium
@@ -22,6 +22,7 @@ module Selenium
22
22
  module DriverExtensions
23
23
  module HasRemoteStatus
24
24
  def remote_status
25
+ WebDriver.logger.deprecate('#remote_status', '#status')
25
26
  @bridge.status
26
27
  end
27
28
  end # HasRemoteStatus
@@ -19,43 +19,41 @@
19
19
 
20
20
  module Selenium
21
21
  module WebDriver
22
- #
23
- # @api private
24
- #
25
-
26
22
  module DriverExtensions
27
- module Rotatable
28
- ORIENTATIONS = %i[landscape portrait].freeze
29
-
23
+ module PrintsPage
24
+ #
25
+ # Save a page as a PDF to the given path
30
26
  #
31
- # Change the screen orientation
27
+ # @example Save Printed Page
28
+ # driver.save_print_page('../printed_page.pdf')
32
29
  #
33
- # @param [:landscape, :portrait] orientation
30
+ # @param [String] path to where the pdf should be saved
34
31
  #
35
32
  # @api public
36
33
  #
37
34
 
38
- def rotation=(orientation)
39
- unless ORIENTATIONS.include?(orientation)
40
- raise ArgumentError, "expected #{ORIENTATIONS.inspect}, got #{orientation.inspect}"
35
+ def save_print_page(path, **options)
36
+ File.open(path, 'wb') do |file|
37
+ content = Base64.decode64 print_page(options)
38
+ file << content
41
39
  end
42
-
43
- bridge.screen_orientation = orientation.to_s.upcase
44
40
  end
45
- alias_method :rotate, :rotation=
46
41
 
47
42
  #
48
- # Get the current screen orientation
43
+ # Return a Base64 encoded Print Page as a string
49
44
  #
50
- # @return [:landscape, :portrait] orientation
45
+ # @see https://w3c.github.io/webdriver/#print-page
51
46
  #
52
47
  # @api public
53
48
  #
54
49
 
55
- def orientation
56
- bridge.screen_orientation.to_sym.downcase
50
+ def print_page(**options)
51
+ options[:pageRanges] = Array(options.delete(:page_ranges)) || []
52
+ options[:shrinkToFit] = options.delete(:shrink_to_fit) { true }
53
+
54
+ @bridge.print_page(options)
57
55
  end
58
- end # Rotatable
56
+ end # PrintsPage
59
57
  end # DriverExtensions
60
58
  end # WebDriver
61
59
  end # Selenium
@@ -23,6 +23,7 @@ module Selenium
23
23
  ELEMENT_KEY = 'element-6066-11e4-a52e-4f735466cecf'
24
24
 
25
25
  include SearchContext
26
+ include TakesScreenshot
26
27
 
27
28
  #
28
29
  # Creates a new Element
@@ -91,13 +92,18 @@ module Selenium
91
92
  end
92
93
 
93
94
  #
94
- # Get the value of a the given attribute of the element. Will return the current value, even if
95
- # this has been modified after the page has been loaded. More exactly, this method will return
96
- # the value of the given attribute, unless that attribute is not present, in which case the
97
- # value of the property with the same name is returned. If neither value is set, nil is
98
- # returned. The "style" attribute is converted as best can be to a text representation with a
99
- # trailing semi-colon. The following are deemed to be "boolean" attributes, and will
100
- # return either "true" or "false":
95
+ # This method attempts to provide the most likely desired current value for the attribute
96
+ # of the element, even when that desired value is actually a JavaScript property.
97
+ # It is implemented with a custom JavaScript atom. To obtain the exact value of the attribute or property,
98
+ # use #dom_attribute or #property methods respectively.
99
+ #
100
+ # More exactly, this method will return the value of the property with the given name,
101
+ # if it exists. If it does not, then the value of the attribute with the given name is returned.
102
+ # If neither exists, null is returned.
103
+ #
104
+ # The "style" attribute is converted as best can be to a text representation with a trailing semi-colon.
105
+ #
106
+ # The following are deemed to be "boolean" attributes, and will return either "true" or "false":
101
107
  #
102
108
  # async, autofocus, autoplay, checked, compact, complete, controls, declare, defaultchecked,
103
109
  # defaultselected, defer, disabled, draggable, ended, formnovalidate, hidden, indeterminate,
@@ -105,22 +111,46 @@ module Selenium
105
111
  # nowrap, open, paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
106
112
  # selected, spellcheck, truespeed, willvalidate
107
113
  #
108
- # Finally, the following commonly mis-capitalized attribute/property names are evaluated as
109
- # expected:
114
+ # Finally, the following commonly mis-capitalized attribute/property names are evaluated as expected:
110
115
  #
111
- # class, readonly
116
+ # When the value of "class" is requested, the "className" property is returned.
117
+ # When the value of "readonly" is requested, the "readOnly" property is returned.
112
118
  #
113
119
  # @param [String] name attribute name
114
120
  # @return [String, nil] attribute value
115
121
  #
122
+ # @see #dom_attribute
123
+ # @see #property
124
+ #
116
125
 
117
126
  def attribute(name)
118
127
  bridge.element_attribute self, name
119
128
  end
120
129
 
121
130
  #
122
- # Get the value of a the given property with the same name of the element. If the value is not
123
- # set, nil is returned.
131
+ # Gets the value of a declared HTML attribute of this element.
132
+ #
133
+ # As opposed to the #attribute method, this method
134
+ # only returns attributes declared in the element's HTML markup.
135
+ #
136
+ # If the attribute is not set, nil is returned.
137
+ #
138
+ # @param [String] name attribute name
139
+ # @return [String, nil] attribute value
140
+ #
141
+ # @see #attribute
142
+ # @see #property
143
+ #
144
+
145
+ def dom_attribute(name)
146
+ bridge.element_dom_attribute self, name
147
+ end
148
+
149
+ #
150
+ # Gets the value of a JavaScript property of this element
151
+ # This will return the current value,
152
+ # even if this has been modified after the page has been loaded.
153
+ # If the value is not set, nil is returned.
124
154
  #
125
155
  # @param [String] name property name
126
156
  # @return [String, nil] property value
@@ -130,6 +160,26 @@ module Selenium
130
160
  bridge.element_property self, name
131
161
  end
132
162
 
163
+ #
164
+ # Gets the computed WAI-ARIA role of element
165
+ #
166
+ # @return [String]
167
+ #
168
+
169
+ def aria_role
170
+ bridge.element_aria_role self
171
+ end
172
+
173
+ #
174
+ # Gets the computed WAI-ARIA label of element.
175
+ #
176
+ # @return [String]
177
+ #
178
+
179
+ def accessible_name
180
+ bridge.element_aria_label self
181
+ end
182
+
133
183
  #
134
184
  # Get the text content of this element
135
185
  #
@@ -327,6 +377,10 @@ module Selenium
327
377
 
328
378
  tn == 'option' || (tn == 'input' && %w[radio checkbox].include?(type))
329
379
  end
380
+
381
+ def screenshot
382
+ bridge.element_screenshot(self)
383
+ end
330
384
  end # Element
331
385
  end # WebDriver
332
386
  end # Selenium
@@ -26,7 +26,10 @@ module Selenium
26
26
  attr_reader :source
27
27
 
28
28
  def initialize(source)
29
- raise TypeError, "#{source.type} is not a valid input type" unless Interactions::SOURCE_TYPES.include? source.type
29
+ unless Interactions::SOURCE_TYPES.include? source.type
30
+ raise TypeError,
31
+ "#{source.type} is not a valid input type"
32
+ end
30
33
 
31
34
  @source = source
32
35
  end
@@ -108,10 +108,11 @@ module Selenium
108
108
  #
109
109
  # @param [String] old
110
110
  # @param [String, nil] new
111
- # @param [Symbol, Array<Sybmol>] id
111
+ # @param [Symbol, Array<Symbol>] id
112
+ # @param [String] reference
112
113
  # @yield appends additional message to end of provided template
113
114
  #
114
- def deprecate(old, new = nil, id: [], &block)
115
+ def deprecate(old, new = nil, id: [], reference: '', &block)
115
116
  id = Array(id)
116
117
  return if @ignored.include?(:deprecations) || (@ignored & id).any?
117
118
 
@@ -123,6 +124,8 @@ module Selenium
123
124
  else
124
125
  ' and will be removed in a future release.'
125
126
  end
127
+ message << " See explanation for this deprecation: #{reference}." unless reference.empty?
128
+
126
129
  warn message, &block
127
130
  end
128
131
 
@@ -46,12 +46,15 @@ module Selenium
46
46
  raise ArgumentError, 'name is required' unless opts[:name]
47
47
  raise ArgumentError, 'value is required' unless opts[:value]
48
48
 
49
- opts[:path] ||= '/'
49
+ # NOTE: This is required because of https://bugs.chromium.org/p/chromedriver/issues/detail?id=3732
50
50
  opts[:secure] ||= false
51
51
 
52
52
  same_site = opts.delete(:same_site)
53
53
  opts[:sameSite] = same_site if same_site
54
54
 
55
+ http_only = opts.delete(:http_only)
56
+ opts[:httpOnly] = http_only if http_only
57
+
55
58
  obj = opts.delete(:expires)
56
59
  opts[:expiry] = seconds_from(obj).to_i if obj
57
60
 
@@ -106,6 +109,7 @@ module Selenium
106
109
  #
107
110
 
108
111
  def logs
112
+ WebDriver.logger.deprecate('Manager#logs', 'Chrome::Driver#logs')
109
113
  @logs ||= Logs.new(@bridge)
110
114
  end
111
115
 
@@ -174,6 +178,7 @@ module Selenium
174
178
  domain: cookie['domain'] && strip_port(cookie['domain']),
175
179
  expires: cookie['expiry'] && datetime_at(cookie['expiry']),
176
180
  same_site: cookie['sameSite'],
181
+ http_only: cookie['httpOnly'],
177
182
  secure: cookie['secure']
178
183
  }
179
184
  end
@@ -23,19 +23,51 @@ module Selenium
23
23
  W3C_OPTIONS = %i[browser_name browser_version platform_name accept_insecure_certs page_load_strategy proxy
24
24
  set_window_rect timeouts unhandled_prompt_behavior strict_file_interactability].freeze
25
25
 
26
- W3C_OPTIONS.each do |key|
27
- define_method key do
28
- @options[key]
26
+ class << self
27
+ attr_reader :driver_path
28
+
29
+ def chrome(**opts)
30
+ Chrome::Options.new(**opts)
31
+ end
32
+
33
+ def firefox(**opts)
34
+ Firefox::Options.new(**opts)
35
+ end
36
+
37
+ def ie(**opts)
38
+ IE::Options.new(**opts)
39
+ end
40
+ alias_method :internet_explorer, :ie
41
+
42
+ def edge(**opts)
43
+ Edge::Options.new(**opts)
44
+ end
45
+ alias_method :microsoftedge, :edge
46
+
47
+ def safari(**opts)
48
+ Safari::Options.new(**opts)
29
49
  end
30
50
 
31
- define_method "#{key}=" do |value|
32
- @options[key] = value
51
+ def set_capabilities
52
+ (W3C_OPTIONS + self::CAPABILITIES.keys).each do |key|
53
+ next if method_defined? key
54
+
55
+ define_method key do
56
+ @options[key]
57
+ end
58
+
59
+ define_method "#{key}=" do |value|
60
+ @options[key] = value
61
+ end
62
+ end
33
63
  end
34
64
  end
35
65
 
36
66
  attr_accessor :options
37
67
 
38
68
  def initialize(options: nil, **opts)
69
+ self.class.set_capabilities
70
+
39
71
  @options = if options
40
72
  WebDriver.logger.deprecate(":options as keyword for initializing #{self.class}",
41
73
  "custom values directly in #new constructor",
@@ -62,6 +94,14 @@ module Selenium
62
94
  @options[name] = value
63
95
  end
64
96
 
97
+ def ==(other)
98
+ return false unless other.is_a? self.class
99
+
100
+ as_json == other.as_json
101
+ end
102
+
103
+ alias_method :eql?, :==
104
+
65
105
  #
66
106
  # @api private
67
107
  #
@@ -69,28 +109,40 @@ module Selenium
69
109
  def as_json(*)
70
110
  options = @options.dup
71
111
 
72
- w3c_options = options.select { |key, _val| W3C_OPTIONS.include?(key) }
73
- options.delete_if { |key, _val| W3C_OPTIONS.include?(key) }
112
+ w3c_options = process_w3c_options(options)
74
113
 
75
114
  self.class::CAPABILITIES.each do |capability_alias, capability_name|
76
115
  capability_value = options.delete(capability_alias)
77
- options[capability_name] = capability_value unless capability_value.nil?
116
+ options[capability_name] = capability_value if !capability_value.nil? && !options.key?(capability_name)
78
117
  end
79
118
  browser_options = defined?(self.class::KEY) ? {self.class::KEY => options} : options
80
119
 
81
- process_browser_options(browser_options) if private_methods(false).include?(:process_browser_options)
120
+ process_browser_options(browser_options)
82
121
  generate_as_json(w3c_options.merge(browser_options))
83
122
  end
84
123
 
85
124
  private
86
125
 
126
+ def process_w3c_options(options)
127
+ w3c_options = options.select { |key, _val| W3C_OPTIONS.include?(key) }
128
+ w3c_options[:unhandled_prompt_behavior] &&= w3c_options[:unhandled_prompt_behavior]&.to_s&.tr('_', ' ')
129
+ options.delete_if { |key, _val| W3C_OPTIONS.include?(key) }
130
+ w3c_options
131
+ end
132
+
133
+ def process_browser_options(_browser_options)
134
+ nil
135
+ end
136
+
137
+ def camelize?(_key)
138
+ true
139
+ end
140
+
87
141
  def generate_as_json(value, camelize_keys: true)
88
- if value.respond_to?(:as_json)
142
+ if value.is_a?(Hash)
143
+ process_json_hash(value, camelize_keys)
144
+ elsif value.respond_to?(:as_json)
89
145
  value.as_json
90
- elsif value.is_a?(Hash)
91
- value.each_with_object({}) do |(key, val), hash|
92
- hash[convert_json_key(key, camelize: camelize_keys)] = generate_as_json(val, camelize_keys: camelize_keys)
93
- end
94
146
  elsif value.is_a?(Array)
95
147
  value.map { |val| generate_as_json(val, camelize_keys: camelize_keys) }
96
148
  elsif value.is_a?(Symbol)
@@ -100,6 +152,16 @@ module Selenium
100
152
  end
101
153
  end
102
154
 
155
+ def process_json_hash(value, camelize_keys)
156
+ value.each_with_object({}) do |(key, val), hash|
157
+ next if val.respond_to?(:empty?) && val.empty?
158
+
159
+ camelize = camelize_keys ? camelize?(key) : false
160
+ key = convert_json_key(key, camelize: camelize)
161
+ hash[key] = generate_as_json(val, camelize_keys: camelize)
162
+ end
163
+ end
164
+
103
165
  def convert_json_key(key, camelize: true)
104
166
  key = key.to_s if key.is_a?(Symbol)
105
167
  key = camel_case(key) if camelize