selenium-webdriver 3.142.7 → 4.0.0.alpha1

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +21 -43
  3. data/lib/selenium/webdriver.rb +2 -4
  4. data/lib/selenium/webdriver/chrome/bridge.rb +3 -21
  5. data/lib/selenium/webdriver/chrome/driver.rb +12 -39
  6. data/lib/selenium/webdriver/chrome/options.rb +3 -7
  7. data/lib/selenium/webdriver/chrome/profile.rb +2 -2
  8. data/lib/selenium/webdriver/chrome/service.rb +4 -9
  9. data/lib/selenium/webdriver/common.rb +7 -16
  10. data/lib/selenium/webdriver/common/action_builder.rb +97 -249
  11. data/lib/selenium/webdriver/common/driver.rb +2 -4
  12. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +1 -1
  13. data/lib/selenium/webdriver/common/element.rb +3 -6
  14. data/lib/selenium/webdriver/common/error.rb +27 -203
  15. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -5
  16. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +13 -13
  17. data/lib/selenium/webdriver/common/manager.rb +1 -1
  18. data/lib/selenium/webdriver/common/options.rb +148 -24
  19. data/lib/selenium/webdriver/common/service.rb +16 -34
  20. data/lib/selenium/webdriver/common/socket_poller.rb +2 -2
  21. data/lib/selenium/webdriver/common/w3c_options.rb +45 -0
  22. data/lib/selenium/webdriver/edge.rb +0 -1
  23. data/lib/selenium/webdriver/edge/driver.rb +14 -10
  24. data/lib/selenium/webdriver/edge/service.rb +6 -7
  25. data/lib/selenium/webdriver/firefox.rb +2 -6
  26. data/lib/selenium/webdriver/firefox/binary.rb +3 -80
  27. data/lib/selenium/webdriver/firefox/bridge.rb +47 -0
  28. data/lib/selenium/webdriver/firefox/driver.rb +44 -22
  29. data/lib/selenium/webdriver/firefox/marionette/driver.rb +1 -1
  30. data/lib/selenium/webdriver/firefox/options.rb +2 -2
  31. data/lib/selenium/webdriver/firefox/profile.rb +25 -14
  32. data/lib/selenium/webdriver/firefox/service.rb +4 -4
  33. data/lib/selenium/webdriver/ie/driver.rb +5 -18
  34. data/lib/selenium/webdriver/ie/options.rb +2 -2
  35. data/lib/selenium/webdriver/ie/service.rb +4 -4
  36. data/lib/selenium/webdriver/remote.rb +2 -6
  37. data/lib/selenium/webdriver/remote/bridge.rb +515 -69
  38. data/lib/selenium/webdriver/remote/capabilities.rb +77 -99
  39. data/lib/selenium/webdriver/remote/commands.rb +156 -0
  40. data/lib/selenium/webdriver/remote/driver.rb +12 -5
  41. data/lib/selenium/webdriver/remote/http/default.rb +0 -9
  42. data/lib/selenium/webdriver/remote/response.rb +16 -47
  43. data/lib/selenium/webdriver/safari.rb +1 -1
  44. data/lib/selenium/webdriver/safari/bridge.rb +3 -3
  45. data/lib/selenium/webdriver/safari/driver.rb +4 -1
  46. data/lib/selenium/webdriver/safari/service.rb +4 -4
  47. data/lib/selenium/webdriver/support/select.rb +1 -1
  48. data/lib/selenium/webdriver/version.rb +1 -1
  49. data/selenium-webdriver.gemspec +3 -3
  50. metadata +14 -5
@@ -62,7 +62,7 @@ module Selenium
62
62
  #
63
63
 
64
64
  def cookie_named(name)
65
- all_cookies.find { |c| c[:name] == name }
65
+ convert_cookie(@bridge.cookie(name))
66
66
  end
67
67
 
68
68
  #
@@ -19,35 +19,159 @@
19
19
 
20
20
  module Selenium
21
21
  module WebDriver
