webdrivers 3.8.1 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,219 +2,153 @@
2
2
 
3
3
  require 'rubygems/package'
4
4
  require 'zip'
5
+ require 'webdrivers/logger'
6
+ require 'webdrivers/network'
7
+ require 'webdrivers/system'
8
+ require 'selenium-webdriver'
5
9
 
6
10
  module Webdrivers
7
- class Common
8
- class << self
9
- attr_accessor :version
11
+ class ConnectionError < StandardError
12
+ end
10
13
 
11
- def update
12
- unless site_available?
13
- return current_version.nil? ? nil : binary
14
- end
14
+ class VersionError < StandardError
15
+ end
15
16
 
16
- # Newer not specified or latest not found, so use existing
17
- return binary if desired_version.nil? && File.exist?(binary)
17
+ class << self
18
+ attr_accessor :proxy_addr, :proxy_port, :proxy_user, :proxy_pass, :install_dir
18
19
 
19
- # Can't find desired and no existing binary
20
- if desired_version.nil?
21
- msg = "Unable to find the latest version of #{file_name}; try downloading manually " \
22
- "from #{base_url} and place in #{install_dir}"
23
- raise StandardError, msg
24
- end
20
+ attr_writer :cache_time
25
21
 
26
- if correct_binary?
27
- Webdrivers.logger.debug 'Expected webdriver version found'
28
- return binary
29
- end
22
+ def cache_time
23
+ @cache_time || 0
24
+ end
30
25
 
31
- remove # Remove outdated exe
32
- download # Fetch latest
33
- end
26
+ def logger
27
+ @logger ||= Webdrivers::Logger.new
28
+ end
34
29
 
35
- def desired_version
36
- if version.is_a?(Gem::Version)
37
- version
38
- elsif version.nil?
39
- latest_version
40
- else
41
- normalize_version(version)
42
- end
43
- end
30
+ def configure
31
+ yield self
32
+ end
44
33
 
45
- def latest_version
46
- return @latest_version if @latest_version
34
+ def net_http_ssl_fix
35
+ raise 'Webdrivers.net_http_ssl_fix is no longer available.' \
36
+ ' Please see https://github.com/titusfortner/webdrivers#ssl_connect-errors.'
37
+ end
38
+ end
47
39
 
48
- raise StandardError, 'Can not reach site' unless site_available?
40
+ class Common
41
+ class << self
42
+ attr_writer :required_version
43
+ attr_reader :cache_warning
49
44
 
50
- @latest_version = downloads.keys.max
45
+ def version
46
+ Webdrivers.logger.deprecate("#{self.class}#version", "#{self.class}#required_version")
47
+ required_version
51
48
  end
52
49
 
53
- def remove
54
- max_attempts = 3
55
- attempts_made = 0
56
- delay = 0.5
57
- Webdrivers.logger.debug "Deleting #{binary}"
58
- @download_url = nil
59
-
60
- begin
61
- attempts_made += 1
62
- File.delete binary if File.exist? binary
63
- rescue Errno::EACCES # Solves an intermittent file locking issue on Windows
64
- sleep(delay)
65
- retry if File.exist?(binary) && attempts_made <= max_attempts
66
- raise
67
- end
50
+ def version=(version)
51
+ Webdrivers.logger.deprecate("#{self.class}#version=", "#{self.class}#required_version=")
52
+ self.required_version = version
68
53
  end
69
54
 
70
- def download
71
- raise StandardError, 'Can not reach site' unless site_available?
72
-
73
- filename = File.basename download_url
74
-
75
- FileUtils.mkdir_p(install_dir) unless File.exist?(install_dir)
76
- Dir.chdir install_dir do
77
- df = Tempfile.open(['', filename], binmode: true) do |file|
78
- file.print get(download_url)
79
- file
80
- end
81
-
82
- raise "Could not download #{download_url}" unless File.exist? df.to_path
83
-
84
- Webdrivers.logger.debug "Successfully downloaded #{df.to_path}"
55
+ def required_version
56
+ normalize_version @required_version
57
+ end
85
58
 
