selenium-webdriver 3.0.0.beta4.0 → 4.0.0.alpha5

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 (139) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +679 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE +1 -1
  5. data/README.md +1 -1
  6. data/lib/selenium-webdriver.rb +2 -2
  7. data/lib/selenium/server.rb +23 -16
  8. data/lib/selenium/webdriver.rb +43 -25
  9. data/lib/selenium/webdriver/atoms.rb +20 -1
  10. data/lib/selenium/webdriver/atoms/findElements.js +122 -0
  11. data/lib/selenium/webdriver/atoms/getAttribute.js +84 -11
  12. data/lib/selenium/webdriver/atoms/isDisplayed.js +100 -0
  13. data/lib/selenium/webdriver/chrome.rb +15 -20
  14. data/lib/selenium/webdriver/chrome/bridge.rb +29 -66
  15. data/lib/selenium/webdriver/{edge/service.rb → chrome/driver.rb} +19 -22
  16. data/lib/selenium/webdriver/chrome/options.rb +221 -0
  17. data/lib/selenium/webdriver/chrome/profile.rb +6 -7
  18. data/lib/selenium/webdriver/chrome/service.rb +20 -20
  19. data/lib/selenium/webdriver/common.rb +19 -11
  20. data/lib/selenium/webdriver/common/action_builder.rb +98 -246
  21. data/lib/selenium/webdriver/common/alert.rb +2 -7
  22. data/lib/selenium/webdriver/common/driver.rb +89 -51
  23. data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +45 -0
  24. data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +50 -0
  25. data/lib/selenium/webdriver/common/driver_extensions/{has_input_devices.rb → has_debugger.rb} +12 -25
  26. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +38 -0
  27. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +3 -5
  28. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +51 -0
  29. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +3 -3
  30. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +51 -0
  31. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +2 -2
  32. data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +2 -2
  33. data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +2 -2
  34. data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +4 -4
  35. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +9 -3
  36. data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +3 -5
  37. data/lib/selenium/webdriver/common/element.rb +32 -10
  38. data/lib/selenium/webdriver/common/error.rb +103 -121
  39. data/lib/selenium/webdriver/common/file_reaper.rb +3 -5
  40. data/lib/selenium/webdriver/common/html5/local_storage.rb +2 -2
  41. data/lib/selenium/webdriver/common/html5/session_storage.rb +2 -2
  42. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +3 -2
  43. data/lib/selenium/webdriver/common/interactions/input_device.rb +54 -0
  44. data/lib/selenium/webdriver/common/interactions/interaction.rb +53 -0
  45. data/lib/selenium/webdriver/{phantomjs.rb → common/interactions/interactions.rb} +17 -20
  46. data/lib/selenium/webdriver/common/interactions/key_actions.rb +145 -0
  47. data/lib/selenium/webdriver/common/interactions/key_input.rb +66 -0
  48. data/lib/selenium/webdriver/common/interactions/none_input.rb +36 -0
  49. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +363 -0
  50. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +139 -0
  51. data/lib/selenium/webdriver/common/keys.rb +37 -15
  52. data/lib/selenium/webdriver/common/log_entry.rb +4 -4
  53. data/lib/selenium/webdriver/common/logger.rb +147 -0
  54. data/lib/selenium/webdriver/common/logs.rb +2 -2
  55. data/lib/selenium/webdriver/common/manager.rb +177 -0
  56. data/lib/selenium/webdriver/common/navigation.rb +2 -2
  57. data/lib/selenium/webdriver/common/options.rb +47 -105
  58. data/lib/selenium/webdriver/common/platform.rb +44 -38
  59. data/lib/selenium/webdriver/common/port_prober.rb +8 -19
  60. data/lib/selenium/webdriver/common/profile_helper.rb +13 -5
  61. data/lib/selenium/webdriver/common/proxy.rb +14 -8
  62. data/lib/selenium/webdriver/common/search_context.rb +16 -20
  63. data/lib/selenium/webdriver/common/service.rb +115 -30
  64. data/lib/selenium/webdriver/common/socket_lock.rb +12 -7
  65. data/lib/selenium/webdriver/common/socket_poller.rb +29 -22
  66. data/lib/selenium/webdriver/common/target_locator.rb +6 -6
  67. data/lib/selenium/webdriver/common/timeouts.rb +2 -2
  68. data/lib/selenium/webdriver/common/wait.rb +14 -8
  69. data/lib/selenium/webdriver/common/window.rb +38 -2
  70. data/lib/selenium/webdriver/common/zipper.rb +3 -5
  71. data/lib/selenium/webdriver/edge.rb +33 -20
  72. data/lib/selenium/webdriver/edge_chrome/bridge.rb +30 -0
  73. data/lib/selenium/webdriver/edge_chrome/driver.rb +38 -0
  74. data/lib/selenium/webdriver/{common/driver_extensions/has_touch_screen.rb → edge_chrome/options.rb} +10 -12
  75. data/lib/selenium/webdriver/edge_chrome/profile.rb +33 -0
  76. data/lib/selenium/webdriver/edge_chrome/service.rb +36 -0
  77. data/lib/selenium/webdriver/edge_html/driver.rb +39 -0
  78. data/lib/selenium/webdriver/edge_html/options.rb +91 -0
  79. data/lib/selenium/webdriver/edge_html/service.rb +47 -0
  80. data/lib/selenium/webdriver/firefox.rb +24 -29
  81. data/lib/selenium/webdriver/firefox/bridge.rb +15 -39
  82. data/lib/selenium/webdriver/firefox/{util.rb → driver.rb} +13 -19
  83. data/lib/selenium/webdriver/firefox/extension.rb +28 -8
  84. data/lib/selenium/webdriver/firefox/options.rb +162 -0
  85. data/lib/selenium/webdriver/firefox/profile.rb +23 -82
  86. data/lib/selenium/webdriver/firefox/profiles_ini.rb +5 -5
  87. data/lib/selenium/webdriver/firefox/service.rb +18 -37
  88. data/lib/selenium/webdriver/ie.rb +13 -19
  89. data/lib/selenium/webdriver/ie/driver.rb +40 -0
  90. data/lib/selenium/webdriver/ie/options.rb +118 -0
  91. data/lib/selenium/webdriver/ie/service.rb +20 -20
  92. data/lib/selenium/webdriver/remote.rb +15 -17
  93. data/lib/selenium/webdriver/remote/bridge.rb +281 -300
  94. data/lib/selenium/webdriver/remote/capabilities.rb +102 -83
  95. data/lib/selenium/webdriver/remote/commands.rb +132 -192
  96. data/lib/selenium/webdriver/remote/driver.rb +52 -0
  97. data/lib/selenium/webdriver/remote/http/common.rb +23 -13
  98. data/lib/selenium/webdriver/remote/http/curb.rb +10 -7
  99. data/lib/selenium/webdriver/remote/http/default.rb +52 -32
  100. data/lib/selenium/webdriver/remote/http/persistent.rb +8 -4
  101. data/lib/selenium/webdriver/remote/response.rb +32 -35
  102. data/lib/selenium/webdriver/remote/server_error.rb +2 -2
  103. data/lib/selenium/webdriver/safari.rb +24 -22
  104. data/lib/selenium/webdriver/safari/bridge.rb +21 -21
  105. data/lib/selenium/webdriver/safari/driver.rb +41 -0
  106. data/lib/selenium/webdriver/safari/options.rb +66 -0
  107. data/lib/selenium/webdriver/safari/service.rb +8 -24
  108. data/lib/selenium/webdriver/support.rb +3 -2
  109. data/lib/selenium/webdriver/support/abstract_event_listener.rb +2 -2
  110. data/lib/selenium/webdriver/support/block_event_listener.rb +3 -3
  111. data/lib/selenium/webdriver/support/color.rb +13 -13
  112. data/lib/selenium/webdriver/support/escaper.rb +2 -2
  113. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  114. data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
  115. data/lib/selenium/webdriver/support/select.rb +20 -21
  116. data/lib/selenium/webdriver/version.rb +24 -0
  117. data/selenium-webdriver.gemspec +31 -17
  118. metadata +331 -248
  119. data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
  120. data/lib/selenium/webdriver/common/keyboard.rb +0 -69
  121. data/lib/selenium/webdriver/common/mouse.rb +0 -88
  122. data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -81
  123. data/lib/selenium/webdriver/common/touch_screen.rb +0 -121
  124. data/lib/selenium/webdriver/common/w3c_error.rb +0 -191
  125. data/lib/selenium/webdriver/edge/bridge.rb +0 -76
  126. data/lib/selenium/webdriver/edge/legacy_support.rb +0 -117
  127. data/lib/selenium/webdriver/firefox/binary.rb +0 -186
  128. data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
  129. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  130. data/lib/selenium/webdriver/firefox/launcher.rb +0 -114
  131. data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
  132. data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
  133. data/lib/selenium/webdriver/firefox/w3c_bridge.rb +0 -79
  134. data/lib/selenium/webdriver/ie/bridge.rb +0 -76
  135. data/lib/selenium/webdriver/phantomjs/bridge.rb +0 -65
  136. data/lib/selenium/webdriver/phantomjs/service.rb +0 -66
  137. data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -682
  138. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -228
  139. data/lib/selenium/webdriver/remote/w3c_commands.rb +0 -135
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -27,12 +27,11 @@ module Selenium
27
27
  module_function
