selenium-webdriver 4.0.0.alpha3 → 4.0.0.alpha4

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 (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
@@ -20,10 +20,6 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module EdgeHtml
23
- #
24
- # @api private
25
- #
26
-
27
23
  class Service < WebDriver::Service
28
24
  DEFAULT_PORT = 17556
29
25
  EXECUTABLE = 'MicrosoftWebDriver'
@@ -46,6 +42,6 @@ module Selenium
46
42
  driver_args
47
43
  end
48
44
  end # Service
49
- end # Edge
45
+ end # EdgeHtml
50
46
  end # WebDriver
51
47
  end # Service
@@ -25,7 +25,6 @@ module Selenium
25
25
  module WebDriver
26
26
  module Firefox
27
27
  autoload :Extension, 'selenium/webdriver/firefox/extension'
28
- autoload :Binary, 'selenium/webdriver/firefox/binary'
29
28
  autoload :ProfilesIni, 'selenium/webdriver/firefox/profiles_ini'
30
29
  autoload :Profile, 'selenium/webdriver/firefox/profile'
31
30
  autoload :Bridge, 'selenium/webdriver/firefox/bridge'
@@ -41,18 +40,25 @@ module Selenium
41
40
 
42
41
  def self.driver_path=(path)
43
42
  WebDriver.logger.deprecate 'Selenium::WebDriver::Firefox#driver_path=',
44
- 'Selenium::WebDriver::Firefox::Service#driver_path='
43
+ 'Selenium::WebDriver::Firefox::Service#driver_path=',
44
+ id: :driver_path
45
45
  Selenium::WebDriver::Firefox::Service.driver_path = path
46
46
  end
47
47
 
48
48
  def self.driver_path
49
49
  WebDriver.logger.deprecate 'Selenium::WebDriver::Firefox#driver_path',
50
- 'Selenium::WebDriver::Firefox::Service#driver_path'
50
+ 'Selenium::WebDriver::Firefox::Service#driver_path',
51
+ id: :driver_path
51
52
  Selenium::WebDriver::Firefox::Service.driver_path
52
53
  end
53
54
 
54
55
  def self.path=(path)
55
- Binary.path = path
56
+ Platform.assert_executable path
57
+ @path = path
58
+ end
59
+
60
+ def self.path
61
+ @path ||= nil
56
62
  end
57
63
  end # Firefox
58
64
  end # WebDriver
@@ -31,22 +31,6 @@ module Selenium
31
31
  include DriverExtensions::HasWebStorage
32
32
  include DriverExtensions::TakesScreenshot
33
33
 
34
- def initialize(opts = {})
35
- opts[:desired_capabilities] ||= Remote::Capabilities.firefox
36
-
37
- opts[:url] ||= service_url(opts)
38
-
39
- listener = opts.delete(:listener)
40
- desired_capabilities = opts.delete(:desired_capabilities)
41
- options = opts.delete(:options)
42
-
43
- @bridge = Remote::Bridge.new(opts)
44
- @bridge.extend Bridge
45
- @bridge.create_session(desired_capabilities, options)
46
-
47
- super(@bridge, listener: listener)
48
- end
49
-
50
34
  def browser
51
35
  :firefox
52
36
  end
@@ -139,7 +139,10 @@ module Selenium
139
139
  #
140
140
 
141
141
  def as_json(*)
142
- {KEY => generate_as_json(super)}
142
+ options = super
143
+ options['binary'] ||= Firefox.path if Firefox.path
144
+
145
+ {KEY => generate_as_json(options)}
143
146
  end
144
147
 
145
148
  private
@@ -23,18 +23,10 @@ module Selenium
23
23
  class Profile
24
24
  include ProfileHelper
25
25
 
26
- VALID_PREFERENCE_TYPES = [TrueClass, FalseClass, Integer, Float, String].freeze
27
- WEBDRIVER_EXTENSION_PATH = File.expand_path("#{WebDriver.root}/selenium/webdriver/firefox/extension/webdriver.xpi")
28
- WEBDRIVER_PREFS = {
29
- native_events: 'webdriver_enable_native_events',
30
- untrusted_certs: 'webdriver_accept_untrusted_certs',
31
- untrusted_issuer: 'webdriver_assume_untrusted_issuer',
32
- port: 'webdriver_firefox_port',
33
- log_file: 'webdriver.log.file'
34
- }.freeze
26
+ VALID_PREFERENCE_TYPES = [TrueClass, FalseClass, Integer, Float, String].freeze
35
27
 
36
28
  attr_reader :name, :log_file
37
- attr_writer :secure_ssl, :native_events, :load_no_focus_lib
29
+ attr_writer :secure_ssl, :load_no_focus_lib
38
30
 
39
31
  class << self
40
32
  def ini
@@ -48,12 +40,6 @@ module Selenium
48
40
  raise Error::WebDriverError, "unable to find profile named: #{name.inspect}"
49
41
  end
50
42
 
51
- def default_preferences
52
- @default_preferences ||= JSON.parse(
53
- File.read(File.expand_path("#{WebDriver.root}/selenium/webdriver/firefox/extension/prefs.json"))
54
- ).freeze
55
- end
56
-
57
43
  def decoded(json)
58
44
  JSON.parse(json)
59
45
  end
@@ -74,14 +60,7 @@ module Selenium
74
60
  def initialize(model = nil)
75
61
  @model = verify_model(model)
76
62
 
77
- model_prefs = read_model_prefs
78
-
79
- if model_prefs.empty?
80
- assign_default_preferences
81
- else
82
- assign_updated_preferences(model_prefs)
83
- end
84
-
63
+ @additional_prefs = read_model_prefs
85
64
  @extensions = {}
86
65
  end
87
66
 
@@ -125,12 +104,6 @@ module Selenium
125
104
  self[WEBDRIVER_PREFS[:log_file]] = file
126
105
  end
127
106
 
128
- def add_webdriver_extension
129
- return if @extensions.key?(:webdriver)
130
-
131
- add_extension(WEBDRIVER_EXTENSION_PATH, :webdriver)
132
- end
133
-
134
107
  #
135
108
  # Add the extension (directory, .zip or .xpi) at the given path to the profile.
136
109
  #
@@ -139,26 +112,6 @@ module Selenium
139
112
  @extensions[name] = Extension.new(path)
140
113
  end
141
114
 
142
- def native_events?
143
- @native_events == true
144
- end
145
-
146
- def load_no_focus_lib?
147
- @load_no_focus_lib == true
148
- end
149
-
150
- def secure_ssl?
151
- @secure_ssl == true
152
- end
153
-
154
- def assume_untrusted_certificate_issuer?
155
- @untrusted_issuer == true
156
- end
157
-
158
- def assume_untrusted_certificate_issuer=(bool)
159
- @untrusted_issuer = bool
160
- end
161
-
162
115
  def proxy=(proxy)
163
116
  raise TypeError, "expected #{Proxy.name}, got #{proxy.inspect}:#{proxy.class}" unless proxy.is_a? Proxy
164
117
 
@@ -186,24 +139,6 @@ module Selenium
186
139
 
187
140
  private
188
141
 
189
- def assign_default_preferences
190
- @native_events = DEFAULT_ENABLE_NATIVE_EVENTS
191
- @secure_ssl = DEFAULT_SECURE_SSL
192
- @untrusted_issuer = DEFAULT_ASSUME_UNTRUSTED_ISSUER
193
- @load_no_focus_lib = DEFAULT_LOAD_NO_FOCUS_LIB
194
-
195
- @additional_prefs = {}
196
- end
197
-
198
- def assign_updated_preferences(model_prefs)
199
- @native_events = model_prefs.delete(WEBDRIVER_PREFS[:native_events]) == 'true'
200
- @secure_ssl = model_prefs.delete(WEBDRIVER_PREFS[:untrusted_certs]) != 'true'
201
- @untrusted_issuer = model_prefs.delete(WEBDRIVER_PREFS[:untrusted_issuer]) == 'true'
202
- # not stored in profile atm, so will always be false.
203
- @load_no_focus_lib = model_prefs.delete(WEBDRIVER_PREFS[:load_no_focus_lib]) == 'true'
204
- @additional_prefs = model_prefs
205
- end
206
-
207
142
  def set_manual_proxy_preference(key, value)
208
143
  return unless value
209
144
 
@@ -244,15 +179,7 @@ module Selenium
244
179
 
245
180
  def update_user_prefs_in(directory)
246
181
  path = File.join(directory, 'user.js')
247
- prefs = read_user_prefs(path)
248
-
249
- prefs.merge! self.class.default_preferences.fetch 'mutable'
250
- prefs.merge! @additional_prefs
251
- prefs.merge! self.class.default_preferences.fetch 'frozen'
252
-
253
- prefs[WEBDRIVER_PREFS[:untrusted_certs]] = !secure_ssl?
254
- prefs[WEBDRIVER_PREFS[:native_events]] = native_events?
255
- prefs[WEBDRIVER_PREFS[:untrusted_issuer]] = assume_untrusted_certificate_issuer?
182
+ prefs = read_user_prefs(path).merge(@additional_prefs)
256
183
 
257
184
  # If the user sets the home page, we should also start up there
258
185
  prefs['startup.homepage_welcome_url'] = prefs['browser.startup.homepage']
@@ -20,10 +20,6 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Firefox
23
- #
24
- # @api private
25
- #
26
-
27
23
  class Service < WebDriver::Service
28
24
  DEFAULT_PORT = 4444
29
25
  EXECUTABLE = 'geckodriver'
@@ -26,13 +26,15 @@ module Selenium
26
26
 
27
27
  def self.driver_path=(path)
28
28
  WebDriver.logger.deprecate 'Selenium::WebDriver::IE#driver_path=',
29
- 'Selenium::WebDriver::IE::Service#driver_path='
29
+ 'Selenium::WebDriver::IE::Service#driver_path=',
30
+ id: :driver_path
30
31
  Selenium::WebDriver::IE::Service.driver_path = path
31
32
  end
32
33
 
33
34
  def self.driver_path
34
35
  WebDriver.logger.deprecate 'Selenium::WebDriver::IE#driver_path',
35
- 'Selenium::WebDriver::IE::Service#driver_path'
36
+ 'Selenium::WebDriver::IE::Service#driver_path',
37
+ id: :driver_path
36
38
  Selenium::WebDriver::IE::Service.driver_path
37
39
  end
38
40
  end # IE
@@ -31,21 +31,6 @@ module Selenium
31
31
  include DriverExtensions::HasWebStorage
32
32
  include DriverExtensions::TakesScreenshot
33
33
 
34
- def initialize(opts = {})
35
- opts[:desired_capabilities] ||= Remote::Capabilities.internet_explorer
36
-
37
- opts[:url] ||= service_url(opts)
38
-
39
- listener = opts.delete(:listener)
40
- desired_capabilities = opts.delete(:desired_capabilities)
41
- options = opts.delete(:options)
42
-
43
- @bridge = Remote::Bridge.new(opts)
44
- @bridge.create_session(desired_capabilities, options)
45
-
46
- super(@bridge, listener: listener)
47
- end
48
-
49
34
  def browser
50
35
  :internet_explorer
51
36
  end
@@ -20,10 +20,6 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module IE
23
- #
24
- # @api private
25
- #
26
-
27
23
  class Service < WebDriver::Service
28
24
  DEFAULT_PORT = 5555
29
25
  EXECUTABLE = 'IEDriverServer'
@@ -40,11 +36,11 @@ module Selenium
40
36
  def extract_service_args(driver_opts)
41
37
  driver_args = super
42
38
  driver_opts = driver_opts.dup
43
- driver_args << "--log-level=#{driver_opts.delete(:log_level).to_s.upcase}" if driver_opts.key?(:log_level)
44
- driver_args << "--log-file=#{driver_opts.delete(:log_file)}" if driver_opts.key?(:log_file)
45
- driver_args << "--implementation=#{driver_opts.delete(:implementation).to_s.upcase}" if driver_opts.key?(:implementation)
46
- driver_args << "--host=#{driver_opts.delete(:host)}" if driver_opts.key?(:host)
47
- driver_args << "--extract_path=#{driver_opts.delete(:extract_path)}" if driver_opts.key?(:extract_path)
39
+ driver_args << "--log-level=#{driver_opts[:log_level].to_s.upcase}" if driver_opts.key?(:log_level)
40
+ driver_args << "--log-file=#{driver_opts[:log_file]}" if driver_opts.key?(:log_file)
41
+ driver_args << "--implementation=#{driver_opts[:implementation].to_s.upcase}" if driver_opts.key?(:implementation)
42
+ driver_args << "--host=#{driver_opts[:host]}" if driver_opts.key?(:host)
43
+ driver_args << "--extract_path=#{driver_opts[:extract_path]}" if driver_opts.key?(:extract_path)
48
44
  driver_args << "--silent" if driver_opts[:silent] == true
49
45
  driver_args
50
46
  end
@@ -30,30 +30,17 @@ module Selenium
30
30
 
31
31
  #
32
32
  # Initializes the bridge with the given server URL
33
- # @param [Hash] opts options for the driver
34
- # @option opts [String] :url url for the remote server
35
- # @option opts [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
36
- # @option opts [Capabilities] :desired_capabilities an instance of Remote::Capabilities describing the capabilities you want
33
+ # @param [String, URI] :url url for the remote server
34
+ # @param [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
37
35
  # @api private
38
36
  #
39
37
 
40
- def initialize(opts = {})
41
- opts = opts.dup
42
-
43
- http_client = opts.delete(:http_client) { Http::Default.new }
44
- url = opts.delete(:url) { "http://#{Platform.localhost}:#{PORT}/wd/hub" }
45
- opts.delete(:options)
46
-
47
- unless opts.empty?
48
- raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
49
- end
50
-
38
+ def initialize(http_client: nil, url:)
51
39
  uri = url.is_a?(URI) ? url : URI.parse(url)
52
40
  uri.path += '/' unless %r{\/$}.match?(uri.path)
53
41
 
54
- http_client.server_url = uri
55
-
56
- @http = http_client
42
+ @http = http_client || Http::Default.new
43
+ @http.server_url = uri
57
44
  @file_detector = nil
58
45
  end
59
46
 
@@ -525,23 +512,28 @@ module Selenium
525
512
  alias_method :switch_to_active_element, :active_element
526
513
 
527
514
  def find_element_by(how, what, parent = nil)
528
- how, what = convert_locators(how, what)
515
+ how, what = convert_locator(how, what)
516
+
517
+ return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
529
518
 
530
519
  id = if parent
531
- execute :find_child_element, {id: parent}, {using: how, value: what}
520
+ execute :find_child_element, {id: parent}, {using: how, value: what.to_s}
532
521
  else
533
- execute :find_element, {}, {using: how, value: what}
522
+ execute :find_element, {}, {using: how, value: what.to_s}
534
523
  end
524
+
535
525
  Element.new self, element_id_from(id)
536
526
  end
537
527
 
538
528
  def find_elements_by(how, what, parent = nil)
539
- how, what = convert_locators(how, what)
529
+ how, what = convert_locator(how, what)
530
+
531
+ return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
540
532
 
541
533
  ids = if parent
542
- execute :find_child_elements, {id: parent}, {using: how, value: what}
534
+ execute :find_child_elements, {id: parent}, {using: how, value: what.to_s}
543
535
  else
544
- execute :find_elements, {}, {using: how, value: what}
536
+ execute :find_elements, {}, {using: how, value: what.to_s}
545
537
  end
546
538
 
547
539
  ids.map { |id| Element.new self, element_id_from(id) }
@@ -607,20 +599,30 @@ module Selenium
607
599
  id['ELEMENT'] || id['element-6066-11e4-a52e-4f735466cecf']
608
600
  end
609
601
 
610
- def convert_locators(how, what)
602
+ def convert_locator(how, what)
603
+ how = SearchContext::FINDERS[how.to_sym] || how
604
+
611
605
  case how
612
606
  when 'class name'
613
607
  how = 'css selector'
614
- what = ".#{escape_css(what)}"
608
+ what = ".#{escape_css(what.to_s)}"
615
609
  when 'id'
616
610
  how = 'css selector'
617
- what = "##{escape_css(what)}"
611
+ what = "##{escape_css(what.to_s)}"
618
612
  when 'name'
619
613
  how = 'css selector'
620
- what = "*[name='#{escape_css(what)}']"
614
+ what = "*[name='#{escape_css(what.to_s)}']"
621
615
  when 'tag name'
622
616
  how = 'css selector'
623
617
  end
618
+
619
+ if what.is_a?(Hash)
620
+ what = what.each_with_object({}) do |(h, w), hash|
621
+ h, w = convert_locator(h.to_s, w)
622
+ hash[h] = w
623
+ end
624
+ end
625
+
624
626
  [how, what]
625
627
  end
626
628
 
@@ -631,7 +633,7 @@ module Selenium
631
633
  # @see https://mathiasbynens.be/notes/css-escapes
632
634
  def escape_css(string)
633
635
  string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
634
- string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..-1]}" if !string.empty? && string[0].match?(/[[:digit:]]/)
636
+ string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..-1]}" if string[0]&.match?(/[[:digit:]]/)
635
637
 