86
- decompress_file(df.to_path, filename)
87
- Webdrivers.logger.debug 'Decompression Complete'
88
- Webdrivers.logger.debug "Deleting #{df.to_path}"
89
- df.close!
59
+ def update
60
+ if correct_binary?
61
+ Webdrivers.logger.debug 'The required webdriver version is already on the system'
62
+ return driver_path
90
63
  end
91
- raise "Could not decompress #{download_url} to get #{binary}" unless File.exist?(binary)
92
64
 
93
- FileUtils.chmod 'ugo+rx', binary
94
- Webdrivers.logger.debug "Completed download and processing of #{binary}"
95
- binary
65
+ remove
66
+ System.download(download_url, driver_path)
96
67
  end
97
68
 
98
- def install_dir
99
- Webdrivers.install_dir || File.expand_path(File.join(ENV['HOME'], '.webdrivers'))
100
- end
69
+ def desired_version
70
+ old = "#{self.class}#desired_version"
71
+ new = "#{self.class}#required_version or #{self.class}#latest_version"
72
+ Webdrivers.logger.deprecate(old, new)
101
73
 
102
- def binary
103
- File.join install_dir, file_name
74
+ desired_version.version.empty? ? latest_version : normalize_version(desired_version)
104
75
  end
105
76
 
106
- protected
107
-
108
- def get(url, limit = 10)
109
- raise StandardError, 'Too many HTTP redirects' if limit.zero?
77
+ def remove
78
+ @download_url = nil
79
+ @latest_version = nil
80
+ System.delete "#{System.install_dir}/#{file_name.gsub('.exe', '')}.version"
81
+ System.delete driver_path
82
+ end
110
83
 
111
- response = http.get_response(URI(url))
112
- Webdrivers.logger.debug "Get response: #{response.inspect}"
84
+ def download
85
+ Webdrivers.logger.deprecate('#download', '#update')
86
+ System.download(download_url, driver_path)
87
+ end
113
88
 
114
- case response
115
- when Net::HTTPSuccess
116
- response.body
117
- when Net::HTTPRedirection
118
- location = response['location']
119
- Webdrivers.logger.debug "Redirected to #{location}"
120
- get(location, limit - 1)
121
- else
122
- response.value
123
- end
89
+ def binary
90
+ Webdrivers.logger.deprecate('#binary', '#driver_path')
91
+ driver_path
124
92
  end
125
93
 
126
- def http
127
- if using_proxy
128
- Net::HTTP.Proxy(Webdrivers.proxy_addr, Webdrivers.proxy_port,
129
- Webdrivers.proxy_user, Webdrivers.proxy_pass)
130
- else
131
- Net::HTTP
132
- end
94
+ def driver_path
95
+ File.join System.install_dir, file_name
133
96
  end
134
97
 
135
98
  private
136
99
 
137
100
  def download_url
138
- @download_url ||= downloads[desired_version]
139
- end
140
-
141
- def using_proxy
142
- Webdrivers.proxy_addr && Webdrivers.proxy_port
101
+ @download_url ||= if required_version.version.empty?
102
+ downloads[downloads.keys.max]
103
+ else
104
+ downloads[normalize_version(required_version)]
105
+ end
143
106
  end
144
107
 
145
- def downloaded?
146
- result = File.exist? binary
147
- Webdrivers.logger.debug "File is already downloaded: #{result}"
148
- result
108
+ def exists?
109
+ System.exists? driver_path
149
110
  end
150
111
 
151
- def site_available?
152
- Webdrivers.logger.debug "Looking for Site: #{base_url}"
153
- get(base_url)
154
- Webdrivers.logger.debug "Found Site: #{base_url}"
155
- true
156
- rescue StandardError => e
157
- Webdrivers.logger.debug e
158
- false
112
+ def correct_binary?
113
+ current_version == if required_version.version.empty?
114
+ latest_version
115
+ else
116
+ normalize_version(required_version)
117
+ end
118
+ rescue ConnectionError
119
+ driver_path if sufficient_binary?
159
120
  end
160
121
 