28
28
 
29
29
  def home
30
- # jruby has an issue with ENV['HOME'] on Windows
31
- @home ||= jruby? ? ENV_JAVA['user.home'] : ENV['HOME']
30
+ @home ||= Dir.home
32
31
  end
33
32
 
34
33
  def engine
35
- @engine ||= defined?(RUBY_ENGINE) ? RUBY_ENGINE.to_sym : :ruby
34
+ @engine ||= RUBY_ENGINE.to_sym
36
35
  end
37
36
 
38
37
  def os
@@ -52,8 +51,13 @@ module Selenium
52
51
  end
53
52
 
54
53
  def ci
55
- return :travis if ENV['TRAVIS']
56
- :jenkins if ENV['JENKINS']
54
+ if ENV['TRAVIS']
55
+ :travis
56
+ elsif ENV['JENKINS']
57
+ :jenkins
58
+ elsif ENV['APPVEYOR']
59
+ :appveyor
60
+ end
57
61
  end
58
62
 
59
63
  def bitsize
@@ -72,10 +76,6 @@ module Selenium
72
76
  engine == :jruby
73
77
  end
74
78
 
75
- def ironruby?
76
- engine == :ironruby
77
- end
78
-
79
79
  def ruby_version
80
80
  RUBY_VERSION