636
638
  string
637
639
  end
@@ -105,6 +105,7 @@ module Selenium
105
105
  opts[:timeouts]['implicit'] = opts.delete(:implicit_timeout) if opts.key?(:implicit_timeout)
106
106
  opts[:timeouts]['pageLoad'] = opts.delete(:page_load_timeout) if opts.key?(:page_load_timeout)
107
107
  opts[:timeouts]['script'] = opts.delete(:script_timeout) if opts.key?(:script_timeout)
108
+ opts.delete(:timeouts) if opts[:timeouts].empty?
108
109
  new({browser_name: 'firefox'}.merge(opts))
109
110
  end
110
111
 
@@ -34,24 +34,18 @@ module Selenium
34
34
  include DriverExtensions::HasRemoteStatus
35
35
  include DriverExtensions::HasWebStorage
36
36
 
37
- def initialize(opts = {})
38
- listener = opts.delete(:listener)
39
- desired_capabilities = opts.delete(:desired_capabilities) { Capabilities.new }
40
-
37
+ def initialize(bridge: nil, listener: nil, **opts)
38
+ desired_capabilities = opts[:desired_capabilities]
41
39
  if desired_capabilities.is_a?(Symbol)
42
- unless Capabilities.respond_to?(desired_capabilities)
40
+ unless Remote::Capabilities.respond_to?(desired_capabilities)
43
41
  raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
44
42
  end
45
43
 
46
- desired_capabilities = Capabilities.__send__(desired_capabilities)
44
+ opts[:desired_capabilities] = Remote::Capabilities.__send__(desired_capabilities)
47
45
  end
48
-
49
- @bridge = Bridge.new(opts)
50
- @bridge.create_session(desired_capabilities, opts.delete(:options))
51
-
52
- super(@bridge, listener: listener)
46
+ opts[:url] ||= "http://#{Platform.localhost}:4444/wd/hub"
47
+ super
53
48
  end
54
-
55
49
  end # Driver
56
50
  end # Remote
57
51
  end # WebDriver