161
- def platform
162
- if Selenium::WebDriver::Platform.linux?
163
- "linux#{Selenium::WebDriver::Platform.bitsize}"
164
- elsif Selenium::WebDriver::Platform.mac?
165
- 'mac'
166
- else
167
- 'win'
168
- end
122
+ def sufficient_binary?
123
+ exists?
169
124
  end
170
125
 
171
- def decompress_file(filename, target)
172
- case filename
173
- when /tar\.gz$/
174
- Webdrivers.logger.debug 'Decompressing tar'
175
- untargz_file(filename)
176
- when /tar\.bz2$/
177
- Webdrivers.logger.debug 'Decompressing bz2'
178
- system "tar xjf #{filename}"
179
- filename.gsub('.tar.bz2', '')
180
- when /\.zip$/
181
- Webdrivers.logger.debug 'Decompressing zip'
182
- unzip_file(filename)
183
- else
184
- Webdrivers.logger.debug 'No Decompression needed'
185
- FileUtils.cp(filename, File.join(Dir.pwd, target))
186
- end
126
+ def normalize_version(version)
127
+ Gem::Version.new(version&.to_s)
187
128
  end
188
129
 
189
- def untargz_file(filename)
190
- tar_extract = Gem::Package::TarReader.new(Zlib::GzipReader.open(filename))
191
-
192
- File.open(file_name, 'w+b') do |ucf|
193
- tar_extract.each { |entry| ucf << entry.read }
194
- File.basename ucf
195
- end
130
+ def binary_version
131
+ version = System.call("#{driver_path} --version")
132
+ Webdrivers.logger.debug "Current version of #{driver_path} is #{version}"
133
+ version
134
+ rescue Errno::ENOENT
135
+ Webdrivers.logger.debug "No Such File or Directory: #{driver_path}"
136
+ nil
196
137
  end
197
138
 
198
- def unzip_file(filename)
199
- Zip::File.open(filename) do |zip_file|
200
- zip_file.each do |f|
201
- @top_path ||= f.name
202
- f_path = File.join(Dir.pwd, f.name)
203
- FileUtils.rm_rf(f_path) if File.exist?(f_path)
204
- FileUtils.mkdir_p(File.dirname(f_path)) unless File.exist?(File.dirname(f_path))
205
- zip_file.extract(f, f_path)
139
+ def with_cache(file_name)
140
+ if System.valid_cache?(file_name)
141
+ normalize_version System.cached_version(file_name)
142
+ else
143
+ unless cache_warning
144
+ Webdrivers.logger.warn 'Driver caching is turned off in this version, but will be '\
145
+ 'enabled by default in 4.x. Set the value with `Webdrivers#cache_time=` in seconds'
146
+ @cache_warning = true
206
147
  end
148
+ version = yield
149
+ System.cache_version(file_name, version)
150
+ normalize_version version
207
151
  end
208
- @top_path
209
- end
210
-
211
- # Already have latest version downloaded?
212
- def correct_binary?
213
- desired_version == current_version && File.exist?(binary)
214
- end
215
-
216
- def normalize_version(version)
217
- Gem::Version.new(version.to_s)
218
152
  end
219
153
  end
220
154
  end
@@ -1,41 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'nokogiri'
4
+ require 'webdrivers/common'
4
5
 
5
6
  module Webdrivers
6
7
  class Geckodriver < Common
7
8
  class << self
8
9
  def current_version
9
10
  Webdrivers.logger.debug 'Checking current version'
10
- return nil unless downloaded?
11
+ return nil unless exists?
11
12
 
12
- string = `#{binary} --version`
13
- Webdrivers.logger.debug "Current version of #{binary} is #{string}"
14
- normalize_version string.match(/geckodriver (\d+\.\d+\.\d+)/)[1]
15
- end
13
+ version = binary_version
14
+ return nil if version.nil?
16
15
 
17
- private
16
+ normalize_version version.match(/geckodriver (\d+\.\d+\.\d+)/)[1]
17
+ end
18
18
 
