selenium-webdriver 4.23.0 → 4.31.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +81 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +1 -1
  5. data/NOTICE +1 -1
  6. data/README.md +1 -1
  7. data/bin/linux/selenium-manager +0 -0
  8. data/bin/macos/selenium-manager +0 -0
  9. data/bin/windows/selenium-manager.exe +0 -0
  10. data/lib/selenium/server.rb +1 -1
  11. data/lib/selenium/webdriver/atoms/findElements.js +50 -118
  12. data/lib/selenium/webdriver/atoms/getAttribute.js +3 -3
  13. data/lib/selenium/webdriver/atoms/isDisplayed.js +2 -2
  14. data/lib/selenium/webdriver/bidi/browsing_context.rb +73 -46
  15. data/lib/selenium/webdriver/bidi/log_handler.rb +4 -2
  16. data/lib/selenium/webdriver/{common/driver_extensions/has_web_storage.rb → bidi/network/cookies.rb} +12 -12
  17. data/lib/selenium/webdriver/{common/html5/shared_web_storage.rb → bidi/network/credentials.rb} +15 -25
  18. data/lib/selenium/webdriver/bidi/network/headers.rb +38 -0
  19. data/lib/selenium/webdriver/bidi/{browsing_context_info.rb → network/intercepted_auth.rb} +11 -8
  20. data/lib/selenium/webdriver/bidi/{navigate_result.rb → network/intercepted_item.rb} +10 -6
  21. data/lib/selenium/webdriver/{common/html5/session_storage.rb → bidi/network/intercepted_request.rb} +34 -26
  22. data/lib/selenium/webdriver/bidi/network/intercepted_response.rb +81 -0
  23. data/lib/selenium/webdriver/bidi/network/url_pattern.rb +65 -0
  24. data/lib/selenium/webdriver/bidi/network.rb +139 -0
  25. data/lib/selenium/webdriver/bidi/struct.rb +2 -4
  26. data/lib/selenium/webdriver/bidi.rb +5 -0
  27. data/lib/selenium/webdriver/chrome/service.rb +1 -0
  28. data/lib/selenium/webdriver/chromium/driver.rb +0 -1
  29. data/lib/selenium/webdriver/common/child_process.rb +20 -14
  30. data/lib/selenium/webdriver/common/driver.rb +12 -10
  31. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +4 -3
  32. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +7 -0
  33. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +7 -0
  34. data/lib/selenium/webdriver/common/error.rb +3 -1
  35. data/lib/selenium/webdriver/common/fedcm/account.rb +4 -5
  36. data/lib/selenium/webdriver/common/logger.rb +3 -3
  37. data/lib/selenium/webdriver/common/manager.rb +1 -1
  38. data/lib/selenium/webdriver/common/network.rb +102 -0
  39. data/lib/selenium/webdriver/common/service.rb +11 -4
  40. data/lib/selenium/webdriver/common/service_manager.rb +1 -0
  41. data/lib/selenium/webdriver/common/takes_screenshot.rb +1 -0
  42. data/lib/selenium/webdriver/common/target_locator.rb +3 -2
  43. data/lib/selenium/webdriver/common/wait.rb +1 -1
  44. data/lib/selenium/webdriver/common.rb +1 -4
  45. data/lib/selenium/webdriver/devtools.rb +7 -5
  46. data/lib/selenium/webdriver/edge/service.rb +1 -1
  47. data/lib/selenium/webdriver/firefox/driver.rb +0 -19
  48. data/lib/selenium/webdriver/firefox/service.rb +1 -0
  49. data/lib/selenium/webdriver/ie/driver.rb +1 -1
  50. data/lib/selenium/webdriver/ie/service.rb +1 -0
  51. data/lib/selenium/webdriver/remote/bidi_bridge.rb +22 -0
  52. data/lib/selenium/webdriver/remote/bridge.rb +3 -53
  53. data/lib/selenium/webdriver/remote/http/common.rb +2 -0
  54. data/lib/selenium/webdriver/remote/http/curb.rb +11 -5
  55. data/lib/selenium/webdriver/remote/response.rb +18 -0
  56. data/lib/selenium/webdriver/remote.rb +1 -1
  57. data/lib/selenium/webdriver/safari/driver.rb +1 -2
  58. data/lib/selenium/webdriver/safari/service.rb +1 -1
  59. data/lib/selenium/webdriver/support/guards.rb +2 -2
  60. data/lib/selenium/webdriver/version.rb +1 -1
  61. data/selenium-webdriver.gemspec +4 -2
  62. metadata +16 -10
  63. data/lib/selenium/webdriver/common/html5/local_storage.rb +0 -59