81
81
  end
@@ -92,36 +92,50 @@ module Selenium
92
92
  os == :linux
93
93
  end
94
94
 
95
+ def wsl?
96
+ return false unless linux?
97
+
98
+ File.read('/proc/version').include?('Microsoft')
99
+ rescue Errno::EACCES
100
+ # the file cannot be accessed on Linux on DeX
101
+ false
102
+ end
103
+
95
104
  def cygwin?
96
105
  RUBY_PLATFORM =~ /cygwin/
97
106
  !Regexp.last_match.nil?
98
107
  end
99
108
 
100
109
  def null_device
101
- @null_device ||= if defined?(File::NULL)
102
- File::NULL
103
- else
104
- Platform.windows? ? 'NUL' : '/dev/null'
105
- end
110
+ File::NULL
106
111
  end
107
112
 
108
113
  def wrap_in_quotes_if_necessary(str)
109
114
  windows? && !cygwin? ? %("#{str}") : str
110
115
  end
111
116
 
112
- def cygwin_path(path, opts = {})
117
+ def cygwin_path(path, **opts)
113
118
  flags = []
114
119
  opts.each { |k, v| flags << "--#{k}" if v }
115
120
 
116
121
  `cygpath #{flags.join ' '} "#{path}"`.strip
117
122
  end
118
123
 
124
+ def unix_path(path)
125
+ path.tr(File::ALT_SEPARATOR, File::SEPARATOR)
126
+ end
127
+
128
+ def windows_path(path)
129
+ path.tr(File::SEPARATOR, File::ALT_SEPARATOR)
130
+ end
131
+
119
132
  def make_writable(file)
120
- File.chmod 0766, file
133
+ File.chmod 0o766, file
121
134
  end
122
135
 
123
136
  def assert_file(path)
124
137
  return if File.file? path
138
+
125
139
  raise Error::WebDriverError, "not a file: #{path.inspect}"
