selenium-webdriver 3.141.0 → 3.141.592

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +78 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE +1 -1
  5. data/lib/selenium-webdriver.rb +2 -0
  6. data/lib/selenium/server.rb +9 -7
  7. data/lib/selenium/webdriver.rb +3 -1
  8. data/lib/selenium/webdriver/atoms.rb +20 -1
  9. data/lib/selenium/webdriver/atoms/getAttribute.js +6 -7
  10. data/lib/selenium/webdriver/atoms/isDisplayed.js +60 -59
  11. data/lib/selenium/webdriver/chrome.rb +10 -4
  12. data/lib/selenium/webdriver/chrome/bridge.rb +5 -3
  13. data/lib/selenium/webdriver/chrome/driver.rb +10 -13
  14. data/lib/selenium/webdriver/chrome/options.rb +4 -4
  15. data/lib/selenium/webdriver/chrome/profile.rb +4 -3
  16. data/lib/selenium/webdriver/chrome/service.rb +13 -13
  17. data/lib/selenium/webdriver/common.rb +4 -2
  18. data/lib/selenium/webdriver/common/action_builder.rb +2 -0
  19. data/lib/selenium/webdriver/common/alert.rb +2 -0
  20. data/lib/selenium/webdriver/common/bridge_helper.rb +8 -5
  21. data/lib/selenium/webdriver/common/driver.rb +22 -7
  22. data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +2 -0
  23. data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +2 -0
  24. data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +2 -0
  25. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +3 -3
  26. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +2 -0
  27. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +3 -1
  28. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +2 -0
  29. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +2 -0
  30. data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +2 -0
  31. data/lib/selenium/webdriver/common/driver_extensions/has_touch_screen.rb +2 -0
  32. data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +2 -0
  33. data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +3 -1
  34. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +2 -0
  35. data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +3 -3
  36. data/lib/selenium/webdriver/common/element.rb +3 -1
  37. data/lib/selenium/webdriver/common/error.rb +74 -18
  38. data/lib/selenium/webdriver/common/file_reaper.rb +3 -3
  39. data/lib/selenium/webdriver/common/html5/local_storage.rb +2 -0
  40. data/lib/selenium/webdriver/common/html5/session_storage.rb +2 -0
  41. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +4 -1
  42. data/lib/selenium/webdriver/common/interactions/input_device.rb +3 -0
  43. data/lib/selenium/webdriver/common/interactions/interaction.rb +3 -0
  44. data/lib/selenium/webdriver/common/interactions/interactions.rb +3 -1
  45. data/lib/selenium/webdriver/common/interactions/key_actions.rb +2 -0
  46. data/lib/selenium/webdriver/common/interactions/key_input.rb +4 -0
  47. data/lib/selenium/webdriver/common/interactions/none_input.rb +3 -0
  48. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +2 -0
  49. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +7 -0
  50. data/lib/selenium/webdriver/common/keyboard.rb +4 -1
  51. data/lib/selenium/webdriver/common/keys.rb +3 -0
  52. data/lib/selenium/webdriver/common/log_entry.rb +4 -2
  53. data/lib/selenium/webdriver/common/logger.rb +15 -40
  54. data/lib/selenium/webdriver/common/logs.rb +2 -0
  55. data/lib/selenium/webdriver/common/manager.rb +177 -0
  56. data/lib/selenium/webdriver/common/mouse.rb +3 -0
  57. data/lib/selenium/webdriver/common/navigation.rb +2 -0
  58. data/lib/selenium/webdriver/common/options.rb +26 -0
  59. data/lib/selenium/webdriver/common/platform.rb +26 -30
  60. data/lib/selenium/webdriver/common/port_prober.rb +6 -19
  61. data/lib/selenium/webdriver/common/profile_helper.rb +2 -0
  62. data/lib/selenium/webdriver/common/proxy.rb +13 -5
  63. data/lib/selenium/webdriver/common/search_context.rb +4 -6
  64. data/lib/selenium/webdriver/common/service.rb +74 -29
  65. data/lib/selenium/webdriver/common/socket_lock.rb +10 -3
  66. data/lib/selenium/webdriver/common/socket_poller.rb +45 -48
  67. data/lib/selenium/webdriver/common/target_locator.rb +6 -4
  68. data/lib/selenium/webdriver/common/timeouts.rb +2 -0
  69. data/lib/selenium/webdriver/common/touch_action_builder.rb +5 -6
  70. data/lib/selenium/webdriver/common/touch_screen.rb +4 -1
  71. data/lib/selenium/webdriver/common/w3c_action_builder.rb +3 -0
  72. data/lib/selenium/webdriver/common/w3c_manager.rb +45 -0
  73. data/lib/selenium/webdriver/common/w3c_options.rb +2 -0
  74. data/lib/selenium/webdriver/common/wait.rb +11 -3
  75. data/lib/selenium/webdriver/common/window.rb +2 -0
  76. data/lib/selenium/webdriver/common/zipper.rb +3 -3
  77. data/lib/selenium/webdriver/edge.rb +11 -5
  78. data/lib/selenium/webdriver/edge/bridge.rb +2 -0
  79. data/lib/selenium/webdriver/edge/driver.rb +5 -12
  80. data/lib/selenium/webdriver/edge/options.rb +3 -0
  81. data/lib/selenium/webdriver/edge/service.rb +8 -12
  82. data/lib/selenium/webdriver/firefox.rb +10 -4
  83. data/lib/selenium/webdriver/firefox/binary.rb +7 -6
  84. data/lib/selenium/webdriver/firefox/bridge.rb +47 -0
  85. data/lib/selenium/webdriver/firefox/driver.rb +3 -2
  86. data/lib/selenium/webdriver/firefox/extension.rb +4 -4
  87. data/lib/selenium/webdriver/firefox/launcher.rb +3 -0
  88. data/lib/selenium/webdriver/firefox/legacy/driver.rb +7 -3
  89. data/lib/selenium/webdriver/firefox/marionette/bridge.rb +4 -2
  90. data/lib/selenium/webdriver/firefox/marionette/driver.rb +5 -11
  91. data/lib/selenium/webdriver/firefox/options.rb +3 -1
  92. data/lib/selenium/webdriver/firefox/profile.rb +7 -8
  93. data/lib/selenium/webdriver/firefox/profiles_ini.rb +3 -0
  94. data/lib/selenium/webdriver/firefox/service.rb +8 -19
  95. data/lib/selenium/webdriver/firefox/util.rb +2 -0
  96. data/lib/selenium/webdriver/ie.rb +10 -4
  97. data/lib/selenium/webdriver/ie/driver.rb +4 -10
  98. data/lib/selenium/webdriver/ie/options.rb +4 -2
  99. data/lib/selenium/webdriver/ie/service.rb +8 -12
  100. data/lib/selenium/webdriver/remote.rb +2 -0
  101. data/lib/selenium/webdriver/remote/bridge.rb +6 -4
  102. data/lib/selenium/webdriver/remote/capabilities.rb +23 -10
  103. data/lib/selenium/webdriver/remote/commands.rb +156 -0
  104. data/lib/selenium/webdriver/remote/driver.rb +2 -0
  105. data/lib/selenium/webdriver/remote/http/common.rb +11 -4
  106. data/lib/selenium/webdriver/remote/http/curb.rb +4 -2
  107. data/lib/selenium/webdriver/remote/http/default.rb +31 -25
  108. data/lib/selenium/webdriver/remote/http/persistent.rb +3 -1
  109. data/lib/selenium/webdriver/remote/oss/bridge.rb +5 -2
  110. data/lib/selenium/webdriver/remote/oss/commands.rb +106 -104
  111. data/lib/selenium/webdriver/remote/response.rb +10 -4
  112. data/lib/selenium/webdriver/remote/server_error.rb +2 -0
  113. data/lib/selenium/webdriver/remote/w3c/bridge.rb +28 -13
  114. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +37 -21
  115. data/lib/selenium/webdriver/remote/w3c/commands.rb +61 -58
  116. data/lib/selenium/webdriver/safari.rb +11 -4
  117. data/lib/selenium/webdriver/safari/bridge.rb +5 -3
  118. data/lib/selenium/webdriver/safari/driver.rb +8 -10
  119. data/lib/selenium/webdriver/safari/options.rb +2 -0
  120. data/lib/selenium/webdriver/safari/service.rb +6 -25
  121. data/lib/selenium/webdriver/support.rb +2 -0
  122. data/lib/selenium/webdriver/support/abstract_event_listener.rb +2 -0
  123. data/lib/selenium/webdriver/support/block_event_listener.rb +3 -1
  124. data/lib/selenium/webdriver/support/color.rb +11 -9
  125. data/lib/selenium/webdriver/support/escaper.rb +2 -0
  126. data/lib/selenium/webdriver/support/event_firing_bridge.rb +3 -1
  127. data/lib/selenium/webdriver/support/select.rb +19 -18
  128. data/lib/selenium/webdriver/version.rb +3 -1
  129. data/selenium-webdriver.gemspec +14 -7
  130. metadata +89 -21
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
2
4
  # or more contributor license agreements. See the NOTICE file