@@ -0,0 +1,139 @@
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
+ require_relative 'network/url_pattern'
20
+
21
+ module Selenium
22
+ module WebDriver
23
+ class BiDi
24
+ class Network
25
+ EVENTS = {
26
+ before_request: 'network.beforeRequestSent',
27
+ response_started: 'network.responseStarted',
28
+ response_completed: 'network.responseCompleted',
29
+ auth_required: 'network.authRequired',
30
+ fetch_error: 'network.fetchError'
31
+ }.freeze
32
+
33
+ PHASES = {
34
+ before_request: 'beforeRequestSent',
35
+ response_started: 'responseStarted',
36
+ auth_required: 'authRequired'
37
+ }.freeze
38
+
39
+ def initialize(bidi)
40
+ @bidi = bidi
41
+ end
42
+
43
+ def add_intercept(phases: [], contexts: nil, url_patterns: nil, pattern_type: :string)
44
+ url_patterns = url_patterns && pattern_type ? UrlPattern.format_pattern(url_patterns, pattern_type) : nil
45
+ @bidi.send_cmd('network.addIntercept',
46
+ phases: phases,
47
+ contexts: contexts,
48
+ urlPatterns: url_patterns)
49
+ end
50
+
51
+ def remove_intercept(intercept)
52
+ @bidi.send_cmd('network.removeIntercept', intercept: intercept)
53
+ end
54
+
55
+ def continue_with_auth(request_id, username, password)
56
+ @bidi.send_cmd(
57
+ 'network.continueWithAuth',
58
+ request: request_id,
59
+ action: 'provideCredentials',
60
+ credentials: {
61
+ type: 'password',
62
+ username: username,
63
+ password: password
64
+ }
65
+ )
66
+ end
67
+
68
+ def continue_without_auth(request_id)
69
+ @bidi.send_cmd(
70
+ 'network.continueWithAuth',
71
+ request: request_id,
72
+ action: 'default'
73
+ )
74
+ end
75
+
76
+ def cancel_auth(request_id)
77
+ @bidi.send_cmd(
78
+ 'network.continueWithAuth',
79
+ request: request_id,
80
+ action: 'cancel'
81
+ )
82
+ end
83
+
84
+ def continue_request(**args)
85
+ @bidi.send_cmd(
86
+ 'network.continueRequest',
87
+ request: args[:id],
88
+ body: args[:body],
89
+ cookies: args[:cookies],
90
+ headers: args[:headers],
91
+ method: args[:method],
92
+ url: args[:url]
93
+ )
94
+ end
95
+
96
+ def fail_request(request_id)
97
+ @bidi.send_cmd(
98
+ 'network.failRequest',
99
+ request: request_id
100
+ )
101
+ end
102
+
103
+ def continue_response(**args)
104
+ @bidi.send_cmd(
105
+ 'network.continueResponse',
106
+ request: args[:id],
107
+ cookies: args[:cookies],
108
+ credentials: args[:credentials],
109
+ headers: args[:headers],
110
+ reasonPhrase: args[:reason],
111
+ statusCode: args[:status]
112
+ )
113
+ end
114
+
115
+ def provide_response(**args)
116
+ @bidi.send_cmd(
117
+ 'network.provideResponse',
118
+ request: args[:id],
119
+ body: args[:body],
120
+ cookies: args[:cookies],
121
+ headers: args[:headers],
122
+ reasonPhrase: args[:reason],
123
+ statusCode: args[:status]
124
+ )
125
+ end
126
+
127
+ def set_cache_behavior(behavior, *contexts)
128
+ @bidi.send_cmd('network.setCacheBehavior', cacheBehavior: behavior, contexts: contexts)
129
+ end
130
+
131
+ def on(event, &block)
132
+ event = EVENTS[event] if event.is_a?(Symbol)
133
+ @bidi.add_callback(event, &block)
134
+ @bidi.session.subscribe(event)
135
+ end
136
+ end # Network
137
+ end # BiDi
138
+ end # WebDriver
139
+ end # Selenium
@@ -23,7 +23,7 @@ module Selenium
23
23
  class Struct < ::Struct
