selenium-webdriver 2.53.3 → 3.142.7

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 (162) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +665 -8
  3. data/Gemfile +2 -0
  4. data/LICENSE +1 -1
  5. data/README.md +2 -3
  6. data/lib/selenium/server.rb +76 -73
  7. data/lib/selenium/webdriver/atoms/getAttribute.js +7 -0
  8. data/lib/selenium/webdriver/atoms/isDisplayed.js +102 -0
  9. data/lib/selenium/webdriver/{phantomjs.rb → atoms.rb} +10 -14
  10. data/lib/selenium/webdriver/chrome/bridge.rb +30 -101
  11. data/lib/selenium/webdriver/chrome/driver.rb +127 -0
  12. data/lib/selenium/webdriver/chrome/options.rb +190 -0
  13. data/lib/selenium/webdriver/chrome/profile.rb +21 -20
  14. data/lib/selenium/webdriver/chrome/service.rb +26 -93
  15. data/lib/selenium/webdriver/chrome.rb +15 -6
  16. data/lib/selenium/webdriver/common/action_builder.rb +52 -58
  17. data/lib/selenium/webdriver/common/alert.rb +7 -15
  18. data/lib/selenium/webdriver/common/bridge_helper.rb +18 -22
  19. data/lib/selenium/webdriver/common/driver.rb +72 -72
  20. data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +45 -0
  21. data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +50 -0
  22. data/lib/selenium/webdriver/common/driver_extensions/{has_input_devices.rb → has_debugger.rb} +11 -27
  23. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +6 -10
  24. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +51 -0
  25. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +7 -8
  26. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +51 -0
  27. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +2 -4
  28. data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +2 -4
  29. data/lib/selenium/webdriver/common/driver_extensions/has_touch_screen.rb +3 -5
  30. data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +2 -5
  31. data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +6 -9
  32. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +9 -7
  33. data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +3 -8
  34. data/lib/selenium/webdriver/common/element.rb +59 -39
  35. data/lib/selenium/webdriver/common/error.rb +259 -104
  36. data/lib/selenium/webdriver/common/file_reaper.rb +6 -14
  37. data/lib/selenium/webdriver/common/html5/local_storage.rb +8 -10
  38. data/lib/selenium/webdriver/common/html5/session_storage.rb +8 -10
  39. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +8 -16
  40. data/lib/selenium/webdriver/common/interactions/input_device.rb +54 -0
  41. data/lib/selenium/webdriver/common/interactions/interaction.rb +53 -0
  42. data/lib/selenium/webdriver/{safari/browser.rb → common/interactions/interactions.rb} +17 -14
  43. data/lib/selenium/webdriver/common/interactions/key_actions.rb +145 -0
  44. data/lib/selenium/webdriver/common/interactions/key_input.rb +66 -0
  45. data/lib/selenium/webdriver/{android.rb → common/interactions/none_input.rb} +14 -6
  46. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +363 -0
  47. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +139 -0
  48. data/lib/selenium/webdriver/common/keyboard.rb +10 -14
  49. data/lib/selenium/webdriver/common/keys.rb +102 -82
  50. data/lib/selenium/webdriver/common/log_entry.rb +7 -8
  51. data/lib/selenium/webdriver/common/logger.rb +115 -0
  52. data/lib/selenium/webdriver/common/logs.rb +4 -6
  53. data/lib/selenium/webdriver/common/manager.rb +177 -0
  54. data/lib/selenium/webdriver/common/mouse.rb +12 -14
  55. data/lib/selenium/webdriver/common/navigation.rb +4 -6
  56. data/lib/selenium/webdriver/common/options.rb +26 -127
  57. data/lib/selenium/webdriver/common/platform.rb +75 -101
  58. data/lib/selenium/webdriver/common/port_prober.rb +7 -19
  59. data/lib/selenium/webdriver/common/profile_helper.rb +8 -11
  60. data/lib/selenium/webdriver/common/proxy.rb +68 -74
  61. data/lib/selenium/webdriver/common/search_context.rb +28 -37
  62. data/lib/selenium/webdriver/common/service.rb +219 -0
  63. data/lib/selenium/webdriver/common/socket_lock.rb +15 -16
  64. data/lib/selenium/webdriver/common/socket_poller.rb +30 -28
  65. data/lib/selenium/webdriver/common/target_locator.rb +16 -18
  66. data/lib/selenium/webdriver/common/timeouts.rb +6 -8
  67. data/lib/selenium/webdriver/common/touch_action_builder.rb +5 -10
  68. data/lib/selenium/webdriver/common/touch_screen.rb +22 -23
  69. data/lib/selenium/webdriver/common/w3c_action_builder.rb +212 -0
  70. data/lib/selenium/webdriver/common/w3c_manager.rb +45 -0
  71. data/lib/selenium/webdriver/common/wait.rb +17 -16
  72. data/lib/selenium/webdriver/common/window.rb +50 -17
  73. data/lib/selenium/webdriver/common/zipper.rb +9 -13
  74. data/lib/selenium/webdriver/common.rb +21 -7
  75. data/lib/selenium/webdriver/edge/bridge.rb +34 -63
  76. data/lib/selenium/webdriver/edge/driver.rb +66 -0
  77. data/lib/selenium/webdriver/edge/options.rb +80 -0
  78. data/lib/selenium/webdriver/edge/service.rb +23 -95
  79. data/lib/selenium/webdriver/edge.rb +13 -13
  80. data/lib/selenium/webdriver/firefox/binary.rb +45 -60
  81. data/lib/selenium/webdriver/firefox/driver.rb +50 -0
  82. data/lib/selenium/webdriver/firefox/extension/prefs.json +3 -12
  83. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  84. data/lib/selenium/webdriver/firefox/extension.rb +20 -10
  85. data/lib/selenium/webdriver/firefox/launcher.rb +16 -22
  86. data/lib/selenium/webdriver/firefox/legacy/driver.rb +83 -0
  87. data/lib/selenium/webdriver/firefox/marionette/bridge.rb +49 -0
  88. data/lib/selenium/webdriver/firefox/marionette/driver.rb +90 -0
  89. data/lib/selenium/webdriver/firefox/options.rb +162 -0
  90. data/lib/selenium/webdriver/firefox/profile.rb +47 -48
  91. data/lib/selenium/webdriver/firefox/profiles_ini.rb +11 -18
  92. data/lib/selenium/webdriver/firefox/service.rb +24 -95
  93. data/lib/selenium/webdriver/firefox/util.rb +2 -4
  94. data/lib/selenium/webdriver/firefox.rb +27 -12
  95. data/lib/selenium/webdriver/ie/driver.rb +85 -0
  96. data/lib/selenium/webdriver/ie/options.rb +138 -0
  97. data/lib/selenium/webdriver/ie/service.rb +54 -0
  98. data/lib/selenium/webdriver/ie.rb +12 -10
  99. data/lib/selenium/webdriver/remote/bridge.rb +100 -564
  100. data/lib/selenium/webdriver/remote/capabilities.rb +98 -100
  101. data/lib/selenium/webdriver/remote/driver.rb +51 -0
  102. data/lib/selenium/webdriver/remote/http/common.rb +34 -22
  103. data/lib/selenium/webdriver/remote/http/curb.rb +13 -14
  104. data/lib/selenium/webdriver/remote/http/default.rb +62 -43
  105. data/lib/selenium/webdriver/remote/http/persistent.rb +12 -9
  106. data/lib/selenium/webdriver/remote/oss/bridge.rb +594 -0
  107. data/lib/selenium/webdriver/remote/oss/commands.rb +223 -0
  108. data/lib/selenium/webdriver/remote/response.rb +48 -28
  109. data/lib/selenium/webdriver/remote/server_error.rb +3 -5
  110. data/lib/selenium/webdriver/remote/w3c/bridge.rb +605 -0
  111. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +310 -0
  112. data/lib/selenium/webdriver/remote/w3c/commands.rb +157 -0
  113. data/lib/selenium/webdriver/remote.rb +10 -16
  114. data/lib/selenium/webdriver/safari/bridge.rb +17 -101
  115. data/lib/selenium/webdriver/{firefox/w3c_bridge.rb → safari/driver.rb} +27 -25
  116. data/lib/selenium/webdriver/safari/options.rb +29 -31
  117. data/lib/selenium/webdriver/safari/service.rb +38 -0
  118. data/lib/selenium/webdriver/safari.rb +27 -27
  119. data/lib/selenium/webdriver/support/abstract_event_listener.rb +19 -4
  120. data/lib/selenium/webdriver/support/block_event_listener.rb +3 -5
  121. data/lib/selenium/webdriver/support/color.rb +60 -43
  122. data/lib/selenium/webdriver/support/escaper.rb +43 -0
  123. data/lib/selenium/webdriver/support/event_firing_bridge.rb +39 -41
  124. data/lib/selenium/webdriver/support/select.rb +45 -97
  125. data/lib/selenium/webdriver/support.rb +3 -2
  126. data/lib/selenium/webdriver/{iphone.rb → version.rb} +3 -7
  127. data/lib/selenium/webdriver.rb +36 -23
  128. data/lib/selenium-webdriver.rb +2 -2
  129. data/selenium-webdriver.gemspec +42 -29
  130. metadata +331 -262
  131. data/lib/selenium/client/base.rb +0 -151
  132. data/lib/selenium/client/driver.rb +0 -29
  133. data/lib/selenium/client/errors.rb +0 -28
  134. data/lib/selenium/client/extensions.rb +0 -132
  135. data/lib/selenium/client/idiomatic.rb +0 -507
  136. data/lib/selenium/client/javascript_expression_builder.rb +0 -135
  137. data/lib/selenium/client/javascript_frameworks/jquery.rb +0 -32
  138. data/lib/selenium/client/javascript_frameworks/prototype.rb +0 -32
  139. data/lib/selenium/client/legacy_driver.rb +0 -1722
  140. data/lib/selenium/client/protocol.rb +0 -123
  141. data/lib/selenium/client/selenium_helper.rb +0 -49
  142. data/lib/selenium/client.rb +0 -57
  143. data/lib/selenium/rake/server_task.rb +0 -176
  144. data/lib/selenium/webdriver/android/bridge.rb +0 -68
  145. data/lib/selenium/webdriver/common/core_ext/base64.rb +0 -28
  146. data/lib/selenium/webdriver/common/core_ext/dir.rb +0 -61
  147. data/lib/selenium/webdriver/common/html5/location.rb +0 -19
  148. data/lib/selenium/webdriver/common/w3c_error.rb +0 -194
  149. data/lib/selenium/webdriver/edge/legacy_support.rb +0 -117
  150. data/lib/selenium/webdriver/firefox/bridge.rb +0 -89
  151. data/lib/selenium/webdriver/ie/bridge.rb +0 -88
  152. data/lib/selenium/webdriver/ie/server.rb +0 -133
  153. data/lib/selenium/webdriver/iphone/bridge.rb +0 -64
  154. data/lib/selenium/webdriver/phantomjs/bridge.rb +0 -78
  155. data/lib/selenium/webdriver/phantomjs/service.rb +0 -130
  156. data/lib/selenium/webdriver/remote/commands.rb +0 -211
  157. data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -668
  158. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -236
  159. data/lib/selenium/webdriver/remote/w3c_commands.rb +0 -132
  160. data/lib/selenium/webdriver/safari/resources/client.js +0 -7255
  161. data/lib/selenium/webdriver/safari/server.rb +0 -187
  162. data/lib/selenium-client.rb +0 -21
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -21,45 +21,58 @@ module Selenium
21
21
  module WebDriver