3
5
  # distributed with this work for additional information
@@ -79,6 +81,7 @@ module Selenium
79
81
 
80
82
  def assert_element(element)
81
83
  return if element.is_a? Element
84
+
82
85
  raise TypeError, "expected #{Element}, got #{element.inspect}:#{element.class}"
83
86
  end
84
87
  end # Mouse
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
2
4
  # or more contributor license agreements. See the NOTICE file
3
5
  # distributed with this work for additional information
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
2
4
  # or more contributor license agreements. See the NOTICE file
3
5
  # distributed with this work for additional information
@@ -103,6 +105,30 @@ module Selenium
103
105
  @logs ||= Logs.new(@bridge)
104
106
  end
105
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
+
106
132
  #
107
133
  # @api beta This API may be changed or removed in a future release.
108
134
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
2
4
  # or more contributor license agreements. See the NOTICE file
3
5
  # distributed with this work for additional information
@@ -25,12 +27,11 @@ module Selenium
25
27
  module_function
26
28
 
27
29
  def home
28
- # jruby has an issue with ENV['HOME'] on Windows
29
- @home ||= jruby? ? ENV_JAVA['user.home'] : ENV['HOME']
30
+ @home ||= Dir.home
30
31
  end
31
32
 
32
33
  def engine
33
- @engine ||= defined?(RUBY_ENGINE) ? RUBY_ENGINE.to_sym : :ruby
34
+ @engine ||= RUBY_ENGINE.to_sym
34
35
  end
