selenium-webdriver 2.53.4 → 3.8.0

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 (152) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +363 -10
  3. data/LICENSE +1 -1
  4. data/README.md +2 -3
  5. data/lib/selenium-webdriver.rb +0 -2
  6. data/lib/selenium/server.rb +69 -70
  7. data/lib/selenium/webdriver.rb +32 -23
  8. data/lib/selenium/webdriver/atoms.rb +18 -0
  9. data/lib/selenium/webdriver/atoms/getAttribute.js +8 -0
  10. data/lib/selenium/webdriver/chrome.rb +8 -6
  11. data/lib/selenium/webdriver/chrome/driver.rb +112 -0
  12. data/lib/selenium/webdriver/chrome/options.rb +168 -0
  13. data/lib/selenium/webdriver/chrome/profile.rb +17 -17
  14. data/lib/selenium/webdriver/chrome/service.rb +22 -89
  15. data/lib/selenium/webdriver/common.rb +13 -6
  16. data/lib/selenium/webdriver/common/action_builder.rb +49 -57
  17. data/lib/selenium/webdriver/common/alert.rb +5 -15
  18. data/lib/selenium/webdriver/common/bridge_helper.rb +10 -17
  19. data/lib/selenium/webdriver/common/driver.rb +53 -68
  20. data/lib/selenium/webdriver/common/driver_extensions/{has_input_devices.rb → has_addons.rb} +13 -23
  21. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +4 -8
  22. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +4 -7
  23. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +0 -4
  24. data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +0 -4
  25. data/lib/selenium/webdriver/common/driver_extensions/has_touch_screen.rb +1 -5
  26. data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +0 -5
  27. data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +4 -9
  28. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +7 -7
  29. data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +2 -7
  30. data/lib/selenium/webdriver/common/element.rb +57 -39
  31. data/lib/selenium/webdriver/common/error.rb +204 -106
  32. data/lib/selenium/webdriver/common/file_reaper.rb +3 -11
  33. data/lib/selenium/webdriver/common/html5/local_storage.rb +6 -10
  34. data/lib/selenium/webdriver/common/html5/session_storage.rb +6 -10
  35. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +7 -18
  36. data/lib/selenium/webdriver/{safari/options.rb → common/interactions/input_device.rb} +20 -31
  37. data/lib/selenium/webdriver/common/interactions/interaction.rb +50 -0
  38. data/lib/selenium/webdriver/{safari/browser.rb → common/interactions/interactions.rb} +16 -15
  39. data/lib/selenium/webdriver/common/interactions/key_actions.rb +143 -0
  40. data/lib/selenium/webdriver/common/interactions/key_input.rb +62 -0
  41. data/lib/selenium/webdriver/{android.rb → common/interactions/none_input.rb} +11 -6
  42. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +353 -0
  43. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +132 -0
  44. data/lib/selenium/webdriver/common/keyboard.rb +7 -14
  45. data/lib/selenium/webdriver/common/keys.rb +99 -82
  46. data/lib/selenium/webdriver/common/log_entry.rb +3 -6
  47. data/lib/selenium/webdriver/common/logger.rb +140 -0
  48. data/lib/selenium/webdriver/common/logs.rb +2 -6
  49. data/lib/selenium/webdriver/common/mouse.rb +9 -14
  50. data/lib/selenium/webdriver/common/navigation.rb +2 -6
  51. data/lib/selenium/webdriver/common/options.rb +20 -23
  52. data/lib/selenium/webdriver/common/platform.rb +70 -97
  53. data/lib/selenium/webdriver/common/port_prober.rb +3 -4
  54. data/lib/selenium/webdriver/common/profile_helper.rb +6 -11
  55. data/lib/selenium/webdriver/common/proxy.rb +58 -72
  56. data/lib/selenium/webdriver/common/search_context.rb +22 -29
  57. data/lib/selenium/webdriver/common/service.rb +161 -0
  58. data/lib/selenium/webdriver/common/socket_lock.rb +6 -14
  59. data/lib/selenium/webdriver/common/socket_poller.rb +5 -12
  60. data/lib/selenium/webdriver/common/target_locator.rb +11 -15
  61. data/lib/selenium/webdriver/common/timeouts.rb +4 -8
  62. data/lib/selenium/webdriver/common/touch_action_builder.rb +2 -6
  63. data/lib/selenium/webdriver/common/touch_screen.rb +19 -23
  64. data/lib/selenium/webdriver/common/w3c_action_builder.rb +209 -0
  65. data/lib/selenium/webdriver/{phantomjs.rb → common/w3c_options.rb} +16 -14
  66. data/lib/selenium/webdriver/common/wait.rb +6 -13
  67. data/lib/selenium/webdriver/common/window.rb +48 -17
  68. data/lib/selenium/webdriver/common/zipper.rb +6 -10
  69. data/lib/selenium/webdriver/edge.rb +5 -12
  70. data/lib/selenium/webdriver/edge/bridge.rb +32 -63
  71. data/lib/selenium/webdriver/edge/driver.rb +73 -0
  72. data/lib/selenium/webdriver/edge/service.rb +18 -87
  73. data/lib/selenium/webdriver/firefox.rb +20 -11
  74. data/lib/selenium/webdriver/firefox/binary.rb +40 -56
  75. data/lib/selenium/webdriver/firefox/driver.rb +48 -0
  76. data/lib/selenium/webdriver/firefox/extension.rb +18 -8
  77. data/lib/selenium/webdriver/firefox/extension/prefs.json +3 -11
  78. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  79. data/lib/selenium/webdriver/firefox/launcher.rb +13 -22
  80. data/lib/selenium/webdriver/firefox/legacy/driver.rb +79 -0
  81. data/lib/selenium/webdriver/{iphone.rb → firefox/marionette/bridge.rb} +25 -6
  82. data/lib/selenium/webdriver/firefox/marionette/driver.rb +96 -0
  83. data/lib/selenium/webdriver/firefox/options.rb +149 -0
  84. data/lib/selenium/webdriver/firefox/profile.rb +46 -46
  85. data/lib/selenium/webdriver/firefox/profiles_ini.rb +8 -18
  86. data/lib/selenium/webdriver/firefox/service.rb +23 -83
  87. data/lib/selenium/webdriver/firefox/util.rb +0 -4
  88. data/lib/selenium/webdriver/ie.rb +4 -8
  89. data/lib/selenium/webdriver/ie/driver.rb +90 -0
  90. data/lib/selenium/webdriver/ie/options.rb +136 -0
  91. data/lib/selenium/webdriver/ie/service.rb +58 -0
  92. data/lib/selenium/webdriver/remote.rb +8 -16
  93. data/lib/selenium/webdriver/remote/bridge.rb +96 -565
  94. data/lib/selenium/webdriver/remote/capabilities.rb +76 -94
  95. data/lib/selenium/webdriver/remote/driver.rb +49 -0
  96. data/lib/selenium/webdriver/remote/http/common.rb +22 -20
  97. data/lib/selenium/webdriver/remote/http/curb.rb +9 -12
  98. data/lib/selenium/webdriver/remote/http/default.rb +54 -41
  99. data/lib/selenium/webdriver/remote/http/persistent.rb +9 -8
  100. data/lib/selenium/webdriver/remote/oss/bridge.rb +586 -0
  101. data/lib/selenium/webdriver/remote/oss/commands.rb +221 -0
  102. data/lib/selenium/webdriver/remote/response.rb +39 -27
  103. data/lib/selenium/webdriver/remote/server_error.rb +1 -5
  104. data/lib/selenium/webdriver/remote/w3c/bridge.rb +573 -0
  105. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +290 -0
  106. data/lib/selenium/webdriver/remote/w3c/commands.rb +148 -0
  107. data/lib/selenium/webdriver/safari.rb +20 -29
  108. data/lib/selenium/webdriver/{firefox/w3c_bridge.rb → safari/driver.rb} +21 -30
  109. data/lib/selenium/webdriver/safari/service.rb +57 -0
  110. data/lib/selenium/webdriver/support.rb +1 -2
  111. data/lib/selenium/webdriver/support/abstract_event_listener.rb +17 -4
  112. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -5
  113. data/lib/selenium/webdriver/support/color.rb +57 -42
  114. data/lib/selenium/webdriver/support/escaper.rb +41 -0
  115. data/lib/selenium/webdriver/support/event_firing_bridge.rb +36 -40
  116. data/lib/selenium/webdriver/support/select.rb +33 -86
  117. data/selenium-webdriver.gemspec +22 -25
  118. metadata +254 -261
  119. data/lib/selenium-client.rb +0 -21
  120. data/lib/selenium/client.rb +0 -57
  121. data/lib/selenium/client/base.rb +0 -151
  122. data/lib/selenium/client/driver.rb +0 -29
  123. data/lib/selenium/client/errors.rb +0 -28
  124. data/lib/selenium/client/extensions.rb +0 -132
  125. data/lib/selenium/client/idiomatic.rb +0 -507
  126. data/lib/selenium/client/javascript_expression_builder.rb +0 -135
  127. data/lib/selenium/client/javascript_frameworks/jquery.rb +0 -32
  128. data/lib/selenium/client/javascript_frameworks/prototype.rb +0 -32
  129. data/lib/selenium/client/legacy_driver.rb +0 -1722
  130. data/lib/selenium/client/protocol.rb +0 -123
  131. data/lib/selenium/client/selenium_helper.rb +0 -49
  132. data/lib/selenium/rake/server_task.rb +0 -176
  133. data/lib/selenium/webdriver/android/bridge.rb +0 -68
  134. data/lib/selenium/webdriver/chrome/bridge.rb +0 -139
  135. data/lib/selenium/webdriver/common/core_ext/base64.rb +0 -28
  136. data/lib/selenium/webdriver/common/core_ext/dir.rb +0 -61
  137. data/lib/selenium/webdriver/common/html5/location.rb +0 -19
  138. data/lib/selenium/webdriver/common/w3c_error.rb +0 -194
  139. data/lib/selenium/webdriver/edge/legacy_support.rb +0 -117
  140. data/lib/selenium/webdriver/firefox/bridge.rb +0 -89
  141. data/lib/selenium/webdriver/ie/bridge.rb +0 -88
  142. data/lib/selenium/webdriver/ie/server.rb +0 -133
  143. data/lib/selenium/webdriver/iphone/bridge.rb +0 -64
  144. data/lib/selenium/webdriver/phantomjs/bridge.rb +0 -78
  145. data/lib/selenium/webdriver/phantomjs/service.rb +0 -130
  146. data/lib/selenium/webdriver/remote/commands.rb +0 -211
  147. data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -668
  148. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -236
  149. data/lib/selenium/webdriver/remote/w3c_commands.rb +0 -132
  150. data/lib/selenium/webdriver/safari/bridge.rb +0 -135
  151. data/lib/selenium/webdriver/safari/resources/client.js +0 -7255
  152. data/lib/selenium/webdriver/safari/server.rb +0 -187
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
- #
3
1
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
2
  # or more contributor license agreements. See the NOTICE file