22
22
  class Proxy
23
23
  TYPES = {
24
- :direct => "DIRECT", # Direct connection, no proxy (default on Windows).
25
- :manual => "MANUAL", # Manual proxy settings (e.g., for httpProxy).
26
- :pac => "PAC", # Proxy autoconfiguration from URL.
27
- :auto_detect => "AUTODETECT", # Proxy autodetection (presumably with WPAD).
28
- :system => "SYSTEM" # Use system settings (default on Linux).
29
- }
30
-
31
- attr_reader :type,
32
- :ftp,
33
- :http,
34
- :socks,
35
- :socks_username,
36
- :socks_password,
37
- :no_proxy,
38
- :pac,
39
- :ssl,
40
- :auto_detect
24
+ direct: 'DIRECT', # Direct connection, no proxy (default on Windows).
25
+ manual: 'MANUAL', # Manual proxy settings (e.g., for httpProxy).
26
+ pac: 'PAC', # Proxy autoconfiguration from URL.
27
+ auto_detect: 'AUTODETECT', # Proxy autodetection (presumably with WPAD).
28
+ system: 'SYSTEM' # Use system settings (default on Linux).
29
+ }.freeze
30
+
31
+ ALLOWED = {type: 'proxyType',
32
+ ftp: 'ftpProxy',
33
+ http: 'httpProxy',
34
+ no_proxy: 'noProxy',
35
+ pac: 'proxyAutoconfigUrl',
36
+ ssl: 'sslProxy',
37
+ auto_detect: 'autodetect',
38
+ socks: 'socksProxy',
39
+ socks_username: 'socksUsername',
40
+ socks_password: 'socksPassword',
41
+ socks_version: 'socksVersion'}.freeze
42
+
43
+ ALLOWED.each_key { |t| attr_reader t }
44
+
45
+ def self.json_create(data)
46
+ data['proxyType'] = data['proxyType'].downcase.to_sym
47
+ return if data['proxyType'] == :unspecified
48
+
49
+ proxy = new
50
+
51
+ ALLOWED.each do |k, v|
52
+ proxy.send("#{k}=", data[v]) if data.key?(v)
53
+ end
54
+
55
+ proxy
56
+ end
41
57
 