24
24
  class << self
25
25
  def new(*args, &block)
26
- super(*args) do
26
+ super do
27
27
  define_method(:initialize) do |**kwargs|
28
28
  converted_kwargs = kwargs.transform_keys { |key| self.class.camel_to_snake(key.to_s).to_sym }
29
29
  super(*converted_kwargs.values_at(*self.class.members))
@@ -37,8 +37,6 @@ module Selenium
37
37
  end
38
38
  end
39
39
  end
40
- end
41
-
42
- # BiDi
40
+ end # BiDi
43
41
  end # WebDriver
44
42
  end # Selenium
@@ -25,6 +25,11 @@ module Selenium
25
25
  autoload :LogHandler, 'selenium/webdriver/bidi/log_handler'
26
26
  autoload :BrowsingContext, 'selenium/webdriver/bidi/browsing_context'
27
27
  autoload :Struct, 'selenium/webdriver/bidi/struct'
28
+ autoload :Network, 'selenium/webdriver/bidi/network'
29
+ autoload :InterceptedRequest, 'selenium/webdriver/bidi/network/intercepted_request'
30
+ autoload :InterceptedResponse, 'selenium/webdriver/bidi/network/intercepted_response'
31
+ autoload :InterceptedAuth, 'selenium/webdriver/bidi/network/intercepted_auth'
32
+ autoload :InterceptedItem, 'selenium/webdriver/bidi/network/intercepted_item'
28
33
 
29
34
  def initialize(url:)
30
35
  @ws = WebSocketConnection.new(url: url)
@@ -24,6 +24,7 @@ module Selenium
24
24
  DEFAULT_PORT = 9515
25
25
  EXECUTABLE = 'chromedriver'
26
26
  SHUTDOWN_SUPPORTED = true
27
+ DRIVER_PATH_ENV_KEY = 'SE_CHROMEDRIVER'
27
28
 
28
29
  def log
29
30
  return @log unless @log.is_a? String
@@ -32,7 +32,6 @@ module Selenium
32
32
  DriverExtensions::HasFedCmDialog,
33
33
  DriverExtensions::HasNetworkConditions,
34
34
  DriverExtensions::HasNetworkInterception,
35
- DriverExtensions::HasWebStorage,
36
35
  DriverExtensions::HasLaunching,
37
36
  DriverExtensions::HasPermissions,
38
37
  DriverExtensions::DownloadsFiles,
@@ -64,16 +64,10 @@ module Selenium
64
64
  return unless @pid
65
65
  return if exited?
66
66
 
67
- WebDriver.logger.debug("Sending TERM to process: #{@pid}", id: :process)
68
- terminate(@pid)
69
- poll_for_exit(timeout)
70
-
71
- WebDriver.logger.debug(" -> stopped #{@pid}", id: :process)
72
- rescue TimeoutError, Errno::EINVAL
73
- WebDriver.logger.debug(" -> sending KILL to process: #{@pid}", id: :process)
74
- kill(@pid)
75
- wait
76
- WebDriver.logger.debug(" -> killed #{@pid}", id: :process)
67
+ terminate_and_wait_else_kill(timeout)
68
+ rescue Errno::ECHILD, Errno::ESRCH => e
69
+ # Process exited earlier than terminate/kill could catch
70
+ WebDriver.logger.debug(" -> process: #{@pid} does not exist (#{e.class.name})", id: :process)
77
71
  end
78
72
 
79
73
  def alive?
@@ -91,6 +85,9 @@ module Selenium
91
85
  WebDriver.logger.debug(" -> exit code is #{exit_code.inspect}", id: :process)
92
86
 
93
87
  !!exit_code
88
+ rescue Errno::ECHILD, Errno::ESRCH
89
+ WebDriver.logger.debug(" -> process: #{@pid} already finished", id: :process)
90
+ true
94
91
  end
95
92
 