35
36
 
36
37
  def os
@@ -50,8 +51,13 @@ module Selenium
50
51
  end
51
52
 
52
53
  def ci
53
- return :travis if ENV['TRAVIS']
54
- :jenkins if ENV['JENKINS']
54
+ if ENV['TRAVIS']
55
+ :travis
56
+ elsif ENV['JENKINS']
57
+ :jenkins
58
+ elsif ENV['APPVEYOR']
59
+ :appveyor
60
+ end
55
61
  end
56
62
 
57
63
  def bitsize
@@ -70,10 +76,6 @@ module Selenium
70
76
  engine == :jruby
71
77
  end
72
78
 
73
- def ironruby?
74
- engine == :ironruby
75
- end
76
-
77
79
  def ruby_version
78
80
  RUBY_VERSION
79
81
  end
@@ -92,6 +94,7 @@ module Selenium
92
94
 
93
95
  def wsl?
94
96
  return false unless linux?
97
+
95
98
  File.read('/proc/version').include?('Microsoft')
96
99
  end
97
100
 
@@ -101,30 +104,35 @@ module Selenium
101
104
  end
102
105
 
103
106
  def null_device
104
- @null_device ||= if defined?(File::NULL)
105
- File::NULL
106
- else
107
- Platform.windows? ? 'NUL' : '/dev/null'
108
- end
107
+ File::NULL
109
108
  end
110
109
 
111
110
  def wrap_in_quotes_if_necessary(str)
112
111
  windows? && !cygwin? ? %("#{str}") : str
113
112
  end
114
113
 
115
- def cygwin_path(path, opts = {})
114
+ def cygwin_path(path, **opts)
116
115
  flags = []
117
116
  opts.each { |k, v| flags << "--#{k}" if v }
118
117
 
119
118
  `cygpath #{flags.join ' '} "#{path}"`.strip
120
119
  end
121
120
 