42
58
  def initialize(opts = {})
43
- opts = opts.dup
44
-
45
- self.type = opts.delete(:type) if opts.has_key? :type
46
- self.ftp = opts.delete(:ftp) if opts.has_key? :ftp
47
- self.http = opts.delete(:http) if opts.has_key? :http
48
- self.no_proxy = opts.delete(:no_proxy) if opts.has_key? :no_proxy
49
- self.ssl = opts.delete(:ssl) if opts.has_key? :ssl
50
- self.pac = opts.delete(:pac) if opts.has_key? :pac
51
- self.auto_detect = opts.delete(:auto_detect) if opts.has_key? :auto_detect
52
- self.socks = opts.delete(:socks) if opts.has_key? :socks
53
- self.socks_username = opts.delete(:socks_username) if opts.has_key? :socks_username
54
- self.socks_password = opts.delete(:socks_password) if opts.has_key? :socks_password
55
-
56
- unless opts.empty?
57
- raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
59
+ not_allowed = []
60
+
61
+ opts.each do |k, v|
62
+ if ALLOWED.key?(k)
63
+ send("#{k}=", v)
64
+ else
65
+ not_allowed << k
66
+ end
58
67
  end
68
+
69
+ return if not_allowed.empty?
70
+
71
+ raise ArgumentError, "unknown option#{'s' if not_allowed.size != 1}: #{not_allowed.inspect}"
59
72
  end