126
140
  end
127
141
 
@@ -129,6 +143,7 @@ module Selenium
129
143
  assert_file(path)
130
144
 
131
145
  return if File.executable? path
146
+
132
147
  raise Error::WebDriverError, "not executable: #{path.inspect}"
133
148
  end
134
149
 
@@ -140,15 +155,18 @@ module Selenium
140
155
 
141
156
  def find_binary(*binary_names)
142
157
  paths = ENV['PATH'].split(File::PATH_SEPARATOR)
143
- binary_names.map! { |n| "#{n}.exe" } if windows?
158
+
159
+ if windows?
160
+ binary_names.map! { |n| "#{n}.exe" }
161
+ binary_names.dup.each { |n| binary_names << n.gsub('exe', 'bat') }
162
+ end
144
163
 
145
164
  binary_names.each do |binary_name|
146
165
  paths.each do |path|
147
166
  full_path = File.join(path, binary_name)
148
- full_path.tr!('\\', '/') if windows?
149
- exe = Dir.glob(full_path).first
150
- next unless exe
151
- return exe if File.executable?(exe)
167
+ full_path = unix_path(full_path) if windows?
168
+ exe = Dir.glob(full_path).find { |f| File.executable?(f) }
169
+ return exe if exe
152
170
  end
153
171
  end
154
172
 
@@ -158,7 +176,8 @@ module Selenium
158
176
  def find_in_program_files(*binary_names)
159
177
  paths = [
160
178
  ENV['PROGRAMFILES'] || '\\Program Files',
161
- ENV['ProgramFiles(x86)'] || '\\Program Files (x86)'
179
+ ENV['ProgramFiles(x86)'] || '\\Program Files (x86)',
180
+ ENV['ProgramW6432'] || '\\Program Files'
162
181
  ]
163
182
 
164
183
  paths.each do |root|
@@ -175,6 +194,7 @@ module Selenium
175
194
  info = Socket.getaddrinfo 'localhost', 80, Socket::AF_INET, Socket::SOCK_STREAM
176
195
 
177
196
  return info[0][3] unless info.empty?
197
+
178
198
  raise Error::WebDriverError, "unable to translate 'localhost' for TCP + IPv4"
179
199
  end
180
200
 
@@ -203,17 +223,3 @@ module Selenium
203
223
  end # Platform
204
224
  end # WebDriver
205
225
  end # Selenium
206
-
207
- if __FILE__ == $PROGRAM_NAME
208
- p engine: Selenium::WebDriver::Platform.engine,
209
- os: Selenium::WebDriver::Platform.os,
210
- ruby_version: Selenium::WebDriver::Platform.ruby_version,
211
- jruby?: Selenium::WebDriver::Platform.jruby?,
212
- windows?: Selenium::WebDriver::Platform.windows?,
213
- home: Selenium::WebDriver::Platform.home,
214
- bitsize: Selenium::WebDriver::Platform.bitsize,
215
- localhost: Selenium::WebDriver::Platform.localhost,
216
- ip: Selenium::WebDriver::Platform.ip,
217
- interfaces: Selenium::WebDriver::Platform.interfaces,
218
- null_device: Selenium::WebDriver::Platform.null_device
219
- end
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -25,28 +25,17 @@ module Selenium
25
25
  port
26
26
  end
27
27
 
28
- def self.random
29
- # TODO: Avoid this
30
- #
31
- # (a) should pick a port that's guaranteed to be free on all interfaces
32
- # (b) should pick a random port outside the ephemeral port range
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].freeze
42
- IGNORED_ERRORS << Errno::EBADF if Platform.cygwin?
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
43
32
 
44
33
  def self.free?(port)
45
34
  Platform.interfaces.each do |host|
46
35
  begin
47
36
  TCPServer.new(host, port).close
48
- rescue *IGNORED_ERRORS => ex
49
- $stderr.puts "port prober could not bind to #{host}:#{port} (#{ex.message})" if $DEBUG
37
+ rescue *IGNORED_ERRORS => e
38
+ WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})")
50
39
  # ignored - some machines appear unable to bind to some of their interfaces
51
40
  end
52
41
  end
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -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(*)
@@ -46,7 +54,7 @@ module Selenium
46
54
 