19
- def downloads # rubocop:disable Metrics/AbcSize
20
- doc = Nokogiri::HTML.parse(get(base_url))
21
- items = doc.css('.py-1 a').collect { |item| item['href'] }
22
- items.reject! { |item| item.include?('archive') }
23
- items.select! { |item| item.include?(platform) }
24
- ds = items.each_with_object({}) do |item, hash|
25
- key = normalize_version item[/v(\d+\.\d+\.\d+)/, 1]
26
- hash[key] = "https://github.com#{item}"
27
- end
28
- Webdrivers.logger.debug "Versions now located on downloads site: #{ds.keys}"
29
- ds
19
+ def latest_version
20
+ @latest_version ||= with_cache(file_name) { normalize_version(Network.get_url("#{base_url}/latest")[/[^v]*$/]) }
30
21
  end
31
22
 
23
+ private
24
+
32
25
  def file_name
33
- platform == 'win' ? 'geckodriver.exe' : 'geckodriver'
26
+ System.platform == 'win' ? 'geckodriver.exe' : 'geckodriver'
34
27
  end
35
28
 
36
29
  def base_url
37
30
  'https://github.com/mozilla/geckodriver/releases'
38
31
  end
32
+
33
+ def download_url
34
+ @download_url ||= required_version.version.empty? ? direct_url(latest_version) : direct_url(required_version)
35
+ end
36
+
37
+ def direct_url(version)
38
+ "#{base_url}/download/v#{version}/geckodriver-v#{version}-#{platform_ext}"
39
+ end
40
+
41
+ def platform_ext
42
+ case System.platform
43
+ when 'linux'
44
+ "linux#{System.bitsize}.tar.gz"
45
+ when 'mac'
46
+ 'macos.tar.gz'
47
+ when 'win'
48
+ "win#{System.bitsize}.zip"
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
56
+ ::Selenium::WebDriver::Firefox::Service.driver_path = proc { ::Webdrivers::Geckodriver.update }
57
+ else
58
+ # v3.141.0 and lower
59
+ module Selenium
60
+ module WebDriver
61
+ module Firefox
62
+ def self.driver_path
63
+ @driver_path ||= Webdrivers::Geckodriver.update
64
+ end
65
+ end
39
66
  end
40
67
  end
41
68
  end
@@ -2,17 +2,23 @@
2
2
 
3
3
  require 'nokogiri'
4
4
  require 'rubygems/version'
5
+ require 'webdrivers/common'
5
6
 
6
7
  module Webdrivers
7
8
  class IEdriver < Common
8
9
  class << self
9
10
  def current_version
10
11
  Webdrivers.logger.debug 'Checking current version'
11
- return nil unless downloaded?
12
+ return nil unless exists?
12
13
 
13
- string = `#{binary} --version`
14
- Webdrivers.logger.debug "Current version of #{binary} is #{string}"
15
- normalize_version string.match(/IEDriverServer.exe (\d\.\d+\.\d*\.\d*)/)[1]
14
+ version = binary_version
15
+ return nil if version.nil?
16
+
17
+ normalize_version version.match(/IEDriverServer.exe (\d\.\d+\.\d+)/)[1]
18
+ end
19
+
20
+ def latest_version
21
+ @latest_version ||= with_cache(file_name) { downloads.keys.max }
16
22
  end
17
23
 
18
24
  private
@@ -26,7 +32,7 @@ module Webdrivers
26
32
  end
27
33
 
28
34
  def downloads
29
- doc = Nokogiri::XML.parse(get(base_url))
35
+ doc = Nokogiri::XML.parse(Network.get(base_url))
30
36
  items = doc.css('Key').collect(&:text)
31
37
  items.select! { |item| item.include?('IEDriverServer_Win32') }
32
38
  ds = items.each_with_object({}) do |item, hash|
@@ -39,3 +45,18 @@ module Webdrivers
39
45
  end
40
46
  end
41
47
  end
48
+
49
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
50
+ ::Selenium::WebDriver::IE::Service.driver_path = proc { ::Webdrivers::IEdriver.update }
51
+ else
52
+ # v3.141.0 and lower
53
+ module Selenium
54
+ module WebDriver
55
+ module IE
56
+ def self.driver_path
57
+ @driver_path ||= Webdrivers::IEdriver.update
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end