22
- module Common
23
- class Options
24
- private
25
-
26
- def generate_as_json(value)
27
- if value.respond_to?(:as_json)
28
- value.as_json
29
- elsif value.is_a?(Hash)
30
- value.each_with_object({}) { |(key, val), hash| hash[convert_json_key(key)] = generate_as_json(val) }
31
- elsif value.is_a?(Array)
32
- value.map(&method(:generate_as_json))
33
- elsif value.is_a?(Symbol)
34
- value.to_s
35
- else
36
- value
22
+ class Options
23
+ #
24
+ # @api private
25
+ #
26
+
27
+ def initialize(bridge)
28
+ @bridge = bridge
29
+ end
30
+
31
+ #
32
+ # Add a cookie to the browser
33
+ #
34
+ # @param [Hash] opts the options to create a cookie with.
35
+ # @option opts [String] :name A name
36
+ # @option opts [String] :value A value
37
+ # @option opts [String] :path ('/') A path
38
+ # @option opts [String] :secure (false) A boolean
39
+ # @option opts [Time,DateTime,Numeric,nil] :expires (nil) Expiry date, either as a Time, DateTime, or seconds since epoch.
40
+ #
41
+ # @raise [ArgumentError] if :name or :value is not specified
42
+ #
43
+
44
+ def add_cookie(opts = {})
45
+ raise ArgumentError, 'name is required' unless opts[:name]
46
+ raise ArgumentError, 'value is required' unless opts[:value]
47
+
48
+ opts[:path] ||= '/'
49
+ opts[:secure] ||= false
50
+
51
+ obj = opts.delete(:expires)
52
+ opts[:expiry] = seconds_from(obj).to_i if obj
53
+
54
+ @bridge.add_cookie opts
55
+ end
56
+
57
+ #
58
+ # Get the cookie with the given name
59
+ #
60
+ # @param [String] name the name of the cookie
61
+ # @return [Hash, nil] the cookie, or nil if it wasn't found.
62
+ #
63
+
64
+ def cookie_named(name)
65
+ all_cookies.find { |c| c[:name] == name }
66
+ end
67
+
68
+ #
69
+ # Delete the cookie with the given name
70
+ #
71
+ # @param [String] name the name of the cookie to delete
72
+ #
73
+
74
+ def delete_cookie(name)
75
+ @bridge.delete_cookie name
76
+ end
77
+
78
+ #
79
+ # Delete all cookies
80
+ #
81
+
82
+ def delete_all_cookies
83
+ @bridge.delete_all_cookies
84
+ end
85
+
86
+ #
87
+ # Get all cookies
88
+ #
89
+ # @return [Array<Hash>] list of cookies
90
+ #
91
+
92
+ def all_cookies
93
+ @bridge.cookies.map { |cookie| convert_cookie(cookie) }
94
+ end
95
+
96
+ def timeouts
97
+ @timeouts ||= Timeouts.new(@bridge)
98
+ end
99
+
100
+ #
101
+ # @api beta This API may be changed or removed in a future release.
102
+ #
103
+
104
+ def logs
105
+ @logs ||= Logs.new(@bridge)
106
+ end
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}"
37
124
  end
125
+ result['handle']
126
+ else
127
+ raise ArgumentError, "invalid argument for type. Got: '#{type.inspect}'. " \
128
+ "Try :tab or :window"
38
129
  end
130
+ end
39
131
 
40
- def convert_json_key(key)
41
- key = camel_case(key) if key.is_a?(Symbol)
42
- return key if key.is_a?(String)
132
+ #
133
+ # @api beta This API may be changed or removed in a future release.
134
+ #
43
135
 
44
- raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"
45
- end
136
+ def window
137
+ @window ||= Window.new(@bridge)
138
+ end
139
+
140
+ private
141
+
142
+ SECONDS_PER_DAY = 86_400.0
46
143
 
47
- def camel_case(str)
48
- str.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
144
+ def datetime_at(int)
145
+ DateTime.civil(1970) + (int / SECONDS_PER_DAY)
146
+ end
147
+
148
+ def seconds_from(obj)
149
+ case obj
150
+ when Time
151
+ obj.to_f
152
+ when DateTime
153
+ (obj - DateTime.civil(1970)) * SECONDS_PER_DAY
154
+ when Numeric
155
+ obj
156
+ else
157
+ raise ArgumentError, "invalid value for expiration date: #{obj.inspect}"
49
158
  end
50
- end # Options
51
- end # Common
159
+ end
160
+
161
+ def strip_port(str)
162
+ str.split(':', 2).first
163
+ end
164
+
165
+ def convert_cookie(cookie)
166
+ {
167
+ name: cookie['name'],
168
+ value: cookie['value'],
169
+ path: cookie['path'],
170
+ domain: cookie['domain'] && strip_port(cookie['domain']),
171
+ expires: cookie['expiry'] && datetime_at(cookie['expiry']),
172
+ secure: cookie['secure']
173
+ }
174
+ end
175
+ end # Options
52
176
  end # WebDriver
53
177
  end # Selenium