60
73
 
61
74
  def ==(other)
62
- other.kind_of?(self.class) && as_json == other.as_json
75
+ other.is_a?(self.class) && as_json == other.as_json
63
76
  end
64
77
  alias_method :eql?, :==
65
78
 
@@ -108,10 +121,13 @@ module Selenium
108
121
  @socks_password = value
109
122
  end
110
123
 
124
+ def socks_version=(value)
125
+ self.type = :manual
126
+ @socks_version = value
127
+ end
128
+
111
129
  def type=(type)
112
- unless TYPES.has_key? type
113
- raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}"
114
- end
130
+ raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}" unless TYPES.key? type
115
131
 
116
132
  if defined?(@type) && type != @type
117
133
  raise ArgumentError, "incompatible proxy type #{type.inspect} (already set to #{@type.inspect})"
@@ -120,49 +136,27 @@ module Selenium
120
136
  @type = type
121
137
  end
122
138
 
123
- def as_json(opts = nil)
139
+ def as_json(*)
124
140
  json_result = {
125
- "proxyType" => TYPES[type]
126
- }
127
-
128
- json_result["ftpProxy"] = ftp if ftp
129
- json_result["httpProxy"] = http if http
130
- json_result["noProxy"] = no_proxy if no_proxy
131
- json_result["proxyAutoconfigUrl"] = pac if pac
132
- json_result["sslProxy"] = ssl if ssl
133
- json_result["autodetect"] = auto_detect if auto_detect
134
- json_result["socksProxy"] = socks if socks
135
- json_result["socksUsername"] = socks_username if socks_username
136
- json_result["socksPassword"] = socks_password if socks_password
141
+ 'proxyType' => TYPES[type],
142
+ 'ftpProxy' => ftp,
143
+ 'httpProxy' => http,
144
+ 'noProxy' => no_proxy,
145
+ 'proxyAutoconfigUrl' => pac,
146
+ 'sslProxy' => ssl,
147
+ 'autodetect' => auto_detect,
148
+ 'socksProxy' => socks,
149
+ 'socksUsername' => socks_username,
150
+ 'socksPassword' => socks_password,
151
+ 'socksVersion' => socks_version
152
+ }.delete_if { |_k, v| v.nil? }
137
153
 