121
+ def unix_path(path)
122
+ path.tr(File::ALT_SEPARATOR, File::SEPARATOR)
123
+ end
124
+
125
+ def windows_path(path)
126
+ path.tr(File::SEPARATOR, File::ALT_SEPARATOR)
127
+ end
128
+
122
129
  def make_writable(file)
123
130
  File.chmod 0o766, file
124
131
  end
125
132
 
126
133
  def assert_file(path)
127
134
  return if File.file? path
135
+
128
136
  raise Error::WebDriverError, "not a file: #{path.inspect}"
129
137
  end
130
138
 
@@ -132,6 +140,7 @@ module Selenium
132
140
  assert_file(path)
133
141
 
134
142
  return if File.executable? path
143
+
135
144
  raise Error::WebDriverError, "not executable: #{path.inspect}"
136
145
  end
137
146
 
@@ -152,7 +161,7 @@ module Selenium
152
161
  binary_names.each do |binary_name|
153
162
  paths.each do |path|
154
163
  full_path = File.join(path, binary_name)
155
- full_path.tr!('\\', '/') if windows?
164
+ full_path = unix_path(full_path) if windows?
156
165
  exe = Dir.glob(full_path).find { |f| File.executable?(f) }
157
166
  return exe if exe
158
167
  end
@@ -182,6 +191,7 @@ module Selenium
182
191
  info = Socket.getaddrinfo 'localhost', 80, Socket::AF_INET, Socket::SOCK_STREAM
183
192
 
184
193
  return info[0][3] unless info.empty?
194
+
185
195
  raise Error::WebDriverError, "unable to translate 'localhost' for TCP + IPv4"
186
196
  end
187
197
 
@@ -210,17 +220,3 @@ module Selenium
210
220
  end # Platform
211
221
  end # WebDriver
212
222
  end # Selenium
213
-
214
- if $PROGRAM_NAME == __FILE__
215
- p engine: Selenium::WebDriver::Platform.engine,
216
- os: Selenium::WebDriver::Platform.os,
217
- ruby_version: Selenium::WebDriver::Platform.ruby_version,
218
- jruby?: Selenium::WebDriver::Platform.jruby?,
219
- windows?: Selenium::WebDriver::Platform.windows?,
220
- home: Selenium::WebDriver::Platform.home,
221
- bitsize: Selenium::WebDriver::Platform.bitsize,
222
- localhost: Selenium::WebDriver::Platform.localhost,
223
- ip: Selenium::WebDriver::Platform.ip,
224
- interfaces: Selenium::WebDriver::Platform.interfaces,
225
- null_device: Selenium::WebDriver::Platform.null_device
226
- end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
2
4
  # or more contributor license agreements. See the NOTICE file
3
5
  # distributed with this work for additional information
@@ -23,25 +25,10 @@ module Selenium
23
25
  port
24
26
  end
25
27
 
26
- def self.random
27
- # TODO: Avoid this
28
- #
29
- # (a) should pick a port that's guaranteed to be free on all interfaces
30
- # (b) should pick a random port outside the ephemeral port range
31
- #
32
- WebDriver.logger.deprecate 'PortProber.random', 'PortProber.above(port)'
33
-
34
- server = TCPServer.new(Platform.localhost, 0)
35
- port = server.addr[1]
36
- server.close
37
-
38
- port
39
- end
40
-
41
- IGNORED_ERRORS = [Errno::EADDRNOTAVAIL, Errno::EAFNOSUPPORT]
42
- IGNORED_ERRORS << Errno::EBADF if Platform.cygwin?
43
- IGNORED_ERRORS << Errno::EACCES if Platform.windows?
44
- IGNORED_ERRORS.freeze
28
+ IGNORED_ERRORS = [Errno::EADDRNOTAVAIL, Errno::EAFNOSUPPORT].tap { |arr|
29
+ arr << Errno::EBADF if Platform.cygwin?
30
+ arr << Errno::EACCES if Platform.windows?
31
+ }.freeze
45
32
 
46
33
  def self.free?(port)
47
34
  Platform.interfaces.each do |host|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
2
4
  # or more contributor license agreements. See the NOTICE file
3
5
  # distributed with this work for additional information
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
2
4
  # or more contributor license agreements. See the NOTICE file