96
93
  def poll_for_exit(timeout)
@@ -110,20 +107,29 @@ module Selenium
110
107
 
111
108
  private
112
109
 
110
+ def terminate_and_wait_else_kill(timeout)
111
+ WebDriver.logger.debug("Sending TERM to process: #{@pid}", id: :process)
112
+ terminate(@pid)
113
+ poll_for_exit(timeout)
114
+
115
+ WebDriver.logger.debug(" -> stopped #{@pid}", id: :process)
116
+ rescue TimeoutError, Errno::EINVAL
117
+ WebDriver.logger.debug(" -> sending KILL to process: #{@pid}", id: :process)
118
+ kill(@pid)
119
+ wait
120
+ WebDriver.logger.debug(" -> killed #{@pid}", id: :process)
121
+ end
122
+
113
123
  def terminate(pid)
114
124
  Process.kill(SIGTERM, pid)
115
125
  end
116
126
 
117
127
  def kill(pid)
118
128
  Process.kill(SIGKILL, pid)
119
- rescue Errno::ECHILD, Errno::ESRCH
120
- # already dead
121
129
  end
122
130
 
123
131
  def waitpid2(pid, flags = 0)
124
132
  Process.waitpid2(pid, flags)
125
- rescue Errno::ECHILD
126
- # already dead
127
133
  end
128
134
  end # ChildProcess
129
135
  end # WebDriver
@@ -104,15 +104,8 @@ module Selenium
104
104
  # @see Script
105
105
  #
106
106
 
107
- def script(*args)
108
- if args.any?
109
- WebDriver.logger.deprecate('`Driver#script` as an alias for `#execute_script`',
110
- '`Driver#execute_script`',
111
- id: :driver_script)
112
- execute_script(*args)
113
- else
114
- @script ||= WebDriver::Script.new(bridge)
115
- end
107
+ def script
108
+ @script ||= WebDriver::Script.new(bridge)
116
109
  end
117
110
 
118
111
  #
@@ -188,7 +181,7 @@ module Selenium
188
181
  bridge.quit
189
182
  ensure
190
183
  @service_manager&.stop
191
- @devtools&.close
184
+ @devtools&.each_value(&:close)
192
185
  end
193
186
 
194
187
  #
@@ -264,6 +257,15 @@ module Selenium
264
257
  bridge.add_virtual_authenticator(options)
265
258
  end
266
259
 
260
+ #
261
+ # @return [Network]
262
+ # @see Network
263
+ #
264
+
265
+ def network
266
+ @network ||= WebDriver::Network.new(bridge)
267
+ end
268
+
267
269
  #-------------------------------- sugar --------------------------------
268
270
 
269
271
  #
@@ -27,12 +27,13 @@ module Selenium
27
27
  # @return [DevTools]
28
28
  #
29
29
 
30
- def devtools
31
- @devtools ||= begin
30
+ def devtools(target_type: 'page')
31
+ @devtools ||= {}
32
+ @devtools[target_type] ||= begin
32
33
  require 'selenium/devtools'
33
34
  Selenium::DevTools.version ||= devtools_version
34
35
  Selenium::DevTools.load_version
35
- Selenium::WebDriver::DevTools.new(url: devtools_url)
36
+ Selenium::WebDriver::DevTools.new(url: devtools_url, target_type: target_type)
36
37
  end
37
38
  end
38
39
  end # HasDevTools
@@ -57,6 +57,13 @@ module Selenium
57
57
  #
58
58
 
59
59
  def on_log_event(kind, &block)
60
+ if browser == :firefox
61
+ WebDriver.logger.deprecate(
62
+ 'Driver#on_log_event on Firefox',
63
+ 'the script.add_console_message_handler or the script.add_javascript_error_handler methods',
64
+ id: :on_log_event
65
+ )
66
+ end
60
67
  raise Error::WebDriverError, "Don't know how to handle #{kind} events" unless KINDS.include?(kind)
61
68
 
62
69
  enabled = log_listeners[kind].any?
@@ -60,6 +60,13 @@ module Selenium
60
60
  #
61
61
 
62
62
  def intercept(&block)