5
3
  # distributed with this work for additional information
@@ -40,13 +38,15 @@ module Selenium
40
38
 
41
39
  IGNORED_ERRORS = [Errno::EADDRNOTAVAIL]
42
40
  IGNORED_ERRORS << Errno::EBADF if Platform.cygwin?
41
+ IGNORED_ERRORS << Errno::EACCES if Platform.windows?
42
+ IGNORED_ERRORS.freeze
43
43
 
44
44
  def self.free?(port)
45
45
  Platform.interfaces.each do |host|
46
46
  begin
47
47
  TCPServer.new(host, port).close
48
48
  rescue *IGNORED_ERRORS => ex
49
- $stderr.puts "port prober could not bind to #{host}:#{port} (#{ex.message})" if $DEBUG
49
+ WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{ex.message})")
50
50
  # ignored - some machines appear unable to bind to some of their interfaces
51
51
  end
52
52
  end
@@ -55,7 +55,6 @@ module Selenium
55
55
  rescue SocketError, Errno::EADDRINUSE
56
56
  false
57
57
  end
58
-
59
58
  end # PortProber
60
59
  end # WebDriver
61
60
  end # Selenium
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
- #
3
1
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
2
  # or more contributor license agreements. See the NOTICE file
5
3
  # distributed with this work for additional information