@@ -29,46 +29,28 @@ module Selenium
29
29
  SOCKET_LOCK_TIMEOUT = 45
30
30
  STOP_TIMEOUT = 20
31
31
 
32
- @default_port = nil
33
- @driver_path = nil
34
- @executable = nil
35
- @missing_text = nil
36
-
37
32
  class << self
38
- attr_reader :default_port, :driver_path, :executable, :missing_text, :shutdown_supported
33
+ attr_reader :driver_path
39
34
 
40
- def chrome(**opts)
41
- Chrome::Service.new(**opts)
35
+ def chrome(*args)
36
+ Chrome::Service.new(*args)
42
37
  end
43
38
 
44
- def firefox(**opts)
45
- binary_path = Firefox::Binary.path
46
- args = opts.delete(:args)
47
- case args
48
- when Hash
49
- args[:binary] ||= binary_path
50
- opts[:args] = args
51
- when Array
52
- opts[:args] = ["--binary=#{binary_path}"]
53
- opts[:args] += args
54
- else
55
- opts[:args] = ["--binary=#{binary_path}"]
56
- end
57
-
58
- Firefox::Service.new(**opts)
39
+ def firefox(*args)
40
+ Firefox::Service.new(*args)
59
41
  end
60
42
 
61
- def ie(**opts)
62
- IE::Service.new(**opts)
43
+ def ie(*args)
44
+ IE::Service.new(*args)
63
45
  end
64
46
  alias_method :internet_explorer, :ie
65
47
 
66
- def edge(**opts)
67
- Edge::Service.new(**opts)
48
+ def edge(*args)
49
+ Edge::Service.new(*args)
68
50
  end
69
51
 
70
- def safari(**opts)
71
- Safari::Service.new(**opts)
52
+ def safari(*args)
53
+ Safari::Service.new(*args)
72
54
  end
73
55
 
74
56
  def driver_path=(path)
@@ -88,7 +70,7 @@ module Selenium
88
70
 
89
71
  def initialize(path: nil, port: nil, args: nil)
90
72
  path ||= self.class.driver_path
91
- port ||= self.class.default_port
73
+ port ||= self.class::DEFAULT_PORT
92
74
  args ||= []
93
75
 
94
76
  @executable_path = binary_path(path)
@@ -113,7 +95,7 @@ module Selenium
113
95
  end
114
96
 
115
97
  def stop
116
- return unless self.class.shutdown_supported
98
+ return unless self.class::SHUTDOWN_SUPPORTED
117
99
 
118
100
  stop_server
119
101
  @process.poll_for_exit STOP_TIMEOUT
@@ -131,9 +113,9 @@ module Selenium
131
113
 
132
114
  def binary_path(path = nil)
133
115
  path = path.call if path.is_a?(Proc)
134
- path ||= Platform.find_binary(self.class.executable)
116
+ path ||= Platform.find_binary(self.class::EXECUTABLE)
135
117
 
136
- raise Error::WebDriverError, self.class.missing_text unless path
118
+ raise Error::WebDriverError, self.class::MISSING_TEXT unless path
137
119
 
138
120
  Platform.assert_executable path
139
121
  path
@@ -201,7 +183,7 @@ module Selenium
201
183
  end
202
184
 
203
185
  def cannot_connect_error_text
204
- "unable to connect to #{self.class.executable} #{@host}:#{@port}"
186
+ "unable to connect to #{self.class::EXECUTABLE} #{@host}:#{@port}"
205
187
  end
206
188
 
207
189
  def socket_lock
@@ -66,8 +66,8 @@ module Selenium
66
66
  }.freeze
67
67
 
68
68
  if Platform.jruby?
69
- # we use a plain TCPSocket here since JRuby has issues select()ing on a connecting socket
70
- # see http://jira.codehaus.org/browse/JRUBY-5165
69
+ # we use a plain TCPSocket here since JRuby has issues closing socket
70
+ # see https://github.com/jruby/jruby/issues/5709
71
71
  def listening?
72
72
  TCPSocket.new(@host, @port).close
73
73
  true
@@ -0,0 +1,45 @@
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
+ class W3COptions < Options
23
+
24
+ #
25
+ # Get the cookie with the given name
26
+ #
27
+ # @param [String] name the name of the cookie
28
+ # @return [Hash, nil] the cookie, or nil if it wasn't found.
29
+ #
30
+
31
+ def cookie_named(name)
32
+ convert_cookie(@bridge.cookie(name))
33
+ end
34
+
35
+ #
36
+ # Delete all cookies
37
+ #
38
+
39
+ def delete_all_cookies
40
+ @bridge.delete_all_cookies
41
+ end
42
+
43
+ end # WC3Options
44
+ end # WebDriver
45
+ end # Selenium
@@ -19,7 +19,6 @@
19
19
 