47
55
  # TODO: must be a better way..
48
56
  FileUtils.rm_rf tmp_directory
49
- FileUtils.mkdir_p File.dirname(tmp_directory), mode: 0700
57
+ FileUtils.mkdir_p File.dirname(tmp_directory), mode: 0o700
50
58
  FileUtils.cp_r directory, tmp_directory
51
59
 
52
60
  tmp_directory
@@ -63,7 +71,7 @@ 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
76
  # can't use Tempfile here since it doesn't support File::BINARY mode on 1.8
69
77
  # can't use Dir.mktmpdir(&blk) because of http://jira.codehaus.org/browse/JRUBY-4082
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -37,9 +37,10 @@ module Selenium
37
37
  auto_detect: 'autodetect',
38
38
  socks: 'socksProxy',
39
39
  socks_username: 'socksUsername',
40
- socks_password: 'socksPassword'}.freeze
40
+ socks_password: 'socksPassword',
41
+ socks_version: 'socksVersion'}.freeze
41
42
 
42
- ALLOWED.keys.each { |t| attr_reader t }
43
+ ALLOWED.each_key { |t| attr_reader t }
43
44
 
44
45
  def self.json_create(data)
45
46
  data['proxyType'] = data['proxyType'].downcase.to_sym
@@ -66,6 +67,7 @@ module Selenium
66
67
  end
67
68
 
68
69
  return if not_allowed.empty?
70
+
69
71
  raise ArgumentError, "unknown option#{'s' if not_allowed.size != 1}: #{not_allowed.inspect}"
70
72
  end
71
73
 
@@ -119,10 +121,13 @@ module Selenium
119
121
  @socks_password = value
120
122
  end
121
123
 
124
+ def socks_version=(value)
125
+ self.type = :manual
126
+ @socks_version = value
127
+ end
128
+
122
129
  def type=(type)
123
- unless TYPES.key? type
124
- raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}"
125
- end
130
+ raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}" unless TYPES.key? type
126
131
 
127
132
  if defined?(@type) && type != @type
128
133
  raise ArgumentError, "incompatible proxy type #{type.inspect} (already set to #{@type.inspect})"
@@ -142,7 +147,8 @@ module Selenium
142
147
  'autodetect' => auto_detect,
143
148
  'socksProxy' => socks,
144
149
  'socksUsername' => socks_username,
145
- 'socksPassword' => socks_password
150
+ 'socksPassword' => socks_password,
151
+ 'socksVersion' => socks_version
146
152
  }.delete_if { |_k, v| v.nil? }
147
153
 
148
154
  json_result if json_result.length > 1
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -30,25 +30,29 @@ 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
36
37
 
37
38
  #
38
- # Find the first element matching the given arguments.
39
+ # Find the first element matching the given arguments
39
40
  #
40
41
  # When using Element#find_element with :xpath, be aware that webdriver
41
42
  # follows standard conventions: a search prefixed with "//" will search
42
43
  # the entire document, not just the children of this current node. Use
43
44
  # ".//" to limit your search to the children of the receiving Element.
44
45
  #
45
- # @param [:class, :class_name, :css, :id, :link_text, :link, :partial_link_text, :name, :tag_name, :xpath] how
46
- # @param [String] what
46
+ # @overload find_element(how, what)
47
+ # @param [Symbol, String] how The method to find the element by
48
+ # @param [String] what The locator to use
49
+ # @overload find_element(opts)
50
+ # @param [Hash] opts Find options
51
+ # @option opts [Symbol] :how Key named after the method to find the element by, containing the locator
47
52
  # @return [Element]
48
53
  #
49
54
  # @raise [Error::NoSuchElementError] if the element doesn't exist
50
55
  #
51
- #
52
56
 
53
57
  def find_element(*args)
54
58
  how, what = extract_args(args)
@@ -56,8 +60,8 @@ module Selenium
56
60
  by = FINDERS[how.to_sym]
57
61
  raise ArgumentError, "cannot find element by #{how.inspect}" unless by
58
62
 
59
- bridge.find_element_by by, what.to_s, ref
60
- rescue Selenium::WebDriver::Error::TimeOutError
63
+ bridge.find_element_by by, what, ref
64
+ rescue Selenium::WebDriver::Error::TimeoutError
61
65
  # Implicit Wait times out in Edge