138
154
  json_result if json_result.length > 1
139
155
  end
140
156
 
141
- def to_json(*args)
157
+ def to_json(*)
142
158
  JSON.generate as_json
143
159
  end
144
-
145
- class << self
146
- def json_create(data)
147
- return if data['proxyType'] == 'UNSPECIFIED'
148
-
149
- proxy = new
150
-
151
- proxy.type = data['proxyType'].downcase.to_sym if data.has_key? 'proxyType'
152
- proxy.ftp = data['ftpProxy'] if data.has_key? 'ftpProxy'
153
- proxy.http = data['httpProxy'] if data.has_key? 'httpProxy'
154
- proxy.no_proxy = data['noProxy'] if data.has_key? 'noProxy'
155
- proxy.pac = data['proxyAutoconfigUrl'] if data.has_key? 'proxyAutoconfigUrl'
156
- proxy.ssl = data['sslProxy'] if data.has_key? 'sslProxy'
157
- proxy.auto_detect = data['autodetect'] if data.has_key? 'autodetect'
158
- proxy.socks = data['socksProxy'] if data.has_key? 'socksProxy'
159
- proxy.socks_username = data['socksUsername'] if data.has_key? 'socksUsername'
160
- proxy.socks_password = data['socksPassword'] if data.has_key? 'socksPassword'
161
-
162
- proxy
163
- end
164
- end # class << self
165
-
166
160
  end # Proxy
167
161
  end # WebDriver
168
162
  end # Selenium
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -20,46 +20,47 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module SearchContext
23
-
24
23
  # @api private
