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
@@ -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