selenium-webdriver 3.142.7 → 4.3.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 (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
@@ -40,13 +40,17 @@ module Selenium
40
40
  :close,
41
41
  :debug, :debug?,
42
42
  :info, :info?,
43
- :warn, :warn?,
43
+ :warn?,
44
44
  :error, :error?,
45
45
  :fatal, :fatal?,
46
46
  :level, :level=
47
47
 
48
- def initialize
49
- @logger = create_logger($stdout)
48
+ #
49
+ # @param [String] progname Allow child projects to use Selenium's Logger pattern
50
+ #
51
+ def initialize(progname = 'Selenium')
52
+ @logger = create_logger(progname)
53
+ @ignored = []
50
54
  end
51
55
 
52
56
  #
@@ -73,28 +77,63 @@ module Selenium
73
77
  @logger.instance_variable_get(:@logdev).dev
74
78
  end
75
79
 
80
+ #
81
+ # Will not log the provided ID.
82
+ #
83
+ # @param [Array, Symbol] id
84
+ #
85
+ def ignore(id)
86
+ Array(id).each { |ignore| @ignored << ignore }
87
+ end
88
+
89
+ #
90
+ # Overrides default #warn to skip ignored messages by provided id
91
+ #
92
+ # @param [String] message
93
+ # @param [Symbol, Array<Sybmol>] id
94
+ # @yield see #deprecate
95
+ #
96
+ def warn(message, id: [])
97
+ id = Array(id)
98
+ return if (@ignored & id).any?
99
+
100
+ msg = id.empty? ? message : "[#{id.map(&:inspect).join(', ')}] #{message} "
101
+ msg += " #{yield}" if block_given?
102
+
103
+ @logger.warn { msg }
104
+ end
105
+
76
106
  #
77
107
  # Marks code as deprecated with/without replacement.
78
108
  #
79
109
  # @param [String] old
80
110
  # @param [String, nil] new
111
+ # @param [Symbol, Array<Symbol>] id
112
+ # @param [String] reference
113
+ # @yield appends additional message to end of provided template
81
114
  #
82
- def deprecate(old, new = nil)
83
- message = +"[DEPRECATION] #{old} is deprecated"
115
+ def deprecate(old, new = nil, id: [], reference: '', &block)
116
+ id = Array(id)
117
+ return if @ignored.include?(:deprecations) || (@ignored & id).any?
118
+
119
+ ids = id.empty? ? '' : "[#{id.map(&:inspect).join(', ')}] "
120
+
121
+ message = +"[DEPRECATION] #{ids}#{old} is deprecated"
84
122
  message << if new
85
123
  ". Use #{new} instead."
86
124
  else
87
- ' and will be removed in the next releases.'
125
+ ' and will be removed in a future release.'
88
126
  end
127
+ message << " See explanation for this deprecation: #{reference}." unless reference.empty?
89
128
 
90
- warn message
129
+ warn message, &block
91
130
  end
92
131
 
93
132
  private
94
133
 
95
- def create_logger(output)
96
- logger = ::Logger.new(output)
97
- logger.progname = 'Selenium'
134
+ def create_logger(name)
135
+ logger = ::Logger.new($stdout)
136
+ logger.progname = name
98
137
  logger.level = default_level
99
138
  logger.formatter = proc do |severity, time, progname, msg|
100
139
  "#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n"
@@ -104,11 +143,7 @@ module Selenium
104
143
  end
105
144
 
106
145
  def default_level
107
- if $DEBUG || ENV.key?('DEBUG')
108
- :debug
109
- else
110
- :warn
111
- end
146
+ $DEBUG || ENV.key?('DEBUG') ? :debug : :warn
112
147
  end
113
148
  end # Logger
114
149
  end # WebDriver
@@ -36,6 +36,7 @@ module Selenium
36
36
  # @option opts [String] :value A value
37
37
  # @option opts [String] :path ('/') A path
38
38
  # @option opts [String] :secure (false) A boolean
39
+ # @option opts [String] :same_site (Strict or Lax) currently supported only in chrome 80+ versions
39
40
  # @option opts [Time,DateTime,Numeric,nil] :expires (nil) Expiry date, either as a Time, DateTime, or seconds since epoch.
40
41
  #
41
42
  # @raise [ArgumentError] if :name or :value is not specified
@@ -45,9 +46,15 @@ module Selenium
45
46
  raise ArgumentError, 'name is required' unless opts[:name]
46
47
  raise ArgumentError, 'value is required' unless opts[:value]
47
48
 
48
- opts[:path] ||= '/'
49
+ # NOTE: This is required because of https://bugs.chromium.org/p/chromedriver/issues/detail?id=3732
49
50
  opts[:secure] ||= false
50
51
 
52
+ same_site = opts.delete(:same_site)
53
+ opts[:sameSite] = same_site if same_site
54
+
55
+ http_only = opts.delete(:http_only)
56
+ opts[:httpOnly] = http_only if http_only
57
+
51
58
  obj = opts.delete(:expires)
52
59
  opts[:expiry] = seconds_from(obj).to_i if obj
53
60
 
@@ -62,7 +69,7 @@ module Selenium
62
69
  #
63
70
 
64
71
  def cookie_named(name)
65
- all_cookies.find { |c| c[:name] == name }
72
+ convert_cookie(@bridge.cookie(name))
66
73
  end
67
74
 
68
75
  #
@@ -97,42 +104,6 @@ module Selenium
97
104
  @timeouts ||= Timeouts.new(@bridge)
98
105
  end
99
106
 
100
- #
101
- # @api beta This API may be changed or removed in a future release.
102
- #
103
-
104
- def logs
105
- @logs ||= Logs.new(@bridge)
106
- end
107
-
108
- #
109
- # Create a new top-level browsing context
110
- # https://w3c.github.io/webdriver/#new-window
111
- # @param type [Symbol] Supports two values: :tab and :window.
112
- # Use :tab if you'd like the new window to share an OS-level window
113
- # with the current browsing context.
114
- # Use :window otherwise
115
- # @return [String] The value of the window handle
116
- #
117
- def new_window(type = :tab)
118
- case type
119
- when :tab, :window
120
- result = @bridge.new_window(type)
121
- unless result.key?('handle')
122
- raise UnknownError, "the driver did not return a handle. " \
123
- "The returned result: #{result.inspect}"
124
- end
125
- result['handle']
126
- else
127
- raise ArgumentError, "invalid argument for type. Got: '#{type.inspect}'. " \
128
- "Try :tab or :window"
129
- end
130
- end
131
-
132
- #
133
- # @api beta This API may be changed or removed in a future release.
134
- #
135
-
136
107
  def window
137
108
  @window ||= Window.new(@bridge)
138
109
  end
@@ -169,6 +140,8 @@ module Selenium
169
140
  path: cookie['path'],
170
141
  domain: cookie['domain'] && strip_port(cookie['domain']),
171
142
  expires: cookie['expiry'] && datetime_at(cookie['expiry']),
143
+ same_site: cookie['sameSite'],
144
+ http_only: cookie['httpOnly'],
172
145
  secure: cookie['secure']
173
146
  }