63
+ if browser == :firefox
64
+ WebDriver.logger.deprecate(
65
+ 'Driver#intercept on Firefox',
66
+ 'the new bidi.network.add_intercept method',
67
+ id: :intercept
68
+ )
69
+ end
63
70
  @interceptor ||= DevTools::NetworkInterceptor.new(devtools)
64
71
  @interceptor.intercept(&block)
65
72
  end
@@ -50,9 +50,11 @@ module Selenium
50
50
  super(URLS[class_name] ? "#{msg}; #{SUPPORT_MSG} #{URLS[class_name]}" : msg)
51
51
  end
52
52
 
53
+ # steep:ignore:start
53
54
  def class_name
54
- self.class.name&.split('::')&.last&.to_sym
55
+ self.class.name.split('::')&.last&.to_sym
55
56
  end
57
+ # steep:ignore:end
56
58
  end
57
59
 
58
60
  #
@@ -21,8 +21,8 @@ module Selenium
21
21
  module WebDriver
22
22
  module FedCM
23
23
  # Represents an account displayed in a FedCm account list.
24
- # See: https://fedidcg.github.io/FedCM/#dictdef-identityprovideraccount
25
- # https://fedidcg.github.io/FedCM/#webdriver-accountlist
24
+ # See: https://w3c-fedid.github.io/FedCM/#dictdef-identityprovideraccount
25
+ # https://w3c-fedid.github.io/FedCM/#webdriver-accountlist
26
26
  class Account
27
27
  LOGIN_STATE_SIGNIN = 'SignIn'
28
28
  LOGIN_STATE_SIGNUP = 'SignUp'
@@ -30,9 +30,7 @@ module Selenium
30
30
  attr_reader :account_id, :email, :name, :given_name, :picture_url,
31
31
  :idp_config_url, :login_state, :terms_of_service_url, :privacy_policy_url
32
32
 
33
- # Initializes a new account with the provided attributes.
34
- #
35
- # @param [Hash]
33
+ # steep:ignore:start
36
34
  def initialize(**args)
37
35
  @account_id = args['accountId']
38
36
  @email = args['email']
@@ -44,6 +42,7 @@ module Selenium
44
42
  @terms_of_service_url = args['termsOfServiceUrl']
45
43
  @privacy_policy_url = args['privacyPolicyUrl']
46
44
  end
45
+ # steep:ignore:end
47
46
  end # Account
48
47
  end # FedCM
49
48
  end # WebDriver
@@ -167,7 +167,7 @@ module Selenium
167
167
 
168
168
  id << :deprecations if @allowed.include?(:deprecations)
169
169
 
170
- message = +"[DEPRECATION] #{old} is deprecated"
170
+ message = "[DEPRECATION] #{old} is deprecated"
171
171
  message << if new
172
172
  ". Use #{new} instead."
173
173
  else
@@ -193,8 +193,8 @@ module Selenium
193
193
 
194
194
  def discard_or_log(level, message, id)
195
195
  id = Array(id)
196
- return if (@ignored & id).any?
197
- return if @allowed.any? && (@allowed & id).none?
196
+ return if @ignored.intersect?(id)
197
+ return if @allowed.any? && !@allowed.intersect?(id)
198
198
 
199
199
  return if ::Logger::Severity.const_get(level.upcase) < @logger.level
200
200
 
@@ -65,7 +65,7 @@ module Selenium
65
65
  # Get the cookie with the given name
66
66
  #
67
67
  # @param [String] name the name of the cookie
68
- # @return [Hash, nil] the cookie, or nil if it wasn't found.
68
+ # @return [Hash] the cookie, or throws a NoSuchCookieError if it wasn't found.
69
69
  #
70
70
 
71
71
  def cookie_named(name)
