selenium-webdriver 4.0.0.alpha3 → 4.0.0.alpha4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +50 -1
  3. data/lib/selenium/server.rb +1 -1
  4. data/lib/selenium/webdriver.rb +2 -0
  5. data/lib/selenium/webdriver/atoms/findElements.js +122 -0
  6. data/lib/selenium/webdriver/atoms/getAttribute.js +84 -7
  7. data/lib/selenium/webdriver/atoms/isDisplayed.js +75 -77
  8. data/lib/selenium/webdriver/chrome.rb +4 -2
  9. data/lib/selenium/webdriver/chrome/driver.rb +1 -16
  10. data/lib/selenium/webdriver/chrome/options.rb +1 -1
  11. data/lib/selenium/webdriver/chrome/service.rb +0 -4
  12. data/lib/selenium/webdriver/common.rb +1 -0
  13. data/lib/selenium/webdriver/common/driver.rb +25 -5
  14. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +38 -0
  15. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +2 -1
  16. data/lib/selenium/webdriver/common/logger.rb +47 -15
  17. data/lib/selenium/webdriver/common/options.rb +2 -1
  18. data/lib/selenium/webdriver/common/platform.rb +3 -0
  19. data/lib/selenium/webdriver/common/port_prober.rb +2 -2
  20. data/lib/selenium/webdriver/common/proxy.rb +0 -0
  21. data/lib/selenium/webdriver/common/search_context.rb +3 -2
  22. data/lib/selenium/webdriver/common/service.rb +0 -13
  23. data/lib/selenium/webdriver/common/socket_lock.rb +2 -2
  24. data/lib/selenium/webdriver/common/wait.rb +1 -1
  25. data/lib/selenium/webdriver/edge.rb +4 -2
  26. data/lib/selenium/webdriver/edge_chrome/driver.rb +1 -1
  27. data/lib/selenium/webdriver/edge_chrome/service.rb +0 -4
  28. data/lib/selenium/webdriver/edge_html/driver.rb +1 -15
  29. data/lib/selenium/webdriver/edge_html/options.rb +1 -1
  30. data/lib/selenium/webdriver/edge_html/service.rb +1 -5
  31. data/lib/selenium/webdriver/firefox.rb +10 -4
  32. data/lib/selenium/webdriver/firefox/driver.rb +0 -16
  33. data/lib/selenium/webdriver/firefox/options.rb +4 -1
  34. data/lib/selenium/webdriver/firefox/profile.rb +4 -77
  35. data/lib/selenium/webdriver/firefox/service.rb +0 -4
  36. data/lib/selenium/webdriver/ie.rb +4 -2
  37. data/lib/selenium/webdriver/ie/driver.rb +0 -15
  38. data/lib/selenium/webdriver/ie/service.rb +5 -9
  39. data/lib/selenium/webdriver/remote/bridge.rb +31 -29
  40. data/lib/selenium/webdriver/remote/capabilities.rb +1 -0
  41. data/lib/selenium/webdriver/remote/driver.rb +6 -12
  42. data/lib/selenium/webdriver/remote/http/default.rb +3 -3
  43. data/lib/selenium/webdriver/safari.rb +4 -2
  44. data/lib/selenium/webdriver/safari/driver.rb +0 -16
  45. data/lib/selenium/webdriver/safari/service.rb +0 -4
  46. data/lib/selenium/webdriver/support.rb +1 -0
  47. data/lib/selenium/webdriver/support/event_firing_bridge.rb +1 -1
  48. data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
  49. data/lib/selenium/webdriver/version.rb +1 -1
  50. data/selenium-webdriver.gemspec +4 -3
  51. metadata +24 -15
  52. data/lib/selenium/webdriver/firefox/binary.rb +0 -110
  53. data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
@@ -30,13 +30,15 @@ module Selenium
30
30
 
31
31
  def self.driver_path=(path)
32
32
  WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome#driver_path=',
33
- 'Selenium::WebDriver::Chrome::Service#driver_path='
33
+ 'Selenium::WebDriver::Chrome::Service#driver_path=',
34
+ id: :driver_path
34
35
  Selenium::WebDriver::Chrome::Service.driver_path = path
35
36
  end
36
37
 
37
38
  def self.driver_path
38
39
  WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome#driver_path',
39
- 'Selenium::WebDriver::Chrome::Service#driver_path'
40
+ 'Selenium::WebDriver::Chrome::Service#driver_path',
41
+ id: :driver_path
40
42
  Selenium::WebDriver::Chrome::Service.driver_path
41
43
  end