174
147
  end
@@ -19,35 +19,159 @@
19
19
 
20
20
  module Selenium
21
21
  module WebDriver
22
- module Common
23
- class Options
24
- private
25
-
26
- def generate_as_json(value)
27
- if value.respond_to?(:as_json)
28
- value.as_json
29
- elsif value.is_a?(Hash)
30
- value.each_with_object({}) { |(key, val), hash| hash[convert_json_key(key)] = generate_as_json(val) }
31
- elsif value.is_a?(Array)
32
- value.map(&method(:generate_as_json))
33
- elsif value.is_a?(Symbol)
34
- value.to_s
35
- else
36
- value
22
+ class Options
23
+ W3C_OPTIONS = %i[browser_name browser_version platform_name accept_insecure_certs page_load_strategy proxy
24
+ set_window_rect timeouts unhandled_prompt_behavior strict_file_interactability
25
+ web_socket_url].freeze
26
+
27
+ class << self
28
+ attr_reader :driver_path
29
+
30
+ def chrome(**opts)
31
+ Chrome::Options.new(**opts)
32
+ end
33
+
34
+ def firefox(**opts)
35
+ Firefox::Options.new(**opts)
36
+ end
37
+
38
+ def ie(**opts)
39
+ IE::Options.new(**opts)
40
+ end
41
+ alias_method :internet_explorer, :ie
42
+
43
+ def edge(**opts)
44
+ Edge::Options.new(**opts)
45
+ end
46
+ alias_method :microsoftedge, :edge
47
+
48
+ def safari(**opts)
49
+ Safari::Options.new(**opts)
50
+ end
51
+
52
+ def set_capabilities
53
+ (W3C_OPTIONS + self::CAPABILITIES.keys).each do |key|
54
+ next if method_defined? key
55
+
56
+ define_method key do
57
+ @options[key]
58
+ end
59
+
60
+ define_method "#{key}=" do |value|
61
+ @options[key] = value
62
+ end
37
63
  end