62
66
  raise Selenium::WebDriver::Error::NoSuchElementError
63
67
  end
@@ -67,10 +71,6 @@ module Selenium
67
71
  #
68
72
  # @see SearchContext#find_element
69
73
  #
70
- # @param [:class, :class_name, :css, :id, :link_text, :link, :partial_link_text, :name, :tag_name, :xpath] how
71
- # @param [String] what
72
- # @return [Array<Element>]
73
- #
74
74
 
75
75
  def find_elements(*args)
76
76
  how, what = extract_args(args)
@@ -78,8 +78,8 @@ module Selenium
78
78
  by = FINDERS[how.to_sym]
79
79
  raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
80
80
 
81
- bridge.find_elements_by by, what.to_s, ref
82
- rescue Selenium::WebDriver::Error::TimeOutError
81
+ bridge.find_elements_by by, what, ref
82
+ rescue Selenium::WebDriver::Error::TimeoutError
83
83
  # Implicit Wait times out in Edge
84
84
  []
85
85
  end
@@ -93,15 +93,11 @@ module Selenium
93
93
  when 1
94
94
  arg = args.first
95
95
 
96
- unless arg.respond_to?(:shift)
97
- raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift"
98
- end
96
+ raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift" unless arg.respond_to?(:shift)
99
97
 
100
98
  # this will be a single-entry hash, so use #shift over #first or #[]
101
99
  arr = arg.dup.shift
102
- unless arr.size == 2
103
- raise ArgumentError, "expected #{arr.inspect} to have 2 elements"
104
- end
100
+ raise ArgumentError, "expected #{arr.inspect} to have 2 elements" unless arr.size == 2
105
101
 
106
102
  arr
107
103
  else
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -23,36 +23,77 @@ module Selenium
23
23
  # Base class implementing default behavior of service object,
24
24
  # responsible for starting and stopping driver implementations.
25
25
  #
26
- # Subclasses must implement the following private methods:
27
- # * #start_process
28
- # * #stop_server
29
- # * #cannot_connect_error_text
30
- #
31
- # @api private
32
- #
33
26
 
34
27
  class Service
35
- START_TIMEOUT = 20
28
+ START_TIMEOUT = 20
36
29
  SOCKET_LOCK_TIMEOUT = 45
37
- STOP_TIMEOUT = 20
30
+ STOP_TIMEOUT = 20
31
+
32
+ class << self
33
+ attr_reader :driver_path
34
+
35
+ def chrome(**opts)
36
+ Chrome::Service.new(**opts)
37
+ end
38
+
39
+ def firefox(**opts)
40
+ Firefox::Service.new(**opts)
41
+ end
42
+
43
+ def ie(**opts)
44
+ IE::Service.new(**opts)
45
+ end
46
+ alias_method :internet_explorer, :ie
47
+
48
+ def edge(**opts)
49
+ Edge::Service.new(**opts)
50
+ end
51
+
52
+ def edge_chrome(**opts)
53
+ EdgeChrome::Service.new(**opts)
54
+ end
55
+
56
+ def edge_html(**opts)
57
+ EdgeHtml::Service.new(**opts)
58
+ end
59
+
60
+ def safari(**opts)
61
+ Safari::Service.new(**opts)
62
+ end
63
+
64
+ def driver_path=(path)
65
+ Platform.assert_executable path if path.is_a?(String)
66
+ @driver_path = path
67
+ end
68
+ end
38
69
 
39
70
  attr_accessor :host
71
+ attr_reader :executable_path
72
+
73
+ #
74
+ # End users should use a class method for the desired driver, rather than using this directly.
75
+ #
76
+ # @api private
77
+ #
78
+
79
+ def initialize(path: nil, port: nil, args: nil)
80
+ path ||= self.class.driver_path
81
+ port ||= self.class::DEFAULT_PORT
82
+ args ||= []
83
+
84
+ @executable_path = binary_path(path)
85
+ @host = Platform.localhost
86
+ @port = Integer(port)
40
87
 
