selenium-webdriver 2.53.4 → 3.0.0.beta1

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 (122) hide show
  1. data/CHANGES +24 -18
  2. data/README.md +2 -3
  3. data/lib/selenium/server.rb +64 -68
  4. data/lib/selenium/webdriver.rb +5 -9
  5. data/lib/selenium/webdriver/chrome.rb +18 -3
  6. data/lib/selenium/webdriver/chrome/bridge.rb +13 -16
  7. data/lib/selenium/webdriver/chrome/profile.rb +7 -9
  8. data/lib/selenium/webdriver/chrome/service.rb +8 -84
  9. data/lib/selenium/webdriver/common.rb +1 -2
  10. data/lib/selenium/webdriver/common/action_builder.rb +28 -38
  11. data/lib/selenium/webdriver/common/alert.rb +7 -10
  12. data/lib/selenium/webdriver/common/bridge_helper.rb +10 -15
  13. data/lib/selenium/webdriver/common/driver.rb +19 -28
  14. data/lib/selenium/webdriver/common/driver_extensions/has_input_devices.rb +0 -3
  15. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +4 -6
  16. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +4 -5
  17. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +0 -2
  18. data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +0 -2
  19. data/lib/selenium/webdriver/common/driver_extensions/has_touch_screen.rb +0 -2
  20. data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +0 -3
  21. data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +3 -6
  22. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +2 -5
  23. data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +2 -5
  24. data/lib/selenium/webdriver/common/element.rb +27 -29
  25. data/lib/selenium/webdriver/common/error.rb +17 -20
  26. data/lib/selenium/webdriver/common/file_reaper.rb +3 -9
  27. data/lib/selenium/webdriver/common/html5/local_storage.rb +6 -8
  28. data/lib/selenium/webdriver/common/html5/session_storage.rb +6 -8
  29. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +6 -15
  30. data/lib/selenium/webdriver/common/keyboard.rb +7 -12
  31. data/lib/selenium/webdriver/common/keys.rb +67 -69
  32. data/lib/selenium/webdriver/common/log_entry.rb +3 -4
  33. data/lib/selenium/webdriver/common/logs.rb +2 -4
  34. data/lib/selenium/webdriver/common/mouse.rb +9 -12
  35. data/lib/selenium/webdriver/common/navigation.rb +2 -4
  36. data/lib/selenium/webdriver/common/options.rb +16 -19
  37. data/lib/selenium/webdriver/common/platform.rb +61 -90
  38. data/lib/selenium/webdriver/common/port_prober.rb +1 -2
  39. data/lib/selenium/webdriver/common/profile_helper.rb +5 -8
  40. data/lib/selenium/webdriver/common/proxy.rb +58 -70
  41. data/lib/selenium/webdriver/common/search_context.rb +15 -19
  42. data/lib/selenium/webdriver/common/service.rb +127 -0
  43. data/lib/selenium/webdriver/common/socket_lock.rb +5 -11
  44. data/lib/selenium/webdriver/common/socket_poller.rb +4 -9
  45. data/lib/selenium/webdriver/common/target_locator.rb +11 -13
  46. data/lib/selenium/webdriver/common/timeouts.rb +4 -6
  47. data/lib/selenium/webdriver/common/touch_action_builder.rb +2 -4
  48. data/lib/selenium/webdriver/common/touch_screen.rb +15 -18
  49. data/lib/selenium/webdriver/common/w3c_error.rb +3 -6
  50. data/lib/selenium/webdriver/common/wait.rb +6 -11
  51. data/lib/selenium/webdriver/common/window.rb +12 -15
  52. data/lib/selenium/webdriver/common/zipper.rb +6 -8
  53. data/lib/selenium/webdriver/edge.rb +18 -3
  54. data/lib/selenium/webdriver/edge/bridge.rb +11 -16
  55. data/lib/selenium/webdriver/edge/legacy_support.rb +38 -39
  56. data/lib/selenium/webdriver/edge/service.rb +8 -82
  57. data/lib/selenium/webdriver/firefox.rb +25 -6
  58. data/lib/selenium/webdriver/firefox/binary.rb +37 -53
  59. data/lib/selenium/webdriver/firefox/bridge.rb +3 -6
  60. data/lib/selenium/webdriver/firefox/extension.rb +4 -6
  61. data/lib/selenium/webdriver/firefox/extension/prefs.json +1 -10
  62. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  63. data/lib/selenium/webdriver/firefox/launcher.rb +8 -11
  64. data/lib/selenium/webdriver/firefox/profile.rb +40 -42
  65. data/lib/selenium/webdriver/firefox/profiles_ini.rb +8 -15
  66. data/lib/selenium/webdriver/firefox/service.rb +23 -79
  67. data/lib/selenium/webdriver/firefox/util.rb +0 -2
  68. data/lib/selenium/webdriver/firefox/w3c_bridge.rb +2 -4
  69. data/lib/selenium/webdriver/ie.rb +16 -7
  70. data/lib/selenium/webdriver/ie/bridge.rb +16 -23
  71. data/lib/selenium/webdriver/{iphone.rb → ie/service.rb} +26 -4
  72. data/lib/selenium/webdriver/phantomjs.rb +8 -3
  73. data/lib/selenium/webdriver/phantomjs/bridge.rb +9 -11
  74. data/lib/selenium/webdriver/phantomjs/service.rb +17 -81
  75. data/lib/selenium/webdriver/remote.rb +0 -2
  76. data/lib/selenium/webdriver/remote/bridge.rb +193 -191
  77. data/lib/selenium/webdriver/remote/capabilities.rb +60 -90
  78. data/lib/selenium/webdriver/remote/commands.rb +197 -192
  79. data/lib/selenium/webdriver/remote/http/common.rb +15 -13
  80. data/lib/selenium/webdriver/remote/http/curb.rb +5 -9
  81. data/lib/selenium/webdriver/remote/http/default.rb +32 -37
  82. data/lib/selenium/webdriver/remote/http/persistent.rb +4 -6
  83. data/lib/selenium/webdriver/remote/response.rb +13 -21
  84. data/lib/selenium/webdriver/remote/server_error.rb +1 -3
  85. data/lib/selenium/webdriver/remote/w3c_bridge.rb +200 -195
  86. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +38 -46
  87. data/lib/selenium/webdriver/remote/w3c_commands.rb +116 -113
  88. data/lib/selenium/webdriver/safari.rb +23 -7
  89. data/lib/selenium/{client/javascript_frameworks/jquery.rb → webdriver/safari/apple_bridge.rb} +28 -9
  90. data/lib/selenium/webdriver/safari/browser.rb +0 -2
  91. data/lib/selenium/webdriver/safari/{bridge.rb → legacy_bridge.rb} +12 -9
  92. data/lib/selenium/webdriver/safari/options.rb +3 -4
  93. data/lib/selenium/webdriver/safari/resources/client.js +56 -7255
  94. data/lib/selenium/webdriver/safari/server.rb +18 -24
  95. data/lib/selenium/{client/javascript_frameworks/prototype.rb → webdriver/safari/service.rb} +27 -9
  96. data/lib/selenium/webdriver/support.rb +1 -0
  97. data/lib/selenium/webdriver/support/abstract_event_listener.rb +17 -2
  98. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -3
  99. data/lib/selenium/webdriver/support/color.rb +55 -38
  100. data/lib/selenium/webdriver/{android.rb → support/escaper.rb} +19 -4
  101. data/lib/selenium/webdriver/support/event_firing_bridge.rb +36 -38
  102. data/lib/selenium/webdriver/support/select.rb +33 -84
  103. data/selenium-webdriver.gemspec +23 -23
  104. metadata +19 -30
  105. data/lib/selenium-client.rb +0 -21
  106. data/lib/selenium/client.rb +0 -57
  107. data/lib/selenium/client/base.rb +0 -151
  108. data/lib/selenium/client/driver.rb +0 -29
  109. data/lib/selenium/client/errors.rb +0 -28
  110. data/lib/selenium/client/extensions.rb +0 -132
  111. data/lib/selenium/client/idiomatic.rb +0 -507
  112. data/lib/selenium/client/javascript_expression_builder.rb +0 -135
  113. data/lib/selenium/client/legacy_driver.rb +0 -1722
  114. data/lib/selenium/client/protocol.rb +0 -123
  115. data/lib/selenium/client/selenium_helper.rb +0 -49
  116. data/lib/selenium/rake/server_task.rb +0 -176
  117. data/lib/selenium/webdriver/android/bridge.rb +0 -68
  118. data/lib/selenium/webdriver/common/core_ext/base64.rb +0 -28
  119. data/lib/selenium/webdriver/common/core_ext/dir.rb +0 -61
  120. data/lib/selenium/webdriver/common/html5/location.rb +0 -19
  121. data/lib/selenium/webdriver/ie/server.rb +0 -133
  122. data/lib/selenium/webdriver/iphone/bridge.rb +0 -64