38
64
  end
65
+ end
66
+
67
+ attr_accessor :options
68
+
69
+ def initialize(**opts)
70
+ self.class.set_capabilities
71
+
72
+ @options = opts
73
+ @options[:browser_name] = self.class::BROWSER
74
+ end
75
+
76
+ #
77
+ # Add a new option not yet handled by bindings.
78
+ #
79
+ # @example Leave Chrome open when chromedriver is killed
80
+ # options = Selenium::WebDriver::Chrome::Options.new
81
+ # options.add_option(:detach, true)
82
+ #
83
+ # @param [String, Symbol] name Name of the option
84
+ # @param [Boolean, String, Integer] value Value of the option
85
+ #
86
+
87
+ def add_option(name, value = nil)
88
+ @options[name.keys.first] = name.values.first if value.nil? && name.is_a?(Hash)
89
+ @options[name] = value
90
+ end
91
+
92
+ def ==(other)
93
+ return false unless other.is_a? self.class
94
+
95
+ as_json == other.as_json
96
+ end
97
+
98
+ alias_method :eql?, :==
99
+
100
+ #
101
+ # @api private
102
+ #
103
+
104
+ def as_json(*)
105
+ options = @options.dup
106
+
107
+ w3c_options = process_w3c_options(options)
108
+
109
+ self.class::CAPABILITIES.each do |capability_alias, capability_name|
110
+ capability_value = options.delete(capability_alias)
111
+ options[capability_name] = capability_value if !capability_value.nil? && !options.key?(capability_name)
112
+ end
113
+ browser_options = defined?(self.class::KEY) ? {self.class::KEY => options} : options
39
114
 
40
- def convert_json_key(key)
41
- key = camel_case(key) if key.is_a?(Symbol)
42
- return key if key.is_a?(String)
115
+ process_browser_options(browser_options)
116
+ generate_as_json(w3c_options.merge(browser_options))
117
+ end
43
118
 
44
- raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"
119
+ private
120
+
121
+ def w3c?(key)
122
+ W3C_OPTIONS.include?(key) || key.to_s.include?(':')
123
+ end
124
+
125
+ def process_w3c_options(options)
126
+ w3c_options = options.select { |key, _val| w3c?(key) }
127
+ w3c_options[:unhandled_prompt_behavior] &&= w3c_options[:unhandled_prompt_behavior]&.to_s&.tr('_', ' ')
128
+ options.delete_if { |key, _val| w3c?(key) }
129
+ w3c_options
130
+ end
131
+
132
+ def process_browser_options(_browser_options)
133
+ nil
134
+ end
135
+
136
+ def camelize?(_key)
137
+ true
138
+ end
139
+
140
+ def generate_as_json(value, camelize_keys: true)
141
+ if value.is_a?(Hash)
142
+ process_json_hash(value, camelize_keys)
143
+ elsif value.respond_to?(:as_json)
144
+ value.as_json
145
+ elsif value.is_a?(Array)
146
+ value.map { |val| generate_as_json(val, camelize_keys: camelize_keys) }
147
+ elsif value.is_a?(Symbol)
148
+ value.to_s
149
+ else
150
+ value
45
151
  end