@@ -0,0 +1,102 @@
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
+ require 'forwardable'
21
+
22
+ module Selenium
23
+ module WebDriver
24
+ class Network
25
+ extend Forwardable
26
+
27
+ attr_reader :callbacks, :network
28
+ alias bidi network
29
+
30
+ def_delegators :network, :continue_with_auth, :continue_with_request, :continue_with_response
31
+
32
+ def initialize(bridge)
33
+ @network = BiDi::Network.new(bridge.bidi)
34
+ @callbacks = {}
35
+ end
36
+
37
+ def remove_handler(id)
38
+ intercept = callbacks[id]
39
+ network.remove_intercept(intercept['intercept'])
40
+ callbacks.delete(id)
41
+ end
42
+
43
+ def clear_handlers
44
+ callbacks.each_key { |id| remove_handler(id) }
45
+ end
46
+
47
+ def add_authentication_handler(username = nil, password = nil, *filter, pattern_type: nil, &block)
48
+ selected_block =
49
+ if username && password
50
+ proc { |auth| auth.authenticate(username, password) }
51
+ else
52
+ block
53
+ end
54
+
55
+ add_handler(
56
+ :auth_required,
57
+ BiDi::Network::PHASES[:auth_required],
58
+ BiDi::InterceptedAuth,
59
+ filter,
60
+ pattern_type: pattern_type,
61
+ &selected_block
62
+ )
63
+ end
64
+
65
+ def add_request_handler(*filter, pattern_type: nil, &block)
66
+ add_handler(
67
+ :before_request,
68
+ BiDi::Network::PHASES[:before_request],
69
+ BiDi::InterceptedRequest,
70
+ filter,
71
+ pattern_type: pattern_type,
72
+ &block
73
+ )
74
+ end
75
+
76
+ def add_response_handler(*filter, pattern_type: nil, &block)
77
+ add_handler(
78
+ :response_started,
79
+ BiDi::Network::PHASES[:response_started],
80
+ BiDi::InterceptedResponse,
81
+ filter,
82
+ pattern_type: pattern_type,
83
+ &block
84
+ )
85
+ end
86
+
87
+ private
88
+
89
+ def add_handler(event_type, phase, intercept_type, filter, pattern_type: nil)
90
+ intercept = network.add_intercept(phases: [phase], url_patterns: [filter].flatten, pattern_type: pattern_type)
91
+ callback_id = network.on(event_type) do |event|
92
+ request = event['request']
93
+ intercepted_item = intercept_type.new(network, request)
94
+ yield(intercepted_item)
95
+ end
96
+
97
+ callbacks[callback_id] = intercept
98
+ callback_id
99
+ end
100
+ end # Network
101
+ end # WebDriver
102
+ end # Selenium
@@ -69,6 +69,7 @@ module Selenium
69
69
  def initialize(path: nil, port: nil, log: nil, args: nil)
70
70
  port ||= self.class::DEFAULT_PORT
71
71
  args ||= []
72
+ path ||= env_path
72
73
 
73
74
  @executable_path = path
74
75
  @host = Platform.localhost
@@ -87,16 +88,22 @@ module Selenium
87
88
  end
88
89
 
89
90
  def launch
90
- @executable_path ||= begin
91
- default_options = WebDriver.const_get("#{self.class.name&.split('::')&.[](2)}::Options").new
92
- DriverFinder.new(default_options, self).driver_path
93
- end
91
+ @executable_path ||= env_path || find_driver_path
94
92
  ServiceManager.new(self).tap(&:start)
95
93
  end
96
94
 
97
95
  def shutdown_supported
98
96
  self.class::SHUTDOWN_SUPPORTED
99
97
  end
98
+
99
+ def find_driver_path
100
+ default_options = WebDriver.const_get("#{self.class.name&.split('::')&.[](2)}::Options").new
101
+ DriverFinder.new(default_options, self).driver_path
102
+ end
103
+
104
+ def env_path
105
+ ENV.fetch(self.class::DRIVER_PATH_ENV_KEY, nil)
106
+ end
100
107
  end # Service
101
108
  end # WebDriver
102
109
  end # Selenium
@@ -113,6 +113,7 @@ module Selenium
113
113
  def stop_server
114
114
  connect_to_server do |http|
115
115
  headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
116
+ WebDriver.logger.debug('Sending shutdown request to server', id: :driver_service)
116
117
  http.get('/shutdown', headers)
117
118
  end
118
119
  end
@@ -36,6 +36,7 @@ module Selenium
36
36
  'It should end with .png extension',
37
37
  id: :screenshot
38
38
  end
39
+ WebDriver.logger.debug("Saving screenshot to #{Dir.pwd}/#{png_path}")
39
40
  File.open(png_path, 'wb') { |f| f << screenshot_as(:png, full_page: full_page) }