42
44
 
@@ -32,22 +32,7 @@ module Selenium
32
32
  include DriverExtensions::HasLocation
33
33
  include DriverExtensions::TakesScreenshot
34
34
  include DriverExtensions::DownloadsFiles
35
-
36
- def initialize(opts = {})
37
- opts[:desired_capabilities] ||= Remote::Capabilities.send(browser)
38
-
39
- opts[:url] ||= service_url(opts)
40
-
41
- listener = opts.delete(:listener)
42
- desired_capabilities = opts.delete(:desired_capabilities)
43
- options = opts.delete(:options)
44
-
45
- @bridge = Remote::Bridge.new(opts)
46
- @bridge.extend Bridge
47
- @bridge.create_session(desired_capabilities, options)
48
-
49
- super(@bridge, listener: listener)
50
- end
35
+ include DriverExtensions::HasDevTools
51
36
 
52
37
  def browser
53
38
  :chrome
@@ -169,7 +169,7 @@ module Selenium
169
169
  # options = Selenium::WebDriver::Chrome::Options.new
170
170
  # options.add_emulation(device_metrics: {width: 400, height: 800, pixelRatio: 1, touch: true})
171
171
  #
172
- # @param [Hash] opts the pre-defined options for adding mobilie emulation values
172
+ # @param [Hash] opts the pre-defined options for adding mobile emulation values
173
173
  # @option opts [String] :device_name A valid device name from the Chrome DevTools Emulation panel
174
174
  # @option opts [Hash] :device_metrics Hash containing width, height, pixelRatio, touch
175
175
  # @option opts [String] :user_agent Full user agent
@@ -20,10 +20,6 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Chrome
23
- #
24
- # @api private
25
- #
26
-
27
23
  class Service < WebDriver::Service
28
24
  DEFAULT_PORT = 9515
29
25
  EXECUTABLE = 'chromedriver'
@@ -62,6 +62,7 @@ require 'selenium/webdriver/common/driver_extensions/has_permissions'
62
62
  require 'selenium/webdriver/common/driver_extensions/has_debugger'
63
63
  require 'selenium/webdriver/common/driver_extensions/uploads_files'
64
64
  require 'selenium/webdriver/common/driver_extensions/has_addons'
65
+ require 'selenium/webdriver/common/driver_extensions/has_devtools'
65
66
  require 'selenium/webdriver/common/keys'
66
67
  require 'selenium/webdriver/common/profile_helper'
67
68
  require 'selenium/webdriver/common/options'
@@ -71,10 +71,10 @@ module Selenium
71
71
  # @api private
72
72
  #
73
73
 
74
- def initialize(bridge, listener: nil)
74
+ def initialize(bridge: nil, listener: nil, **opts)
75
75
  @service = nil
76
- @bridge = bridge
77
- @bridge = Support::EventFiringBridge.new(bridge, listener) if listener
76
+ bridge ||= create_bridge(opts)
77
+ @bridge = listener ? Support::EventFiringBridge.new(bridge, listener) : bridge
78
78
  end
79
79
 
80
80
  def inspect
@@ -276,7 +276,7 @@ module Selenium
276
276
  end
277
277
 
278
278
  def browser
279
- bridge.browser
279
+ bridge&.browser
280
280
  end
281
281
 
282
282
  def capabilities
@@ -294,12 +294,32 @@ module Selenium
294
294
 
295
295
  attr_reader :bridge
296
296
 
297
+ def create_bridge(**opts)
298
+ opts[:url] ||= service_url(opts)
299
+
300
+ desired_capabilities = opts.delete(:desired_capabilities) || Remote::Capabilities.send(browser || :new)
301
+ options = opts.delete(:options)
302
+
303
+ bridge = Remote::Bridge.new(http_client: opts.delete(:http_client), url: opts.delete(:url))
304
+ raise ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
305
+
306
+ namespacing = self.class.to_s.split('::')
307
+
308
+ if Object.const_defined?("#{namespacing[0..-2].join('::')}::Bridge") && !namespacing.include?('Remote')
309
+ bridge.extend Object.const_get("#{namespacing[0, namespacing.length - 1].join('::')}::Bridge")
310
+ end
311
+
312
+ bridge.create_session(desired_capabilities, options)
313
+ bridge
314
+ end
315
+
297
316
  def service_url(opts)
298
317
  @service = opts.delete(:service)
299
318
  %i[driver_opts driver_path port].each do |key|
300
319
  next unless opts.key? key
301
320
 