25
24
  FINDERS = {
26
- :class => 'class name',
27
- :class_name => 'class name',
28
- :css => 'css selector',
29
- :id => 'id',
30
- :link => 'link text',
31
- :link_text => 'link text',
32
- :name => 'name',
33
- :partial_link_text => 'partial link text',
34
- :tag_name => 'tag name',
35
- :xpath => 'xpath',
36
- }
25
+ class: 'class name',
26
+ class_name: 'class name',
27
+ css: 'css selector',
28
+ id: 'id',
29
+ link: 'link text',
30
+ link_text: 'link text',
31
+ name: 'name',
32
+ partial_link_text: 'partial link text',
33
+ tag_name: 'tag name',
34
+ xpath: 'xpath'
35
+ }.freeze
37
36
 
38
37
  #
39
- # Find the first element matching the given arguments.
38
+ # Find the first element matching the given arguments
40
39
  #
41
40
  # When using Element#find_element with :xpath, be aware that webdriver
42
41
  # follows standard conventions: a search prefixed with "//" will search
43
42
  # the entire document, not just the children of this current node. Use
44
43
  # ".//" to limit your search to the children of the receiving Element.
45
44
  #
46
- # @param [:class, :class_name, :css, :id, :link_text, :link, :partial_link_text, :name, :tag_name, :xpath] how
47
- # @param [String] what
45
+ # @overload find_element(how, what)
46
+ # @param [Symbol, String] how The method to find the element by
47
+ # @param [String] what The locator to use
48
+ # @overload find_element(opts)
49
+ # @param [Hash] opts Find options
50
+ # @option opts [Symbol] :how Key named after the method to find the element by, containing the locator
48
51
  # @return [Element]
49
52
  #
50
53
  # @raise [Error::NoSuchElementError] if the element doesn't exist
51
54
  #
52
- #
53
55
 
54
56
  def find_element(*args)
55
57
  how, what = extract_args(args)
56
58
 
57
- unless by = FINDERS[how.to_sym]
58
- raise ArgumentError, "cannot find element by #{how.inspect}"
59
- end
59
+ by = FINDERS[how.to_sym]
60
+ raise ArgumentError, "cannot find element by #{how.inspect}" unless by
60
61
 
61
62
  bridge.find_element_by by, what.to_s, ref
62
- rescue Selenium::WebDriver::Error::TimeOutError
63
+ rescue Selenium::WebDriver::Error::TimeoutError
63
64
  # Implicit Wait times out in Edge
64
65
  raise Selenium::WebDriver::Error::NoSuchElementError
65
66
  end
@@ -69,20 +70,15 @@ module Selenium
69
70
  #
70
71
  # @see SearchContext#find_element
71
72
  #
72
- # @param [:class, :class_name, :css, :id, :link_text, :link, :partial_link_text, :name, :tag_name, :xpath] how
73
- # @param [String] what
74
- # @return [Array<Element>]
75
- #
76
73
 
77
74
  def find_elements(*args)
78
75
  how, what = extract_args(args)
79
76
 
80
- unless by = FINDERS[how.to_sym]
81
- raise ArgumentError, "cannot find elements by #{how.inspect}"
82
- end
77
+ by = FINDERS[how.to_sym]
78
+ raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
83
79
 
84
80
  bridge.find_elements_by by, what.to_s, ref
85
- rescue Selenium::WebDriver::Error::TimeOutError
81
+ rescue Selenium::WebDriver::Error::TimeoutError
86
82
  # Implicit Wait times out in Edge
87
83
  []
88
84
  end
@@ -96,22 +92,17 @@ module Selenium
96
92
  when 1
97
93
  arg = args.first
98
94
 
99
- unless arg.respond_to?(:shift)
100
- raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift"
101
- end
95
+ raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift" unless arg.respond_to?(:shift)
102
96
 
103
97
  # this will be a single-entry hash, so use #shift over #first or #[]
104
98
  arr = arg.dup.shift