152
+ end
46
153
 
47
- def camel_case(str)
48
- str.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
154
+ def process_json_hash(value, camelize_keys)
155
+ value.each_with_object({}) do |(key, val), hash|
156
+ next if val.respond_to?(:empty?) && val.empty?
157
+
158
+ camelize = camelize_keys ? camelize?(key) : false
159
+ key = convert_json_key(key, camelize: camelize)
160
+ hash[key] = generate_as_json(val, camelize_keys: camelize)
49
161
  end
50
- end # Options
51
- end # Common
162
+ end
163
+
164
+ def convert_json_key(key, camelize: true)
165
+ key = key.to_s if key.is_a?(Symbol)
166
+ key = camel_case(key) if camelize
167
+ return key if key.is_a?(String)
168
+
169
+ raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"
170
+ end
171
+
172
+ def camel_case(str)
173
+ str.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
174
+ end
175
+ end # Options
52
176
  end # WebDriver
53
177
  end # Selenium
@@ -57,6 +57,8 @@ module Selenium
57
57
  :jenkins
58
58
  elsif ENV['APPVEYOR']
59
59
  :appveyor
60
+ elsif ENV['GITHUB_ACTIONS']
61
+ :github
60
62
  end
61
63
  end
62
64
 
@@ -95,11 +97,14 @@ module Selenium
95
97
  def wsl?
96
98
  return false unless linux?
97
99
 
98
- File.read('/proc/version').include?('Microsoft')
100
+ File.read('/proc/version').downcase.include?('microsoft')
101
+ rescue Errno::EACCES
102
+ # the file cannot be accessed on Linux on DeX
103
+ false
99
104
  end
100
105
 
101
106
  def cygwin?
102
- RUBY_PLATFORM =~ /cygwin/
107
+ RUBY_PLATFORM.include?('cygwin')
103
108
  !Regexp.last_match.nil?
104
109
  end
105
110
 
@@ -172,9 +177,9 @@ module Selenium
172
177
 
173
178
  def find_in_program_files(*binary_names)
174
179
  paths = [
175
- ENV['PROGRAMFILES'] || '\\Program Files',
176
- ENV['ProgramFiles(x86)'] || '\\Program Files (x86)',
177
- ENV['ProgramW6432'] || '\\Program Files'
180
+ ENV.fetch('PROGRAMFILES', '\\Program Files'),
181
+ ENV.fetch('ProgramFiles(x86)', '\\Program Files (x86)'),
182
+ ENV.fetch('ProgramW6432', '\\Program Files')
178
183
  ]
179
184
 
180
185
  paths.each do |root|
@@ -32,12 +32,10 @@ module Selenium
32
32
 
33
33
  def self.free?(port)
34
34
  Platform.interfaces.each do |host|
35
- begin
36
- TCPServer.new(host, port).close
37
- rescue *IGNORED_ERRORS => ex
38
- WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{ex.message})")
39
- # ignored - some machines appear unable to bind to some of their interfaces
40
- end
35
+ TCPServer.new(host, port).close
36
+ rescue *IGNORED_ERRORS => e
37
+ WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})")
38
+ # ignored - some machines appear unable to bind to some of their interfaces
41
39
  end
42
40
 
43
41
  true
@@ -31,8 +31,16 @@ module Selenium
31
31
  base.extend ClassMethods
32
32
  end
33
33
 
34
+ def self.decoded(json)
35
+ JSON.parse(json).fetch('zip')
36
+ end
37
+
38
+ def encoded
39
+ Zipper.zip(layout_on_disk)
40
+ end
41
+
34
42
  def as_json(*)
35
- {"zip" => Zipper.zip(layout_on_disk)}
43
+ {"zip" => encoded}
36
44
  end