@@ -19,7 +17,6 @@
19
17
 
20
18
  module Selenium
21
19
  module WebDriver
22
-
23
20
  #
24
21
  # @api private
25
22
  #
@@ -28,27 +25,26 @@ module Selenium
28
25
  #
29
26
 
30
27
  module ProfileHelper
31
-
32
28
  def self.included(base)
33
29
  base.extend ClassMethods
34
30
  end
35
31
 
36
- def as_json(opts = nil)
37
- {'zip' => Zipper.zip(layout_on_disk)}
32
+ def as_json(*)
33
+ {"zip" => Zipper.zip(layout_on_disk)}
38
34
  end
39
35
 
40
- def to_json(*args)
36
+ def to_json(*)
41
37
  JSON.generate as_json
42
38
  end
43
39
 
44
40
  private
45
41
 
46
42
  def create_tmp_copy(directory)
47
- tmp_directory = Dir.mktmpdir("webdriver-rb-profilecopy")
43
+ tmp_directory = Dir.mktmpdir('webdriver-rb-profilecopy')
48
44
 
49
45
  # TODO: must be a better way..
50
46
  FileUtils.rm_rf tmp_directory
51
- FileUtils.mkdir_p File.dirname(tmp_directory), :mode => 0700
47
+ FileUtils.mkdir_p File.dirname(tmp_directory), mode: 0o700
52
48
  FileUtils.cp_r directory, tmp_directory