20
20
  require 'net/http'
21
21
 
22
- require 'selenium/webdriver/edge/bridge'
23
22
  require 'selenium/webdriver/edge/driver'
24
23
  require 'selenium/webdriver/edge/options'
25
24
 
@@ -27,25 +27,19 @@ module Selenium
27
27
  #
28
28
 
29
29
  class Driver < WebDriver::Driver
30
+ include DriverExtensions::HasWebStorage
30
31
  include DriverExtensions::TakesScreenshot
31
32
 
32
33
  def initialize(opts = {})
33
- opts[:desired_capabilities] ||= Remote::Capabilities.edge
34
+ opts[:desired_capabilities] = create_capabilities(opts)
34
35
 
35
36
  opts[:url] ||= service_url(opts)
36
37
 
37
38
  listener = opts.delete(:listener)
38
-
39
- # Edge is mostly using W3C dialect, but a request to
40
- # create session responds with OSS-like body,
41
- # so we need to force W3C implementation.
42
39
  desired_capabilities = opts.delete(:desired_capabilities)
43
- bridge = Remote::Bridge.new(opts)
44
- capabilities = bridge.create_session(desired_capabilities)
45
40
 
46
- WebDriver.logger.info 'Forcing W3C dialect.'
47
- @bridge = Remote::W3C::Bridge.new(capabilities, bridge.session_id, **opts)
48
- @bridge.extend Edge::Bridge
41
+ @bridge = Remote::Bridge.new(opts)
42
+ @bridge.create_session(desired_capabilities)
49
43
 
50
44
  super(@bridge, listener: listener)
51
45
  end
@@ -60,6 +54,16 @@ module Selenium
60
54
  @service&.stop
61
55
  end
62
56
 
57
+ private
58
+
59
+ def create_capabilities(opts)
60
+ caps = opts.delete(:desired_capabilities) { Remote::Capabilities.edge }
61
+ options = opts.delete(:options) { Options.new }
62
+ options = options.as_json
63
+ caps.merge!(options) unless options.empty?
64
+
65
+ caps
66
+ end
63
67
  end # Driver
64
68
  end # Edge
65
69
  end # WebDriver
@@ -25,14 +25,13 @@ module Selenium
25
25
  #
26
26
 
27
27
  class Service < WebDriver::Service
28
- @default_port = 17556
29
- @executable = 'MicrosoftWebDriver'
30
- @missing_text = <<~ERROR
31
- Unable to find MicrosoftWebDriver. Please download the server from
32
- https://www.microsoft.com/en-us/download/details.aspx?id=48212 and place it somewhere on your PATH.
33
- More info at https://github.com/SeleniumHQ/selenium/wiki/MicrosoftWebDriver.
28
+ DEFAULT_PORT = 17556
29
+ EXECUTABLE = 'MicrosoftWebDriver'
30
+ MISSING_TEXT = <<~ERROR
31
+ Unable to find MicrosoftWebDriver. Please install it following instructions
32
+ at https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/.
34
33
  ERROR
35
- @shutdown_supported = true
34
+ SHUTDOWN_SUPPORTED = false
36
35
 
37
36
  private
38
37
 
@@ -23,16 +23,12 @@ require 'rexml/document'
23
23
 
24
24
  require 'selenium/webdriver/firefox/driver'
25
25
 
26
- require 'selenium/webdriver/firefox/util'
27
26
  require 'selenium/webdriver/firefox/extension'
28
27
  require 'selenium/webdriver/firefox/binary'
29
28
  require 'selenium/webdriver/firefox/profiles_ini'
30
29
  require 'selenium/webdriver/firefox/profile'
31
- require 'selenium/webdriver/firefox/launcher'
32
- require 'selenium/webdriver/firefox/legacy/driver'
33
-
34
- require 'selenium/webdriver/firefox/marionette/bridge'
35
- require 'selenium/webdriver/firefox/marionette/driver'
30
+ require 'selenium/webdriver/firefox/bridge'
31
+ require 'selenium/webdriver/firefox/driver'
36
32
  require 'selenium/webdriver/firefox/options'
37
33
 
38
34
  module Selenium
@@ -22,83 +22,6 @@ module Selenium
22
22
  module Firefox
23
23
  # @api private
24
24
  class Binary