41
- def initialize(executable_path, port, *extra_args)
42
- @executable_path = executable_path
43
- @host = Platform.localhost
44
- @port = Integer(port)
45
- @extra_args = extra_args
88
+ @extra_args = args.is_a?(Hash) ? extract_service_args(args) : args
46
89
 
47
90
  raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
48
91
  end
49
92
 
50
93
  def start
51
- if process_running?
52
- raise "already started: #{uri.inspect} #{@executable_path.inspect}"
53
- end
94
+ raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
54
95
 
55
- Platform.exit_hook { stop } # make sure we don't leave the server running
96
+ Platform.exit_hook(&method(:stop)) # make sure we don't leave the server running
56
97
 
57
98
  socket_lock.locked do
58
99
  find_free_port
@@ -62,8 +103,12 @@ module Selenium
62
103
  end
63
104
 
64
105
  def stop
65
- return if process_exited?
106
+ return unless self.class::SHUTDOWN_SUPPORTED
107
+
66
108
  stop_server
109
+ @process.poll_for_exit STOP_TIMEOUT
110
+ rescue ChildProcess::TimeoutError
111
+ nil # noop
67
112
  ensure
68
113
  stop_process
69
114
  end
@@ -74,6 +119,29 @@ module Selenium
74
119
 
75
120
  private
76
121
 
122
+ def binary_path(path = nil)
123
+ path = path.call if path.is_a?(Proc)
124
+ path ||= Platform.find_binary(self.class::EXECUTABLE)
125
+
126
+ raise Error::WebDriverError, self.class::MISSING_TEXT unless path
127
+
128
+ Platform.assert_executable path
129
+ path
130
+ end
131
+
132
+ def build_process(*command)
133
+ WebDriver.logger.debug("Executing Process #{command}")
134
+ @process = ChildProcess.build(*command)
135
+ if WebDriver.logger.debug?
136
+ @process.io.stdout = @process.io.stderr = WebDriver.logger.io
137
+ elsif Platform.jruby?
138
+ # Apparently we need to read the output of drivers on JRuby.
139
+ @process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
140
+ end
141
+
142
+ @process
143
+ end
144
+
77
145
  def connect_to_server
78
146
  Net::HTTP.start(@host, @port) do |http|
79
147
  http.open_timeout = STOP_TIMEOUT / 2
@@ -88,21 +156,30 @@ module Selenium
88
156
  end
89
157
 
90
158
  def start_process
91
- raise NotImplementedError, 'subclass responsibility'
92
- end
93
-
94
- def stop_server
95
- raise NotImplementedError, 'subclass responsibility'
159
+ @process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
160
+ # Note: this is a bug only in Windows 7
161
+ @process.leader = true unless Platform.windows?
162
+ @process.start
96
163
  end
97
164
 
98
165
  def stop_process
99
- @process.poll_for_exit STOP_TIMEOUT
100
- rescue ChildProcess::TimeoutError
166
+ return if process_exited?
167
+
101
168
  @process.stop STOP_TIMEOUT
169
+ @process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
170
+ end
171
+
172
+ def stop_server
173
+ return if process_exited?
174
+
175
+ connect_to_server do |http|
176
+ headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
177
+ http.get('/shutdown', headers)
178
+ end
102
179
  end
103
180
 
104
181
  def process_running?
105
- @process && @process.alive?
182
+ defined?(@process) && @process&.alive?
106
183
  end
107
184
 
108
185
  def process_exited?
@@ -112,16 +189,24 @@ module Selenium
112
189
  def connect_until_stable
113
190
  socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
114
191
  return if socket_poller.connected?
192
+
115
193
  raise Error::WebDriverError, cannot_connect_error_text
116
194
  end
117
195
 
118
196
  def cannot_connect_error_text
119
- raise NotImplementedError, 'subclass responsibility'
197
+ "unable to connect to #{self.class::EXECUTABLE} #{@host}:#{@port}"
120
198
  end
121
199
 
122
200
  def socket_lock
123
201
  @socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
124
202
  end
203
+
204
+ protected
205
+
206
+ def extract_service_args(driver_opts)
207
+ driver_opts.key?(:args) ? driver_opts.delete(:args) : []
208
+ end
209
+
125
210
  end # Service
126
211
  end # WebDriver
127
212
  end # Selenium