53
49
 
54
50
  tmp_directory
@@ -72,7 +68,7 @@ module Selenium
72
68
  tmp_dir = Dir.mktmpdir
73
69
  begin
74
70
  zip_path = File.join(tmp_dir, "webdriver-profile-duplicate-#{json.hash}.zip")
75
- File.open(zip_path, "wb") { |zip_file| zip_file << Base64.decode64(data) }
71
+ File.open(zip_path, 'wb') { |zip_file| zip_file << Base64.decode64(data) }
76
72
 
77
73
  new Zipper.unzip(zip_path)
78
74
  ensure
@@ -80,7 +76,6 @@ module Selenium
80
76
  end
81
77
  end
82
78
  end # ClassMethods
83
-
84
79
  end # ProfileHelper
85
80
  end # WebDriver
86
81
  end # Selenium
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
- #
3
1
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
2
  # or more contributor license agreements. See the NOTICE file
5
3
  # distributed with this work for additional information
@@ -21,45 +19,56 @@ module Selenium
21
19
  module WebDriver
22
20
  class Proxy
23
21
  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
22
+ direct: 'DIRECT', # Direct connection, no proxy (default on Windows).
23
+ manual: 'MANUAL', # Manual proxy settings (e.g., for httpProxy).
24
+ pac: 'PAC', # Proxy autoconfiguration from URL.
25
+ auto_detect: 'AUTODETECT', # Proxy autodetection (presumably with WPAD).
26
+ system: 'SYSTEM' # Use system settings (default on Linux).
27
+ }.freeze
28
+
29
+ ALLOWED = {type: 'proxyType',
30
+ ftp: 'ftpProxy',
31
+ http: 'httpProxy',
32
+ no_proxy: 'noProxy',
33
+ pac: 'proxyAutoconfigUrl',
34
+ ssl: 'sslProxy',
35
+ auto_detect: 'autodetect',
36
+ socks: 'socksProxy',
37
+ socks_username: 'socksUsername',
38
+ socks_password: 'socksPassword'}.freeze
39
+
40
+ ALLOWED.each_key { |t| attr_reader t }
41
+
42
+ def self.json_create(data)
43
+ data['proxyType'] = data['proxyType'].downcase.to_sym
44
+ return if data['proxyType'] == :unspecified
45
+
46
+ proxy = new
47
+
48
+ ALLOWED.each do |k, v|
49
+ proxy.send("#{k}=", data[v]) if data.key?(v)
50
+ end
51
+
52
+ proxy
53
+ end
41
54
 
42
55
  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}"
56
+ not_allowed = []
57
+
58
+ opts.each do |k, v|
59
+ if ALLOWED.key?(k)
60
+ send("#{k}=", v)
61
+ else
62
+ not_allowed << k
63
+ end
58
64
  end
65
+
66
+ return if not_allowed.empty?
67
+ raise ArgumentError, "unknown option#{'s' if not_allowed.size != 1}: #{not_allowed.inspect}"
59
68
  end
60
69
 
61
70
  def ==(other)
62
- other.kind_of?(self.class) && as_json == other.as_json
71
+ other.is_a?(self.class) && as_json == other.as_json
63
72
  end
64
73
  alias_method :eql?, :==
65
74
 
@@ -109,7 +118,7 @@ module Selenium
109
118
  end
110
119
 
111
120
  def type=(type)