@@ -20,12 +20,10 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Firefox
23
-
24
23
  # @api private
25
24
  class ProfilesIni
26
-
27
25
  def initialize
28
- @ini_path = File.join(Util.app_data_path, "profiles.ini")
26
+ @ini_path = File.join(Util.app_data_path, 'profiles.ini')
29
27
  @profile_paths = {}
30
28
 
31
29
  parse if File.exist?(@ini_path)
@@ -52,29 +50,24 @@ module Selenium
52
50
  string.split("\n").each do |line|
53
51
  case line
54
52
  when /^\[Profile/
55
- if p = path_for(name, is_relative, path)
56
- @profile_paths[name] = p
57
- name, path = nil
58
- end
53
+ name, path = nil if path_for(name, is_relative, path)
59
54
  when /^Name=(.+)$/
60
- name = $1.strip
55
+ name = Regexp.last_match(1).strip
61
56
  when /^IsRelative=(.+)$/
62
- is_relative = $1.strip == "1"
57
+ is_relative = Regexp.last_match(1).strip == '1'
63
58
  when /^Path=(.+)$/
64
- path = $1.strip
59
+ path = Regexp.last_match(1).strip
65
60
  end
66
61
  end
67
62
 
68
- if p = path_for(name, is_relative, path)
69
- @profile_paths[name] = p
70
- end
63
+ p = path_for(name, is_relative, path)
64
+ @profile_paths[name] = p if p
71
65
  end
72
66
 
73
67
  def path_for(name, is_relative, path)
74
68
  return unless [name, path].any?
75
- path = is_relative ? File.join(Util.app_data_path, path) : path
69
+ is_relative ? File.join(Util.app_data_path, path) : path
76
70
  end
77
-
78
71
  end # ProfilesIni
79
72
  end # Firefox
80
73
  end # WebDriver
@@ -20,104 +20,48 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Firefox
23
-
24
23
  #
25
24
  # @api private
26
25
  #
27
- class Service
28
- START_TIMEOUT = 20
29
- SOCKET_LOCK_TIMEOUT = 45
30
- STOP_TIMEOUT = 5
31
- DEFAULT_PORT = 4444
32
- MISSING_TEXT = "Unable to find geckodriver. Please download the executable from https://github.com/mozilla/geckodriver/releases"
33
-
34
- def self.executable_path
35
- @executable_path ||= (
36
- path = Platform.find_binary("geckodriver*") || Platform.find_binary("wires*")
37
- path or raise Error::WebDriverError, MISSING_TEXT
38
- Platform.assert_executable path
39
-
40
- path
41
- )
42
- end
43
-
44
- def self.executable_path=(path)
45
- Platform.assert_executable path
46
- @executable_path = path
47
- end
48
-
49
- def self.default_service(*extra_args)
50
- new executable_path, DEFAULT_PORT, *extra_args
51
- end
52
26
 
53
- def initialize(executable_path, port, *extra_args)
54
- @executable_path = executable_path
55
- @host = Platform.localhost
56
- @port = Integer(port)
57
-
58
- raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
59
-
60
- @extra_args = extra_args
61
- end
62
-
63
- def start
64
- Platform.exit_hook { stop } # make sure we don't leave the server running
65
-
66
- socket_lock.locked do
67
- find_free_port
68
- start_process
69
- connect_until_stable
70
- end
71
- end
27
+ class Service < WebDriver::Service
28
+ DEFAULT_PORT = 4444
72
29
 
73
- def stop
74
- return if @process.nil? || @process.exited?
75
-
76
- Net::HTTP.start(@host, @port) do |http|
77
- http.open_timeout = STOP_TIMEOUT / 2
78
- http.read_timeout = STOP_TIMEOUT / 2
79
-
80
- http.head("/shutdown")
81
- end
82
- ensure
83
- stop_process
84
- end
85
-
86
- def uri
87
- URI.parse "http://#{@host}:#{@port}"
88
- end
89
-
90
- def find_free_port
91
- @port = PortProber.above @port
92
- end
30
+ private
93
31
 
94
32
  def start_process
95
33
  server_command = [@executable_path, "--binary=#{Firefox::Binary.path}", "--webdriver-port=#{@port}", *@extra_args]
96
34
  @process = ChildProcess.build(*server_command)
97
35
 
98
- @process.io.inherit! if $DEBUG || Platform.os == :windows
36
+ if $DEBUG
37
+ @process.io.inherit!
38
+ elsif Platform.windows?
39
+ # workaround stdio inheritance issue
40
+ # https://github.com/mozilla/geckodriver/issues/48
41
+ @process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
42
+ end
43
+
99
44
  @process.start
100
45
  end
101
46
 
102
47
  def stop_process
103
- @process.poll_for_exit STOP_TIMEOUT
104
- rescue ChildProcess::TimeoutError
105
- @process.stop STOP_TIMEOUT
106
- end
107
-
108
- def connect_until_stable
109
- @socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
110
-
111
- unless @socket_poller.connected?
112
- raise Error::WebDriverError, "unable to connect to Mozilla geckodriver #{@host}:#{@port}"
48
+ super
49
+ return unless Platform.windows? && !$DEBUG
50
+ begin
51
+ @process.io.close
52
+ rescue
53
+ nil
113
54
  end
114
55
  end
115
56
 
116
- def socket_lock
117
- @socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
57
+ def stop_server
58
+ connect_to_server { |http| http.head('/shutdown') }
118
59
  end
119
60
 
61
+ def cannot_connect_error_text
62
+ "unable to connect to Mozilla geckodriver #{@host}:#{@port}"
63
+ end
120
64
  end # Service
121
65
  end # Firefox
122
66
  end # WebDriver
123
- end # Service
67
+ end # Selenium
@@ -20,7 +20,6 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Firefox
23
-
24
23
  # @api private
25
24
  module Util
26
25
  module_function
@@ -41,7 +40,6 @@ module Selenium
41
40
  def stringified?(str)
42
41
  str =~ /^".*"$/
43
42
  end
44
-
45
43
  end # Util
46
44
  end # Firefox
47
45
  end # WebDriver
@@ -20,16 +20,15 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Firefox
23
-
24
23
  # @api private
25
24
  class W3CBridge < Remote::W3CBridge
26
-
27
25
  def initialize(opts = {})
28
26
  caps = opts[:desired_capabilities] ||= Remote::W3CCapabilities.firefox
29
27
  Binary.path = caps[:firefox_binary] if caps[:firefox_binary]
30
28
 
31
- @service = Service.default_service(*extract_service_args(opts))
29
+ @service = Service.new(Firefox.driver_path, Service::DEFAULT_PORT, *extract_service_args(opts))
32
30
  @service.start
31
+
33
32
  opts[:url] = @service.uri
34
33
 
35
34
  super
@@ -59,7 +58,6 @@ module Selenium
59
58
  service_log_path = opts.delete(:service_log_path)
60
59
  service_log_path ? ["--log-path=#{service_log_path}"] : []
61
60
  end
62
-
63
61
  end # W3CBridge
64
62
  end # Firefox
65
63
  end # WebDriver
@@ -17,23 +17,32 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
 
20
+ require 'selenium/webdriver/ie/bridge'
21
+ require 'selenium/webdriver/ie/service'
22
+
20
23
  module Selenium
21
24
  module WebDriver
22
-
23
- # @api private
24
25
  module IE
26
+ MISSING_TEXT = <<-ERROR.tr("\n", '').freeze
27
+ Unable to find IEDriverServer. Please download the server from
28
+ http://selenium-release.storage.googleapis.com/index.html and place it
29
+ somewhere on your PATH. More info at https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver.
30
+ ERROR
31
+
25
32
  def self.driver_path=(path)
26
33
  Platform.assert_executable path
27
34
  @driver_path = path
28
35
  end
29
36
 
30
37
  def self.driver_path
31
- @driver_path ||= nil
32
- end
38
+ @driver_path ||= begin
39
+ path = Platform.find_binary('IEDriverServer')
40
+ raise Error::WebDriverError, MISSING_TEXT unless path
41
+ Platform.assert_executable path
33
42
 
43
+ path
44
+ end
45
+ end
34
46
  end # IE
35
47
  end # WebDriver
36
48
  end # Selenium
37
-
38
- require 'selenium/webdriver/ie/server'
39
- require 'selenium/webdriver/ie/bridge'
@@ -20,48 +20,33 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module IE
23
-
24
23
  #
25
24
  # @api private
26
25
  #
27
26
 
28
27
  class Bridge < Remote::Bridge
29
-
30
- HOST = Platform.localhost
31
- DEFAULT_PORT = 5555
32
- DEFAULT_TIMEOUT = 30
33
-
34
28
  def initialize(opts = {})
35
29
  caps = opts.delete(:desired_capabilities) { Remote::Capabilities.internet_explorer }
36
- timeout = opts.delete(:timeout) { DEFAULT_TIMEOUT }
37
- port = opts.delete(:port) { DEFAULT_PORT }
30
+ port = opts.delete(:port) { Service::DEFAULT_PORT }
38
31
  http_client = opts.delete(:http_client)
39
32
  ignore_mode = opts.delete(:introduce_flakiness_by_ignoring_security_domains)
40
33
  native_events = opts.delete(:native_events) != false
41
- implementation = opts.delete(:implementation)
42
34
 
43
- @server = Server.get(:implementation => implementation)
44
-
45
- @server.log_level = opts.delete(:log_level) if opts[:log_level]
46
- @server.log_file = opts.delete(:log_file) if opts[:log_file]
35
+ @service = Service.new(IE.driver_path, port, *extract_service_args(opts))
47
36
 
48
37
  unless opts.empty?
49
38
  raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
50
39
  end
51
40
 
52
- @port = @server.start Integer(port), timeout
53
-
54
- if ignore_mode
55
- caps['ignoreProtectedModeSettings'] = true
56
- end
41
+ @service.start
57
42
 
43
+ caps['ignoreProtectedModeSettings'] = true if ignore_mode
58
44
  caps['nativeEvents'] = native_events
59
45
 
60
46
  remote_opts = {
61
- :url => @server.uri,
62
- :desired_capabilities => caps
47
+ url: @service.uri,
48
+ desired_capabilities: caps
63
49
  }
64
-
65
50
  remote_opts[:http_client] = http_client if http_client
66
51
 
67
52
  super(remote_opts)
@@ -77,11 +62,19 @@ module Selenium
77
62
 
78
63
  def quit
79
64
  super
80
- nil
81
65
  ensure
82
- @server.stop
66
+ @service.stop if @service
83
67
  end
84
68
 
69
+ private
70
+
71
+ def extract_service_args(opts)
72
+ args = []
73
+ args << "--log-level=#{opts.delete(:log_level).to_s.upcase}" if opts[:log_level]
74
+ args << "--log-file=#{opts.delete(:log_file)}" if opts[:log_file]
75
+ args << "--implementation=#{opts.delete(:implementation).to_s.upcase}" if opts[:implementation]
76
+ args
77
+ end
85
78
  end # Bridge
86
79
  end # IE
87
80
  end # WebDriver
@@ -19,10 +19,32 @@
19
19
 
20
20
  module Selenium
21
21
  module WebDriver
22
- module IPhone
22
+ module IE
23
+ #
24
+ # @api private
25
+ #
23
26
 
24
- end # IPhone
27
+ class Service < WebDriver::Service
28
+ DEFAULT_PORT = 5555
29
+
30
+ private
31
+
32
+ def stop_server
33
+ # server can only be stopped as process
34
+ end
35
+
36
+ def start_process
37
+ server_command = [@executable_path, "--port=#{@port}", *@extra_args]
38
+ @process = ChildProcess.new(*server_command)
39
+
40
+ @process.io.inherit! if $DEBUG
41
+ @process.start
42
+ end
43
+
44
+ def cannot_connect_error_text
45
+ "unable to connect to IE server #{@host}:#{@port}"
46
+ end
47
+ end # Server
48
+ end # IE
25
49
  end # WebDriver
26
50
  end # Selenium
27
-
28
- require 'selenium/webdriver/iphone/bridge'
@@ -24,8 +24,8 @@ require 'selenium/webdriver/phantomjs/bridge'
24
24
 
25
25
  module Selenium
26
26
  module WebDriver
27
-
28
27
  module PhantomJS
28
+ MISSING_TEXT = 'Unable to find phantomjs executable.'.freeze
29
29
 
30
30
  def self.path=(path)
31
31
  Platform.assert_executable path
@@ -33,9 +33,14 @@ module Selenium
33
33
  end
34
34
 
35
35
  def self.path
36
- @path ||= Platform.find_binary("phantomjs")
37
- end
36
+ @path ||= begin
37
+ path = Platform.find_binary('phantomjs')
38
+ raise Error::WebDriverError, MISSING_TEXT unless path
39
+ Platform.assert_executable path
38
40
 
41
+ path
42
+ end
43
+ end
39
44
  end # PhantomJS
40
45
  end # WebDriver
41
46
  end # Selenium
@@ -20,33 +20,32 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module PhantomJS
23
-
24
-
23
+ #
25
24
  # @api private
26
- class Bridge < Remote::Bridge
25
+ #
27
26
 
27
+ class Bridge < Remote::Bridge
28
28
  def initialize(opts = {})
29
29
  http_client = opts.delete(:http_client)
30
30
  caps = opts.delete(:desired_capabilities) { Remote::Capabilities.phantomjs }
31
31
 
32
- if opts.has_key?(:url)
32
+ if opts.key?(:url)
33
33
  url = opts.delete(:url)
34
34
  else
35
35
  args = opts.delete(:args) || caps['phantomjs.cli.args']
36
- port = opts.delete(:port)
37
36
 
38
- @service = Service.default_service(port)
39
- @service.start(args)
37
+ @service = Service.new(PhantomJS.path, Service::DEFAULT_PORT, *args)
38
+ @service.start
40
39
 
41
40
  url = @service.uri
42
41
  end
43
42
 
44
43
  remote_opts = {
45
- :url => url,
46
- :desired_capabilities => caps
44
+ url: url,
45
+ desired_capabilities: caps
47
46
  }
48
47
 
49
- remote_opts.merge!(:http_client => http_client) if http_client
48
+ remote_opts[:http_client] = http_client if http_client
50
49
 
51
50
  super(remote_opts)
52
51
  end
@@ -71,7 +70,6 @@ module Selenium
71
70
  ensure
72
71
  @service.stop if @service
73
72
  end
74
-
75
73
  end # Bridge
76
74
  end # PhantomJS
77
75
  end # WebDriver