3
5
  # distributed with this work for additional information
@@ -35,7 +37,8 @@ module Selenium
35
37
  auto_detect: 'autodetect',
36
38
  socks: 'socksProxy',
37
39
  socks_username: 'socksUsername',
38
- socks_password: 'socksPassword'}.freeze
40
+ socks_password: 'socksPassword',
41
+ socks_version: 'socksVersion'}.freeze
39
42
 
40
43
  ALLOWED.each_key { |t| attr_reader t }
41
44
 
@@ -64,6 +67,7 @@ module Selenium
64
67
  end
65
68
 
66
69
  return if not_allowed.empty?
70
+
67
71
  raise ArgumentError, "unknown option#{'s' if not_allowed.size != 1}: #{not_allowed.inspect}"
68
72
  end
69
73
 
@@ -117,10 +121,13 @@ module Selenium
117
121
  @socks_password = value
118
122
  end
119
123
 
124
+ def socks_version=(value)
125
+ self.type = :manual
126
+ @socks_version = value
127
+ end
128
+
120
129
  def type=(type)
121
- unless TYPES.key? type
122
- raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}"
123
- end
130
+ raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}" unless TYPES.key? type
124
131
 
125
132
  if defined?(@type) && type != @type
126
133
  raise ArgumentError, "incompatible proxy type #{type.inspect} (already set to #{@type.inspect})"
@@ -140,7 +147,8 @@ module Selenium
140
147
  'autodetect' => auto_detect,
141
148
  'socksProxy' => socks,
142
149
  'socksUsername' => socks_username,
143
- 'socksPassword' => socks_password
150
+ 'socksPassword' => socks_password,
151
+ 'socksVersion' => socks_version
144
152
  }.delete_if { |_k, v| v.nil? }
145
153
 
146
154
  json_result if json_result.length > 1
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
2
4
  # or more contributor license agreements. See the NOTICE file
3
5
  # distributed with this work for additional information
@@ -90,15 +92,11 @@ module Selenium
90
92
  when 1
91
93
  arg = args.first
92
94
 
93
- unless arg.respond_to?(:shift)
94
- raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift"
95
- end
95
+ raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift" unless arg.respond_to?(:shift)
96
96
 
97
97
  # this will be a single-entry hash, so use #shift over #first or #[]
98
98
  arr = arg.dup.shift
99
- unless arr.size == 2
100
- raise ArgumentError, "expected #{arr.inspect} to have 2 elements"
101
- end
99
+ raise ArgumentError, "expected #{arr.inspect} to have 2 elements" unless arr.size == 2
102
100
 
103
101
  arr
104
102
  else
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
2
4
  # or more contributor license agreements. See the NOTICE file
3
5
  # distributed with this work for additional information
@@ -21,50 +23,74 @@ module Selenium
21
23
  # Base class implementing default behavior of service object,
22
24
  # responsible for starting and stopping driver implementations.
23
25
  #
24
- # Subclasses must implement the following private methods:
25
- # * #start_process
26
- # * #stop_server
27
- # * #cannot_connect_error_text
28
- #
29
- # @api private
30
- #
31
26
 
32
27
  class Service
33
- START_TIMEOUT = 20
28
+ START_TIMEOUT = 20
34
29
  SOCKET_LOCK_TIMEOUT = 45
35
- STOP_TIMEOUT = 20
30
+ STOP_TIMEOUT = 20
36
31
 
32
+ @default_port = nil
33
+ @driver_path = nil
37
34
  @executable = nil
38
35
  @missing_text = nil
39
36
 
40
37
  class << self
41
- attr_reader :executable, :missing_text
38
+ attr_reader :default_port, :driver_path, :executable, :missing_text
39
+
40
+ def chrome(*args)
41
+ Chrome::Service.new(*args)
42
+ end
43
+
44
+ def firefox(*args)
45
+ Firefox::Service.new(*args)
46
+ end
47
+
48
+ def ie(*args)
49
+ IE::Service.new(*args)
50
+ end
51
+ alias_method :internet_explorer, :ie
52
+
53
+ def edge(*args)
54
+ Edge::Service.new(*args)
55
+ end
56
+
57
+ def safari(*args)
58
+ Safari::Service.new(*args)
59
+ end
60
+
61
+ def driver_path=(path)
62
+ Platform.assert_executable path if path.is_a?(String)
63
+ @driver_path = path
64
+ end
42
65
  end