302
- WebDriver.logger.deprecate(":#{key}", ':service with an instance of Selenium::WebDriver::Service')
321
+ WebDriver.logger.deprecate(":#{key}", ':service with an instance of Selenium::WebDriver::Service',
322
+ id: "service_#{key}".to_sym)
303
323
  end
304
324
  @service ||= Service.send(browser,
305
325
  args: opts.delete(:driver_opts),
@@ -0,0 +1,38 @@
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
+ module Selenium
21
+ module WebDriver
22
+ module DriverExtensions
23
+ module HasDevTools
24
+
25
+ #
26
+ # Retrieves connection to DevTools.
27
+ #
28
+ # @return [DevTools]
29
+ #
30
+
31
+ def devtools
32
+ @devtools ||= DevTools.new(capabilities['goog:chromeOptions']['debuggerAddress'])
33
+ end
34
+
35
+ end # HasDevTools
36
+ end # DriverExtensions
37
+ end # WebDriver
38
+ end # Selenium
@@ -35,7 +35,8 @@ module Selenium
35
35
  extension = File.extname(png_path).downcase
36
36
  if extension != '.png'
37
37
  WebDriver.logger.warn "name used for saved screenshot does not match file type. "\
38
- "It should end with .png extension"
38
+ "It should end with .png extension",
39
+ id: :screenshot
39
40
  end
40
41
  File.open(png_path, 'wb') { |f| f << screenshot_as(:png) }
41
42
  end
@@ -45,8 +45,12 @@ module Selenium
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,60 @@ 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<Sybmol>] id
112
+ # @yield appends additional message to end of provided template
81
113
  #
82
- def deprecate(old, new = nil)
83
- message = +"[DEPRECATION] #{old} is deprecated"
114
+ def deprecate(old, new = nil, id: [], &block)
115
+ id = Array(id)
116
+ return if @ignored.include?(:deprecations) || (@ignored & id).any?
117
+
118
+ ids = id.empty? ? '' : "[#{id.map(&:inspect).join(', ')}] "
119
+
120
+ message = +"[DEPRECATION] #{ids}#{old} is deprecated"
84
121
  message << if new
85
122
  ". Use #{new} instead."
86
123
  else
87
- ' and will be removed in the next releases.'
124
+ ' and will be removed in a future release.'
88
125
  end
89
-
90
- warn message
126
+ warn message, &block
91
127
  end
92
128
 
93
129
  private
94
130
 
95
- def create_logger(output)
96
- logger = ::Logger.new(output)
97
- logger.progname = 'Selenium'
131
+ def create_logger(name)
132
+ logger = ::Logger.new($stdout)
133
+ logger.progname = name
98
134
  logger.level = default_level
99
135
  logger.formatter = proc do |severity, time, progname, msg|
100
136
  "#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n"
@@ -104,11 +140,7 @@ module Selenium
104
140
  end
105
141
 
106
142
  def default_level
107
- if $DEBUG || ENV.key?('DEBUG')
108
- :debug
109
- else
110
- :warn
111
- end
143
+ $DEBUG || ENV.key?('DEBUG') ? :debug : :warn
112
144
  end
113
145
  end # Logger
114
146
  end # WebDriver
@@ -25,7 +25,8 @@ module Selenium
25
25
  def initialize(options: nil, **opts)
26
26
  @options = if options
27
27
  WebDriver.logger.deprecate(":options as keyword for initializing #{self.class}",
28
- "custom values directly in #new constructor")
28
+ "custom values directly in #new constructor",
29
+ id: :options_options)
29
30
  opts.merge(options)
30
31
  else
31
32
  opts
@@ -96,6 +96,9 @@ module Selenium
96
96
  return false unless linux?
97
97
 
98
98
  File.read('/proc/version').include?('Microsoft')
99
+ rescue Errno::EACCES
100
+ # the file cannot be accessed on Linux on DeX
101
+ false
99
102
  end
100
103
 
101
104
  def cygwin?
@@ -34,8 +34,8 @@ module Selenium
34
34
  Platform.interfaces.each do |host|
35
35
  begin
36
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})")
37
+ rescue *IGNORED_ERRORS => e
38
+ WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})")
39
39
  # ignored - some machines appear unable to bind to some of their interfaces
40
40
  end
41
41
  end
File without changes
@@ -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,7 +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
+ bridge.find_element_by by, what, ref
63
64
  rescue Selenium::WebDriver::Error::TimeoutError
64
65
  # Implicit Wait times out in Edge
65
66
  raise Selenium::WebDriver::Error::NoSuchElementError