112
- unless TYPES.has_key? type
121
+ unless TYPES.key? type
113
122
  raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}"
114
123
  end
115
124
 
@@ -120,49 +129,26 @@ module Selenium
120
129
  @type = type
121
130
  end
122
131
 
123
- def as_json(opts = nil)
132
+ def as_json(*)
124
133
  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
134
+ 'proxyType' => TYPES[type],
135
+ 'ftpProxy' => ftp,
136
+ 'httpProxy' => http,
137
+ 'noProxy' => no_proxy,
138
+ 'proxyAutoconfigUrl' => pac,
139
+ 'sslProxy' => ssl,
140
+ 'autodetect' => auto_detect,
141
+ 'socksProxy' => socks,
142
+ 'socksUsername' => socks_username,
143
+ 'socksPassword' => socks_password
144
+ }.delete_if { |_k, v| v.nil? }
137
145
 
138
146
  json_result if json_result.length > 1
139
147
  end
140
148
 
141
- def to_json(*args)
149
+ def to_json(*)
142
150
  JSON.generate as_json
143
151
  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
152
  end # Proxy
167
153
  end # WebDriver
168
154
  end # Selenium
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
- #
3
1
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
2
  # or more contributor license agreements. See the NOTICE file
5
3
  # distributed with this work for additional information
@@ -20,43 +18,44 @@
20
18
  module Selenium
21
19
  module WebDriver
22
20
  module SearchContext
23
-
24
21
  # @api private
25
22
  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
- }
23
+ class: 'class name',
24
+ class_name: 'class name',
25
+ css: 'css selector',
26
+ id: 'id',
27
+ link: 'link text',
28
+ link_text: 'link text',
29
+ name: 'name',
30
+ partial_link_text: 'partial link text',
31
+ tag_name: 'tag name',
32
+ xpath: 'xpath'
33
+ }.freeze
37
34
 
38
35
  #
39
- # Find the first element matching the given arguments.
36
+ # Find the first element matching the given arguments
40
37
  #
41
38
  # When using Element#find_element with :xpath, be aware that webdriver
42
39
  # follows standard conventions: a search prefixed with "//" will search
43
40
  # the entire document, not just the children of this current node. Use
44
41
  # ".//" to limit your search to the children of the receiving Element.
45
42
  #
46
- # @param [:class, :class_name, :css, :id, :link_text, :link, :partial_link_text, :name, :tag_name, :xpath] how
47
- # @param [String] what
43
+ # @overload find_element(how, what)
44
+ # @param [Symbol, String] how The method to find the element by
45
+ # @param [String] what The locator to use
46
+ # @overload find_element(opts)
47
+ # @param [Hash] opts Find options
48
+ # @option opts [Symbol] :how Key named after the method to find the element by, containing the locator
48
49
  # @return [Element]
49
50
  #
50
51
  # @raise [Error::NoSuchElementError] if the element doesn't exist
51
52
  #
52
- #
53
53
 
54
54
  def find_element(*args)
55
55
  how, what = extract_args(args)
56
56
 
57
- unless by = FINDERS[how.to_sym]
58
- raise ArgumentError, "cannot find element by #{how.inspect}"
59
- end
57
+ by = FINDERS[how.to_sym]
58
+ raise ArgumentError, "cannot find element by #{how.inspect}" unless by
60
59
 
61
60
  bridge.find_element_by by, what.to_s, ref
62
61
  rescue Selenium::WebDriver::Error::TimeOutError
@@ -69,17 +68,12 @@ module Selenium
69
68
  #
70
69
  # @see SearchContext#find_element
71
70
  #
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
71
 
77
72
  def find_elements(*args)
78
73
  how, what = extract_args(args)
79
74
 
80
- unless by = FINDERS[how.to_sym]
81
- raise ArgumentError, "cannot find elements by #{how.inspect}"
82
- end
75
+ by = FINDERS[how.to_sym]
76
+ raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
83
77
 
84
78
  bridge.find_elements_by by, what.to_s, ref
85
79
  rescue Selenium::WebDriver::Error::TimeOutError
@@ -111,7 +105,6 @@ module Selenium
111
105
  raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
112
106
  end
113
107
  end
114
-
115
108
  end # SearchContext
116
109
  end # WebDriver
117
110
  end # Selenium
