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
@@ -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