37
45
 
38
46
  def to_json(*)
@@ -63,18 +71,12 @@ module Selenium
63
71
 
64
72
  module ClassMethods
65
73
  def from_json(json)
66
- data = JSON.parse(json).fetch('zip')
74
+ data = decoded(json)
67
75
 
68
- # can't use Tempfile here since it doesn't support File::BINARY mode on 1.8
69
- # can't use Dir.mktmpdir(&blk) because of http://jira.codehaus.org/browse/JRUBY-4082
70
- tmp_dir = Dir.mktmpdir
71
- begin
72
- zip_path = File.join(tmp_dir, "webdriver-profile-duplicate-#{json.hash}.zip")
76
+ Tempfile.create do |zip_path|
73
77
  File.open(zip_path, 'wb') { |zip_file| zip_file << Base64.decode64(data) }
74
78
 
75
79
  new Zipper.unzip(zip_path)
76
- ensure
77
- FileUtils.rm_rf tmp_dir
78
80
  end
79
81
  end
80
82
  end # ClassMethods
@@ -127,7 +127,10 @@ module Selenium
127
127
  end
128
128
 
129
129
  def type=(type)
130
- raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}" unless TYPES.key? type
130
+ unless TYPES.key? type
131
+ raise ArgumentError,
132
+ "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}"
133
+ end
131
134
 
132
135
  if defined?(@type) && type != @type
133
136
  raise ArgumentError, "incompatible proxy type #{type.inspect} (already set to #{@type.inspect})"
@@ -138,10 +141,10 @@ module Selenium
138
141
 
139
142
  def as_json(*)
140
143
  json_result = {
141
- 'proxyType' => TYPES[type],
144
+ 'proxyType' => TYPES[type].downcase,
142
145
  'ftpProxy' => ftp,
143
146
  'httpProxy' => http,
144
- 'noProxy' => no_proxy,
147
+ 'noProxy' => no_proxy.is_a?(String) ? no_proxy.split(', ') : no_proxy,
145
148
  'proxyAutoconfigUrl' => pac,
146
149
  'sslProxy' => ssl,
147
150
  'autodetect' => auto_detect,
@@ -30,6 +30,7 @@ module Selenium
30
30
  link_text: 'link text',
31
31
  name: 'name',
32
32
  partial_link_text: 'partial link text',
33
+ relative: 'relative',
33
34
  tag_name: 'tag name',
34
35
  xpath: 'xpath'
35
36
  }.freeze
@@ -59,10 +60,7 @@ module Selenium
59
60
  by = FINDERS[how.to_sym]
60
61
  raise ArgumentError, "cannot find element by #{how.inspect}" unless by
61
62
 
62
- bridge.find_element_by by, what.to_s, ref
63
- rescue Selenium::WebDriver::Error::TimeoutError
64
- # Implicit Wait times out in Edge
65
- raise Selenium::WebDriver::Error::NoSuchElementError
63
+ bridge.find_element_by by, what, ref
66
64
  end
67
65
 
68
66
  #
@@ -77,10 +75,7 @@ module Selenium
77
75
  by = FINDERS[how.to_sym]
78
76
  raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
79
77
 
80
- bridge.find_elements_by by, what.to_s, ref
81
- rescue Selenium::WebDriver::Error::TimeoutError
82
- # Implicit Wait times out in Edge
83
- []
78
+ bridge.find_elements_by by, what, ref
84
79
  end
85
80
 
86
81
  private
@@ -92,7 +87,10 @@ module Selenium
92
87
  when 1
93
88
  arg = args.first
94
89
 
95
- raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift" unless arg.respond_to?(:shift)
90
+ unless arg.respond_to?(:shift)
91
+ raise ArgumentError,
92
+ "expected #{arg.inspect}:#{arg.class} to respond to #shift"
93
+ end
96
94
 
97
95
  # this will be a single-entry hash, so use #shift over #first or #[]
98
96
  arr = arg.dup.shift