@@ -77,7 +78,7 @@ module Selenium
77
78
  by = FINDERS[how.to_sym]
78
79
  raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
79
80
 
80
- bridge.find_elements_by by, what.to_s, ref
81
+ bridge.find_elements_by by, what, ref
81
82
  rescue Selenium::WebDriver::Error::TimeoutError
82
83
  # Implicit Wait times out in Edge
83
84
  []
@@ -37,19 +37,6 @@ module Selenium
37
37
  end
38
38
 
39
39
  def firefox(**opts)
40
- binary_path = Firefox::Binary.path
41
- args = opts.delete(:args)
42
- case args
43
- when Hash
44
- args[:binary] ||= binary_path
45
- opts[:args] = args
46
- when Array
47
- opts[:args] = ["--binary=#{binary_path}"]
48
- opts[:args] += args
49
- else
50
- opts[:args] = ["--binary=#{binary_path}"]
51
- end
52
-
53
40
  Firefox::Service.new(**opts)
54
41
  end
55
42
 
@@ -69,8 +69,8 @@ module Selenium
69
69
  ChildProcess.close_on_exec @server
70
70
 
71
71
  true
72
- rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => ex
73
- WebDriver.logger.debug("#{self}: #{ex.message}")
72
+ rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => e
73
+ WebDriver.logger.debug("#{self}: #{e.message}")
74
74
  false
75
75
  end
76
76
 
@@ -55,7 +55,7 @@ module Selenium
55
55
  begin
56
56
  result = yield
57
57
  return result if result
58
- rescue *@ignored => last_error
58
+ rescue *@ignored => last_error # rubocop:disable Naming/RescuedExceptionsVariableName
59
59
  # swallowed
60
60
  end
61
61
 
@@ -28,13 +28,15 @@ module Selenium
28
28
 
29
29
  def self.driver_path=(path)
30
30
  WebDriver.logger.deprecate 'Selenium::WebDriver::Edge#driver_path=',
31
- 'Selenium::WebDriver::Edge::Service#driver_path='
31
+ 'Selenium::WebDriver::Edge::Service#driver_path=',
32
+ id: :driver_path
32
33
  Selenium::WebDriver::Edge::Service.driver_path = path
33
34
  end
34
35
 
35
36
  def self.driver_path
36
37
  WebDriver.logger.deprecate 'Selenium::WebDriver::Edge#driver_path',
37
- 'Selenium::WebDriver::Edge::Service#driver_path'
38
+ 'Selenium::WebDriver::Edge::Service#driver_path',
39
+ id: :driver_path
38
40
  Selenium::WebDriver::Edge::Service.driver_path
39
41
  end
40
42
  end # EdgeHtml
@@ -33,6 +33,6 @@ module Selenium
33
33
  :edge_chrome
34
34
  end
35
35
  end # Driver
36
- end # Chrome
36
+ end # EdgeChrome
37
37
  end # WebDriver
38
38
  end # Selenium
@@ -22,10 +22,6 @@ require 'selenium/webdriver/chrome/service'
22
22
  module Selenium
23
23
  module WebDriver
24
24
  module EdgeChrome
25
- #
26
- # @api private
27
- #
28
-
29
25
  class Service < Selenium::WebDriver::Chrome::Service
30
26
  DEFAULT_PORT = 9515
31
27
  EXECUTABLE = 'msedgedriver'
@@ -30,24 +30,10 @@ module Selenium
30
30
  include DriverExtensions::HasWebStorage
31
31
  include DriverExtensions::TakesScreenshot
32
32
 
33
- def initialize(opts = {})
34
- opts[:desired_capabilities] ||= Remote::Capabilities.edge
35
-
36
- opts[:url] ||= service_url(opts)
37
-
38
- listener = opts.delete(:listener)
39
- desired_capabilities = opts.delete(:desired_capabilities)
40
-
41
- @bridge = Remote::Bridge.new(opts)
42
- @bridge.create_session(desired_capabilities)
43
-
44
- super(@bridge, listener: listener)
45
- end
46
-
47
33
  def browser
48
34
  :edge
49
35
  end
50
36
  end # Driver
51
- end # Edge
37
+ end # EdgeHtml
52
38
  end # WebDriver
53
39
  end # Selenium
@@ -86,6 +86,6 @@ module Selenium
86
86
  raise Error::WebDriverError, "could not find extension at #{path.inspect}" unless File.directory?(path)
87
87
  end
88
88
  end # Options
89
- end # Edge
89
+ end # EdgeHtml
90
90
  end # WebDriver
91
91
  end # Selenium