40
41
  end
41
42
 
@@ -50,6 +50,7 @@ module Selenium
50
50
  # @param type either :tab or :window
51
51
  #
52
52
 
53
+ # steep:ignore:start
53
54
  def new_window(type = :window)
54
55
  raise ArgumentError, "Valid types are :tab and :window, received: #{type.inspect}" unless %i[window
55
56
  tab].include?(type)
@@ -70,6 +71,7 @@ module Selenium
70
71
  window(handle)
71
72
  end
72
73
  end
74
+ # steep:ignore:end
73
75
 
74
76
  #
75
77
  # switch to the given window handle
@@ -96,12 +98,11 @@ module Selenium
96
98
  @bridge.switch_to_window id
97
99
 
98
100
  begin
99
- returned = yield
101
+ yield
100
102
  ensure
101
103
  current_handles = @bridge.window_handles
102
104
  original = current_handles.first unless current_handles.include? original
103
105
  @bridge.switch_to_window original
104
- returned
105
106
  end
106
107
  else
107
108
  @bridge.switch_to_window id
@@ -65,7 +65,7 @@ module Selenium
65
65
  msg = if @message
66
66
  @message.dup
67
67
  else
68
- +"timed out after #{@timeout} seconds"
68
+ "timed out after #{@timeout} seconds"
69
69
  end
70
70
 
71
71
  msg << " (#{last_error.message})" if last_error
@@ -63,10 +63,6 @@ require 'selenium/webdriver/common/action_builder'
63
63
  require 'selenium/webdriver/common/virtual_authenticator/credential'
64
64
  require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options'
65
65
  require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator'
66
- require 'selenium/webdriver/common/html5/shared_web_storage'
67
- require 'selenium/webdriver/common/html5/local_storage'
68
- require 'selenium/webdriver/common/html5/session_storage'
69
- require 'selenium/webdriver/common/driver_extensions/has_web_storage'
70
66
  require 'selenium/webdriver/common/driver_extensions/downloads_files'
71
67
  require 'selenium/webdriver/common/driver_extensions/has_session_id'
72
68
  require 'selenium/webdriver/common/driver_extensions/has_network_conditions'
@@ -102,3 +98,4 @@ require 'selenium/webdriver/common/child_process'
102
98
  require 'selenium/webdriver/common/script'
103
99
  require 'selenium/webdriver/common/fedcm/account'
104
100
  require 'selenium/webdriver/common/fedcm/dialog'
101
+ require 'selenium/webdriver/common/network'
@@ -28,10 +28,10 @@ module Selenium
28
28
  autoload :Request, 'selenium/webdriver/devtools/request'
29
29
  autoload :Response, 'selenium/webdriver/devtools/response'
30
30
 
31
- def initialize(url:)
31
+ def initialize(url:, target_type:)
32
32
  @ws = WebSocketConnection.new(url: url)
33
33
  @session_id = nil
34
- start_session
34
+ start_session(target_type: target_type)
35
35
  end
36
36
 
37
37
  def close
@@ -81,10 +81,12 @@ module Selenium
81
81
 
82
82
  private
83
83
 
84
- def start_session
84
+ def start_session(target_type:)
85
85
  targets = target.get_targets.dig('result', 'targetInfos')
86
- page_target = targets.find { |target| target['type'] == 'page' }
87
- session = target.attach_to_target(target_id: page_target['targetId'], flatten: true)
86
+ found_target = targets.find { |target| target['type'] == target_type }
87
+ raise Error::WebDriverError, "Target type '#{target_type}' not found" unless found_target
88
+
89
+ session = target.attach_to_target(target_id: found_target['targetId'], flatten: true)
88
90
  @session_id = session.dig('result', 'sessionId')
89
91
  end
90
92
 
@@ -24,7 +24,7 @@ module Selenium
24
24
  DEFAULT_PORT = 9515
25
25
  EXECUTABLE = 'msedgedriver'
26
26
  SHUTDOWN_SUPPORTED = true
27
-
27
+ DRIVER_PATH_ENV_KEY = 'SE_EDGEDRIVER'
28
28
  def log
29
29
  return @log unless @log.is_a? String
30
30