25
- NO_FOCUS_LIBRARY_NAME = 'x_ignore_nofocus.so'
26
- NO_FOCUS_LIBRARIES = [
27
- ["#{WebDriver.root}/selenium/webdriver/firefox/native/linux/amd64/#{NO_FOCUS_LIBRARY_NAME}",
28
- "amd64/#{NO_FOCUS_LIBRARY_NAME}"],
29
- ["#{WebDriver.root}/selenium/webdriver/firefox/native/linux/x86/#{NO_FOCUS_LIBRARY_NAME}",
30
- "x86/#{NO_FOCUS_LIBRARY_NAME}"]
31
- ].freeze
32
-
33
- WAIT_TIMEOUT = 90
34
- QUIT_TIMEOUT = 5
35
-
36
- def start_with(profile, profile_path, *args)
37
- if Platform.cygwin?
38
- profile_path = Platform.cygwin_path(profile_path, windows: true)
39
- elsif Platform.windows?
40
- profile_path = Platform.windows_path(profile_path)
41
- end
42
-
43
- ENV['XRE_CONSOLE_LOG'] = profile.log_file if profile.log_file
44
- ENV['XRE_PROFILE_PATH'] = profile_path
45
- ENV['MOZ_NO_REMOTE'] = '1' # able to launch multiple instances
46
- ENV['MOZ_CRASHREPORTER_DISABLE'] = '1' # disable breakpad
47
- ENV['NO_EM_RESTART'] = '1' # prevent the binary from detaching from the console
48
-
49
- modify_link_library_path profile_path if Platform.linux? && (profile.native_events? || profile.load_no_focus_lib?)
50
-
51
- execute(*args)
52
- end
53
-
54
- def quit
55
- return unless @process
56
-
57
- @process.poll_for_exit QUIT_TIMEOUT
58
- rescue ChildProcess::TimeoutError
59
- # ok, force quit
60
- @process.stop QUIT_TIMEOUT
61
- end
62
-
63
- def wait
64
- return unless @process
65
-
66
- begin
67
- @process.poll_for_exit(WAIT_TIMEOUT)
68
- rescue ChildProcess::TimeoutError => e
69
- @process.stop
70
- raise e
71
- end
72
- end
73
-
74
- private
75
-
76
- def execute(*extra_args)
77
- args = [self.class.path, '-no-remote'] + extra_args
78
- @process = ChildProcess.build(*args)
79
- WebDriver.logger.debug("Executing Process #{args}")
80
-
81
- @process.io.stdout = @process.io.stderr = WebDriver.logger.io if WebDriver.logger.debug?
82
- @process.start
83
- end
84
-
85
- def modify_link_library_path(profile_path)
86
- paths = []
87
-
88
- NO_FOCUS_LIBRARIES.each do |from, to|
89
- dest = File.join(profile_path, to)
90
- FileUtils.mkdir_p File.dirname(dest)
91
- FileUtils.cp from, dest
92
-
93
- paths << File.expand_path(File.dirname(dest))
94
- end
95
-
96
- paths += ENV['LD_LIBRARY_PATH'].to_s.split(File::PATH_SEPARATOR)
97
-
98
- ENV['LD_LIBRARY_PATH'] = paths.uniq.join(File::PATH_SEPARATOR)
99
- ENV['LD_PRELOAD'] = NO_FOCUS_LIBRARY_NAME
100
- end
101
-
102
25
  class << self
103
26
  #
104
27
  # @api private
@@ -130,8 +53,8 @@ module Selenium
130
53
  @path = Platform.cygwin_path(@path, windows: true) if Platform.cygwin?
131
54
 
132
55
  unless File.file?(@path.to_s)
133
- error = "Could not find Firefox binary (os=#{Platform.os}). " \
134
- "Make sure Firefox is installed or set the path manually with #{self}.path="
56
+ error = "Could not find Firefox binary (os=#{Platform.os}). "
57
+ error << "Make sure Firefox is installed or set the path manually with #{self}.path="
135
58
  raise Error::WebDriverError, error
136
59
  end
137
60
 
@@ -173,7 +96,7 @@ module Selenium
173
96
  lm = Win32::Registry::HKEY_LOCAL_MACHINE
174
97
  lm.open('SOFTWARE\\Mozilla\\Mozilla Firefox') do |reg|
175
98
  main = lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{reg.keys[0]}\\Main")
176
- entry = main.find { |key, _type, _data| key =~ /pathtoexe/i }
99
+ entry = main.find { |key, _type, _data| /pathtoexe/i.match? key }
177
100
  return entry.last if entry
178
101
  end
179
102
  rescue LoadError