105
- unless arr.size == 2
106
- raise ArgumentError, "expected #{arr.inspect} to have 2 elements"
107
- end
99
+ raise ArgumentError, "expected #{arr.inspect} to have 2 elements" unless arr.size == 2
108
100
 
109
101
  arr
110
102
  else
111
103
  raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
112
104
  end
113
105
  end
114
-
115
106
  end # SearchContext
116
107
  end # WebDriver
117
108
  end # Selenium
@@ -0,0 +1,219 @@
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
+ #
23
+ # Base class implementing default behavior of service object,
24
+ # responsible for starting and stopping driver implementations.
25
+ #
26
+
27
+ class Service
28
+ START_TIMEOUT = 20
29
+ SOCKET_LOCK_TIMEOUT = 45
30
+ STOP_TIMEOUT = 20
31
+
32
+ @default_port = nil
33
+ @driver_path = nil
34
+ @executable = nil
35
+ @missing_text = nil
36
+
37
+ class << self
38
+ attr_reader :default_port, :driver_path, :executable, :missing_text, :shutdown_supported
39
+
40
+ def chrome(**opts)
41
+ Chrome::Service.new(**opts)
42
+ end
43
+
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)
59
+ end
60
+
61
+ def ie(**opts)
62
+ IE::Service.new(**opts)
63
+ end
64
+ alias_method :internet_explorer, :ie
65
+
66
+ def edge(**opts)
67
+ Edge::Service.new(**opts)
68
+ end
69
+
70
+ def safari(**opts)
71
+ Safari::Service.new(**opts)
72
+ end
73
+
74
+ def driver_path=(path)
75
+ Platform.assert_executable path if path.is_a?(String)
76
+ @driver_path = path
77
+ end
78
+ end
79
+
80
+ attr_accessor :host
81
+ attr_reader :executable_path
82
+
83
+ #
84
+ # End users should use a class method for the desired driver, rather than using this directly.
85
+ #
86
+ # @api private
87
+ #
88
+
89
+ def initialize(path: nil, port: nil, args: nil)
90
+ path ||= self.class.driver_path
91
+ port ||= self.class.default_port
92
+ args ||= []
93
+
94
+ @executable_path = binary_path(path)
95
+ @host = Platform.localhost
96
+ @port = Integer(port)
97
+
98
+ @extra_args = args.is_a?(Hash) ? extract_service_args(args) : args
99
+
100
+ raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
101
+ end
102
+
103
+ def start
104
+ raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
105
+
106
+ Platform.exit_hook(&method(:stop)) # make sure we don't leave the server running
107
+
108
+ socket_lock.locked do
109
+ find_free_port
110
+ start_process
111
+ connect_until_stable
112
+ end
113
+ end
114
+
115
+ def stop
116
+ return unless self.class.shutdown_supported
117
+
118
+ stop_server
119
+ @process.poll_for_exit STOP_TIMEOUT
120
+ rescue ChildProcess::TimeoutError
121
+ nil # noop
122
+ ensure
123
+ stop_process
124
+ end
125
+
126
+ def uri
127
+ @uri ||= URI.parse("http://#{@host}:#{@port}")
128
+ end
129
+
130
+ private
131
+
132
+ def binary_path(path = nil)
133
+ path = path.call if path.is_a?(Proc)
134
+ path ||= Platform.find_binary(self.class.executable)
135
+
136
+ raise Error::WebDriverError, self.class.missing_text unless path
137
+
138
+ Platform.assert_executable path
139
+ path
140
+ end
141
+
142
+ def build_process(*command)
143
+ WebDriver.logger.debug("Executing Process #{command}")
144
+ @process = ChildProcess.build(*command)
145
+ if WebDriver.logger.debug?
146
+ @process.io.stdout = @process.io.stderr = WebDriver.logger.io
147
+ elsif Platform.jruby?
148
+ # Apparently we need to read the output of drivers on JRuby.
149
+ @process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
150
+ end
151
+
152
+ @process
153
+ end
154
+
155
+ def connect_to_server
156
+ Net::HTTP.start(@host, @port) do |http|
157
+ http.open_timeout = STOP_TIMEOUT / 2
158
+ http.read_timeout = STOP_TIMEOUT / 2
159
+
160
+ yield http
161
+ end
162
+ end
163
+
164
+ def find_free_port
165
+ @port = PortProber.above(@port)
166
+ end
167
+
168
+ def start_process
169
+ @process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
170
+ # Note: this is a bug only in Windows 7
171
+ @process.leader = true unless Platform.windows?
172
+ @process.start
173
+ end
174
+
175
+ def stop_process
176
+ return if process_exited?
177
+
178
+ @process.stop STOP_TIMEOUT
179
+ @process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
180
+ end
181
+
182
+ def stop_server
183
+ return if process_exited?
184
+
185
+ connect_to_server { |http| http.get('/shutdown') }
186
+ end
187
+
188
+ def process_running?
189
+ defined?(@process) && @process&.alive?
190
+ end
191
+
192
+ def process_exited?
193
+ @process.nil? || @process.exited?
194
+ end
195
+
196
+ def connect_until_stable
197
+ socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
198
+ return if socket_poller.connected?
199
+
200
+ raise Error::WebDriverError, cannot_connect_error_text
201
+ end
202
+
203
+ def cannot_connect_error_text
204
+ "unable to connect to #{self.class.executable} #{@host}:#{@port}"
205
+ end
206
+
207
+ def socket_lock
208
+ @socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
209
+ end
210
+
211
+ protected
212
+
213
+ def extract_service_args(driver_opts)
214
+ driver_opts.key?(:args) ? driver_opts.delete(:args) : []
215
+ end
216
+
217
+ end # Service
218
+ end # WebDriver
219
+ end # Selenium
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -19,13 +19,11 @@
19
19
 
