selenium-webdriver 3.142.7 → 4.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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