43
66
 
44
67
  attr_accessor :host
68
+ attr_reader :executable_path
45
69
 
46
- def initialize(executable_path, port, driver_opts)
47
- @executable_path = binary_path(executable_path)
48
- @host = Platform.localhost
49
- @port = Integer(port)
50
- @extra_args = extract_service_args(driver_opts)
70
+ #
71
+ # End users should use a class method for the desired driver, rather than using this directly.
72
+ #
73
+ # @api private
74
+ #
51
75
 
52
- raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
53
- end
76
+ def initialize(path: nil, port: nil, args: nil)
77
+ path ||= self.class.driver_path
78
+ port ||= self.class.default_port
79
+ args ||= []
54
80
 
55
- def binary_path(path)
56
- path = Platform.find_binary(self.class.executable) if path.nil?
57
- raise Error::WebDriverError, self.class.missing_text unless path
58
- Platform.assert_executable path
59
- path
81
+ @executable_path = binary_path(path)
82
+ @host = Platform.localhost
83
+ @port = Integer(port)
84
+
85
+ @extra_args = args.is_a?(Hash) ? extract_service_args(args) : args
86
+
87
+ raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
60
88
  end
61
89
 
62
90
  def start
63
- if process_running?
64
- raise "already started: #{uri.inspect} #{@executable_path.inspect}"
65
- end
91
+ raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
66
92
 
67
- Platform.exit_hook { stop } # make sure we don't leave the server running
93
+ Platform.exit_hook(&method(:stop)) # make sure we don't leave the server running
68
94
 
69
95
  socket_lock.locked do
70
96
  find_free_port
@@ -74,9 +100,12 @@ module Selenium
74
100
  end
75
101
 
76
102
  def stop
103
+ return unless @shutdown_supported
104
+
77
105
  stop_server
78
106
  @process.poll_for_exit STOP_TIMEOUT
79
107
  rescue ChildProcess::TimeoutError
108
+ nil # noop
80
109
  ensure
81
110
  stop_process
82
111
  end
@@ -87,6 +116,16 @@ module Selenium
87
116
 
88
117
  private
89
118
 
119
+ def binary_path(path = nil)
120
+ path = path.call if path.is_a?(Proc)
121
+ path ||= Platform.find_binary(self.class.executable)
122
+
123
+ raise Error::WebDriverError, self.class.missing_text unless path
124
+
125
+ Platform.assert_executable path
126
+ path
127
+ end
128
+
90
129
  def build_process(*command)
91
130
  WebDriver.logger.debug("Executing Process #{command}")
92
131
  @process = ChildProcess.build(*command)
@@ -114,22 +153,27 @@ module Selenium
114
153
  end
115
154
 
116
155
  def start_process
117
- raise NotImplementedError, 'subclass responsibility'
156
+ @process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
157
+ # Note: this is a bug only in Windows 7
158
+ @process.leader = true unless Platform.windows?
159
+ @process.start
118
160
  end
119
161
 
120
162
  def stop_process
121
163
  return if process_exited?
164
+
122
165
  @process.stop STOP_TIMEOUT
123
166
  @process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
124
167
  end
125
168
 
126
169
  def stop_server
127
170
  return if process_exited?
171
+
128
172
  connect_to_server { |http| http.get('/shutdown') }
129
173
  end
130
174
 
131
175
  def process_running?
132
- defined?(@process) && @process && @process.alive?
176
+ defined?(@process) && @process&.alive?
133
177
  end
134
178
 
135
179
  def process_exited?
@@ -139,11 +183,12 @@ module Selenium
139
183
  def connect_until_stable
140
184
  socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
141
185
  return if socket_poller.connected?
186
+
142
187
  raise Error::WebDriverError, cannot_connect_error_text
143
188
  end
144
189
 
145
190
  def cannot_connect_error_text
146
- raise NotImplementedError, 'subclass responsibility'
191
+ "unable to connect to #{self.class.executable} #{@host}:#{@port}"
147
192
  end
148
193
 
149
194
  def socket_lock