20
20
  module Selenium
21
21
  module WebDriver
22
-
23
22
  #
24
23
  # @api private
25
24
  #
26
25
 
27
26
  class SocketLock
28
-
29
27
  def initialize(port, timeout)
30
28
  @port = port
31
29
  @timeout = timeout
@@ -36,7 +34,7 @@ module Selenium
36
34
  # execution block if the lock could be successfully obtained.
37
35
  #
38
36
 
39
- def locked(&blk)
37
+ def locked
40
38
  lock
41
39
 
42
40
  begin
@@ -49,19 +47,21 @@ module Selenium
49
47
  private
50
48
 
51
49
  def lock
52
- max_time = Time.now + @timeout
50
+ max_time = current_time + @timeout
53
51
 
54
- until can_lock? || Time.now >= max_time
55
- sleep 0.1
56
- end
52
+ sleep 0.1 until can_lock? || current_time >= max_time
57
53
 
58
- unless did_lock?
59
- raise Error::WebDriverError, "unable to bind to locking port #{@port} within #{@timeout} seconds"
60
- end
54
+ return if did_lock?
55
+
56
+ raise Error::WebDriverError, "unable to bind to locking port #{@port} within #{@timeout} seconds"
57
+ end
58
+
59
+ def current_time
60
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
61
61
  end
62
62
 
63
63
  def release
64
- @server && @server.close
64
+ @server&.close
65
65
  end
66
66
 
67
67
  def can_lock?
@@ -70,14 +70,13 @@ module Selenium
70
70
 
71
71
  true
72
72
  rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => ex
73
- $stderr.puts "#{self}: #{ex.message}" if $DEBUG
73
+ WebDriver.logger.debug("#{self}: #{ex.message}")
74
74
  false
75
75
  end
76
76
 
77
77
  def did_lock?
78
- !!@server
78
+ !@server.nil?
79
79
  end
80
-
81
80
  end # SocketLock
82
81
  end # WebDriver
83
82
  end # Selenium