@@ -0,0 +1,161 @@
1
+ # Licensed to the Software Freedom Conservancy (SFC) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The SFC licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ module Selenium
19
+ module WebDriver
20
+ #
21
+ # Base class implementing default behavior of service object,
22
+ # responsible for starting and stopping driver implementations.
23
+ #
24
+ # Subclasses must implement the following private methods:
25
+ # * #start_process
26
+ # * #stop_server
27
+ # * #cannot_connect_error_text
28
+ #
29
+ # @api private
30
+ #
31
+
32
+ class Service
33
+ START_TIMEOUT = 20
34
+ SOCKET_LOCK_TIMEOUT = 45
35
+ STOP_TIMEOUT = 20
36
+
37
+ @executable = nil
38
+ @missing_text = nil
39
+
40
+ class << self
41
+ attr_reader :executable, :missing_text
42
+ end
43
+
44
+ attr_accessor :host
45
+
46
+ def initialize(executable_path, port, driver_opts)
47
+ @executable_path = binary_path(executable_path)
48
+ @host = Platform.localhost
49
+ @port = Integer(port)
50
+ @extra_args = extract_service_args(driver_opts)
51
+
52
+ raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
53
+ end
54
+
55
+ def binary_path(path)
56
+ path = Platform.find_binary(self.class.executable) if path.nil?
57
+ raise Error::WebDriverError, self.class.missing_text unless path
58
+ Platform.assert_executable path
59
+ path
60
+ end
61
+
62
+ def start
63
+ if process_running?
64
+ raise "already started: #{uri.inspect} #{@executable_path.inspect}"
65
+ end
66
+
67
+ Platform.exit_hook { stop } # make sure we don't leave the server running
68
+
69
+ socket_lock.locked do
70
+ find_free_port
71
+ start_process
72
+ connect_until_stable
73
+ end
74
+ end
75
+
76
+ def stop
77
+ stop_server
78
+ @process.poll_for_exit STOP_TIMEOUT
79
+ rescue ChildProcess::TimeoutError
80
+ ensure
81
+ stop_process
82
+ end
83
+
84
+ def uri
85
+ @uri ||= URI.parse("http://#{@host}:#{@port}")
86
+ end
87
+
88
+ private
89
+
90
+ def build_process(*command)
91
+ WebDriver.logger.debug("Executing Process #{command}")
92
+ @process = ChildProcess.build(*command)
93
+ if WebDriver.logger.debug?
94
+ @process.io.stdout = @process.io.stderr = WebDriver.logger.io
95
+ elsif Platform.jruby?
96
+ # Apparently we need to read the output of drivers on JRuby.
97
+ @process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
98
+ end
99
+
100
+ @process
101
+ end
102
+
103
+ def connect_to_server
104
+ Net::HTTP.start(@host, @port) do |http|
105
+ http.open_timeout = STOP_TIMEOUT / 2
106
+ http.read_timeout = STOP_TIMEOUT / 2
107
+
108
+ yield http
109
+ end
110
+ end
111
+
112
+ def find_free_port
113
+ @port = PortProber.above(@port)
114
+ end
115
+
116
+ def start_process
117
+ raise NotImplementedError, 'subclass responsibility'
118
+ end
119
+
120
+ def stop_process
121
+ return if process_exited?
122
+ @process.stop STOP_TIMEOUT
123
+ @process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
124
+ end
125
+
126
+ def stop_server
127
+ return if process_exited?
128
+ connect_to_server { |http| http.get('/shutdown') }
129
+ end
130
+
131
+ def process_running?
132
+ @process && @process.alive?
133
+ end
134
+
135
+ def process_exited?
136
+ @process.nil? || @process.exited?
137
+ end
138
+
139
+ def connect_until_stable
140
+ socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
141
+ return if socket_poller.connected?
142
+ raise Error::WebDriverError, cannot_connect_error_text
143
+ end
144
+
145
+ def cannot_connect_error_text
146
+ raise NotImplementedError, 'subclass responsibility'
147
+ end
148
+
149
+ def socket_lock
150
+ @socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
151
+ end
152
+
153
+ protected
154
+
155
+ def extract_service_args(driver_opts)
156
+ driver_opts.key?(:args) ? driver_opts.delete(:args) : []
157
+ end
158
+
159
+ end # Service
160
+ end # WebDriver
161
+ end # Selenium