webdrivers 4.0.1 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -42,9 +42,10 @@ module Webdrivers
42
42
  # Returns currently installed Chrome/Chromium version.
43
43
  #
44
44
  # @return [Gem::Version]
45
- def chrome_version
45
+ def browser_version
46
46
  normalize_version ChromeFinder.version
47
47
  end
48
+ alias chrome_version browser_version
48
49
 
49
50
  #
50
51
  # Returns url with domain for calls to get this driver.
@@ -102,7 +103,7 @@ module Webdrivers
102
103
  # @example
103
104
  # 73.0.3683.75 -> 73.0.3683
104
105
  def release_version
105
- chrome = normalize_version(chrome_version)
106
+ chrome = normalize_version(browser_version)
106
107
  normalize_version(chrome.segments[0..2].join('.'))
107
108
  end
108
109
 
@@ -19,6 +19,7 @@ module Webdrivers
19
19
  end
20
20
 
21
21
  DEFAULT_CACHE_TIME = 86_400 # 24 hours
22
+ DEFAULT_INSTALL_DIR = File.expand_path(File.join(ENV['HOME'], '.webdrivers'))
22
23
 
23
24
  class << self
24
25
  attr_accessor :proxy_addr, :proxy_port, :proxy_user, :proxy_pass
@@ -30,7 +31,8 @@ module Webdrivers
30
31
  # are set, it defaults to 86,400 Seconds (24 hours).
31
32
  #
32
33
  def cache_time
33
- (ENV['WD_CACHE_TIME'] || @cache_time || DEFAULT_CACHE_TIME).to_i
34
+ @cache_time ||= (ENV['WD_CACHE_TIME'] || DEFAULT_CACHE_TIME)
35
+ @cache_time.to_i
34
36
  end
35
37
 
36
38
  #
@@ -38,7 +40,7 @@ module Webdrivers
38
40
  #
39
41
  # @return [String]
40
42
  def install_dir
41
- @install_dir || ENV['WD_INSTALL_DIR'] || File.expand_path(File.join(ENV['HOME'], '.webdrivers'))
43
+ @install_dir ||= ENV['WD_INSTALL_DIR'] || DEFAULT_INSTALL_DIR
42
44
  end
43
45
 
44
46
  def logger
@@ -65,7 +67,7 @@ module Webdrivers
65
67
  raise 'Webdrivers.net_http_ssl_fix is no longer available.' \
66
68
  ' Please see https://github.com/titusfortner/webdrivers#ssl_connect-errors.'
67
69
  end
68
- end
70
+ end
69
71
 
70
72
  class Common
71
73
  class << self
@@ -76,7 +78,7 @@ end
76
78
  #
77
79
  # @return [Gem::Version]
78
80
  def required_version
79
- normalize_version @required_version
81
+ normalize_version(@required_version ||= nil)
80
82
  end
81
83
 
82
84
  #
@@ -109,7 +111,7 @@ end
109
111
  #
110
112
  # @return [String]
111
113
  def driver_path
112
- System.escape_path File.join(System.install_dir, file_name)
114
+ File.absolute_path File.join(System.install_dir, file_name)
113
115
  end
114
116
 
115
117
  private
@@ -145,7 +147,7 @@ end
145
147
  end
146
148
 
147
149
  def binary_version
148
- version = System.call("#{driver_path} --version")
150
+ version = System.call(driver_path, '--version')
149
151
  Webdrivers.logger.debug "Current version of #{driver_path} is #{version}"
150
152
  version
151
153
  rescue Errno::ENOENT
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Webdrivers
4
+ #
5
+ # @api private
6
+ #
7
+ class EdgeFinder
8
+ class << self
9
+ def version
10
+ version = send("#{System.platform}_version", location)
11
+
12
+ raise VersionError, 'Failed to find Edge binary or its version.' if version.nil? || version.empty?
13
+
14
+ Webdrivers.logger.debug "Browser version: #{version}"
15
+ version[/\d+\.\d+\.\d+\.\d+/] # Microsoft Edge 73.0.3683.75 -> 73.0.3683.75
16
+ end
17
+
18
+ def location
19
+ user_defined_location || send("#{System.platform}_location")
20
+ end
21
+
22
+ private
23
+
24
+ def user_defined_location
25
+ if Selenium::WebDriver::EdgeChrome.path
26
+ Webdrivers.logger.debug "Selenium::WebDriver::EdgeChrome.path: #{Selenium::WebDriver::EdgeChrome.path}"
27
+ return Selenium::WebDriver::EdgeChrome.path
28
+ end
29
+
30
+ return if ENV['WD_EDGE_CHROME_PATH'].nil?
31
+
32
+ Webdrivers.logger.debug "WD_EDGE_CHROME_PATH: #{ENV['WD_EDGE_CHROME_PATH']}"
33
+ ENV['WD_EDGE_CHROME_PATH']
34
+ end
35
+
36
+ def win_location
37
+ envs = %w[LOCALAPPDATA PROGRAMFILES PROGRAMFILES(X86)]
38
+ directories = ['\\Microsoft\\Edge Dev\\Application', '\\Microsoft\\Edge SxS\\Application']
39
+ file = 'msedge.exe'
40
+
41
+ directories.each do |dir|
42
+ envs.each do |root|
43
+ option = "#{ENV[root]}\\#{dir}\\#{file}"
44
+ return option if File.exist?(option)
45
+ end
46
+ end
47
+ nil
48
+ end
49
+
50
+ def mac_location
51
+ directories = ['', File.expand_path('~')]
52
+ files = ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
53
+ '/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev',
54
+ '/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary']
55
+
56
+ directories.each do |dir|
57
+ files.each do |file|
58
+ option = "#{dir}/#{file}"
59
+ return option if File.exist?(option)
60
+ end
61
+ end
62
+ nil
63
+ end
64
+
65
+ def linux_location
66
+ raise 'Default location not yet known'
67
+ end
68
+
69
+ def win_version(location)
70
+ System.call("powershell (Get-ItemProperty '#{location}').VersionInfo.ProductVersion")&.strip
71
+ end
72
+
73
+ def linux_version(location)
74
+ System.call(location, '--product-version')&.strip
75
+ end
76
+
77
+ def mac_version(location)
78
+ System.call(location, '--version')&.strip
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+ require 'webdrivers/common'
5
+ require 'webdrivers/chromedriver'
6
+ require 'webdrivers/edge_finder'
7
+
8
+ module Webdrivers
9
+ class Edgedriver < Chromedriver
10
+ class << self
11
+ undef :chrome_version
12
+ #
13
+ # Returns currently installed Edge version.
14
+ #
15
+ # @return [Gem::Version]
16
+ def browser_version
17
+ normalize_version EdgeFinder.version
18
+ end
19
+
20
+ #
21
+ # Returns url with domain for calls to get this driver.
22
+ #
23
+ # @return [String]
24
+ def base_url
25
+ # 'https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/'
26
+ 'https://msedgedriver.azureedge.net/'
27
+ end
28
+
29
+ def remove
30
+ super
31
+ end
32
+
33
+ private
34
+
35
+ def latest_point_release(version)
36
+ # Microsoft doesn't currently provide LATEST_RELEASE_X.Y.Z - only use X
37
+ # but require the driver version be >= the passed in version
38
+ str = Network.get(URI.join(base_url, "LATEST_RELEASE_#{version.segments[0]}"))
39
+ latest_release = normalize_version(str.encode('ASCII-8BIT', 'UTF-16'))
40
+ raise VersionError unless latest_release >= version
41
+
42
+ latest_release
43
+ rescue NetworkError, VersionError
44
+ msg = failed_to_find_message(version)
45
+ Webdrivers.logger.debug msg
46
+ raise VersionError, msg
47
+ end
48
+
49
+ def failed_to_find_message(version)
50
+ msg = "Unable to find latest point release version for #{version}."
51
+ msg = begin
52
+ # str = Network.get(URI.join(base_url, 'LATEST_RELEASE'))
53
+ # Microsoft doesn't yet/ever support LATEST_RELEASE - Use Canary as latest
54
+ str = Network.get(URI.join(base_url, 'LATEST_CANARY'))
55
+ latest_release = normalize_version(str.encode('ASCII-8BIT', 'UTF-16'))
56
+ if version > latest_release
57
+ "#{msg} You appear to be using a non-production version of Edge."
58
+ else
59
+ msg
60
+ end
61
+ rescue NetworkError
62
+ "#{msg} A network issue is preventing determination of latest msedgedriver release."
63
+ end
64
+
65
+ "#{msg} Please set `Webdrivers::Edgedriver.required_version = <desired driver version>` "\
66
+ "to a known edgedriver version: Can not reach #{base_url}"
67
+ end
68
+
69
+ def file_name
70
+ System.platform == 'win' ? 'msedgedriver.exe' : 'msedgedriver'
71
+ end
72
+
73
+ def download_url
74
+ return @download_url if @download_url
75
+
76
+ version = if required_version == EMPTY_VERSION
77
+ latest_version
78
+ else
79
+ normalize_version(required_version)
80
+ end
81
+
82
+ file_name = System.platform == 'win' ? 'win32' : "#{System.platform}64"
83
+ url = "#{base_url}/#{version}/edgedriver_#{file_name}.zip"
84
+ Webdrivers.logger.debug "msedgedriver URL: #{url}"
85
+ @download_url = url
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ if defined? Selenium::WebDriver::EdgeChrome
92
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
93
+ ::Selenium::WebDriver::EdgeChrome::Service.driver_path = proc { ::Webdrivers::Edgedriver.update }
94
+ else
95
+ # v3.141.0 and lower
96
+ module Selenium
97
+ module WebDriver
98
+ module EdgeChrome
99
+ def self.driver_path
100
+ @driver_path ||= Webdrivers::Edgedriver.update
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rubygems/package'
4
4
  require 'zip'
5
+ require 'English'
5
6
 
6
7
  module Webdrivers
7
8
  #
@@ -89,7 +90,7 @@ module Webdrivers
89
90
  when /tar\.bz2$/
90
91
  untarbz2_file(tempfile)
91
92
  when /\.zip$/
92
- unzip_file(tempfile)
93
+ unzip_file(tempfile, File.basename(target))
93
94
  else
94
95
  Webdrivers.logger.debug 'No Decompression needed'
95
96
  FileUtils.cp(tempfile, File.join(Dir.pwd, file_name))
@@ -114,19 +115,17 @@ module Webdrivers
114
115
  end
115
116
  end
116
117
 
117
- def unzip_file(filename)
118
+ def unzip_file(filename, driver_name)
118
119
  Webdrivers.logger.debug "Decompressing #{filename}"
119
120
 
120
121
  Zip::File.open(filename) do |zip_file|
121
- zip_file.each do |f|
122
- @top_path ||= f.name
123
- f_path = File.join(Dir.pwd, f.name)
124
- delete(f_path)
125
- FileUtils.mkdir_p(File.dirname(f_path)) unless File.exist?(File.dirname(f_path))
126
- zip_file.extract(f, f_path)
127
- end
122
+ driver = zip_file.get_entry(driver_name)
123
+ f_path = File.join(Dir.pwd, driver.name)
124
+ delete(f_path)
125
+ FileUtils.mkdir_p(File.dirname(f_path)) unless File.exist?(File.dirname(f_path))
126
+ zip_file.extract(driver, f_path)
128
127
  end
129
- @top_path
128
+ driver_name
130
129
  end
131
130
 
132
131
  def platform
@@ -145,15 +144,16 @@ module Webdrivers
145
144
  Selenium::WebDriver::Platform.bitsize
146
145
  end
147
146
 
148
- def call(cmd)
147
+ def call(process, arg = nil)
148
+ cmd = arg ? [process, arg] : process # Windows provides powershell command (process) only, no args.
149
149
  Webdrivers.logger.debug "making System call: #{cmd}"
150
- `#{cmd}`
151
- end
152
-
153
- def escape_path(path)
154
- return path.tr('/', '\\') if platform == 'win' # Windows
150
+ p = IO.popen(cmd)
151
+ out = p.read
152
+ p.close
153
+ raise "Failed to make system call: #{cmd}" unless $CHILD_STATUS.success?
155
154
 
156
- Shellwords.escape(path) # Linux and macOS
155
+ Webdrivers.logger.debug "System call returned: #{out}"
156
+ out
157
157
  end
158
158
  end
159
159
  end
@@ -19,8 +19,6 @@ namespace :webdrivers do
19
19
  desc 'Remove and download updated chromedriver if necessary'
20
20
  task :update, [:version] do |_, args|
21
21
  args.with_defaults(version: 0)
22
- Webdrivers.cache_time = ENV.fetch('WD_CACHE_TIME', 86_400)
23
- Webdrivers.install_dir = ENV.fetch('WD_INSTALL_DIR', nil)
24
22
  Webdrivers::Chromedriver.required_version = args.version
25
23
  Webdrivers::Chromedriver.update
26
24
  Webdrivers.logger.info "Updated to chromedriver #{Webdrivers::Chromedriver.current_version}"
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :webdrivers do
4
+ require 'webdrivers/edgedriver'
5
+
6
+ namespace :edgedriver do
7
+ Webdrivers.logger.level = :info
8
+
9
+ desc 'Print current edgedriver version'
10
+ task :version do
11
+ gem_ver = Webdrivers::Edgedriver.current_version
12
+ if gem_ver
13
+ Webdrivers.logger.info "edgedriver #{gem_ver.version}"
14
+ else
15
+ Webdrivers.logger.warn 'No existing edgedriver found.'
16
+ end
17
+ end
18
+
19
+ desc 'Remove and download updated edgedriver if necessary'
20
+ task :update, [:version] do |_, args|
21
+ args.with_defaults(version: 0)
22
+ Webdrivers::Edgedriver.required_version = args.version
23
+ Webdrivers::Edgedriver.update
24
+ Webdrivers.logger.info "Updated to edgedriver #{Webdrivers::Edgedriver.current_version}"
25
+ end
26
+
27
+ desc 'Force remove edgedriver'
28
+ task :remove do
29
+ unless File.exist? Webdrivers::Edgedriver.driver_path
30
+ Webdrivers.logger.info 'No existing edgedriver to remove.'
31
+ next # Return early
32
+ end
33
+
34
+ cur_version = Webdrivers::Edgedriver.current_version
35
+ Webdrivers::Edgedriver.remove
36
+
37
+ if File.exist? Webdrivers::Edgedriver.driver_path # Failed for some reason
38
+ Webdrivers.logger.error 'Failed to remove edgedriver. Please try removing manually.'
39
+ else
40
+ Webdrivers.logger.info "Removed edgedriver #{cur_version}."
41
+ end
42
+ end
43
+ end
44
+ end
@@ -19,8 +19,6 @@ namespace :webdrivers do
19
19
  desc 'Remove and download updated geckodriver if necessary'
20
20
  task :update, [:version] do |_, args|
21
21
  args.with_defaults(version: 0)
22
- Webdrivers.cache_time = ENV.fetch('WD_CACHE_TIME', 86_400)
23
- Webdrivers.install_dir = ENV.fetch('WD_INSTALL_DIR', nil)
24
22
  Webdrivers::Geckodriver.required_version = args.version
25
23
  Webdrivers::Geckodriver.update
26
24
  Webdrivers.logger.info "Updated to geckodriver #{Webdrivers::Geckodriver.current_version}"
@@ -19,8 +19,6 @@ namespace :webdrivers do
19
19
  desc 'Remove and download updated IEDriverServer if necessary'
20
20
  task :update, [:version] do |_, args|
21
21
  args.with_defaults(version: 0)
22
- Webdrivers.cache_time = ENV.fetch('WD_CACHE_TIME', 86_400)
23
- Webdrivers.install_dir = ENV.fetch('WD_INSTALL_DIR', nil)
24
22
  Webdrivers::IEdriver.required_version = args.version
25
23
  Webdrivers::IEdriver.update
26
24
  Webdrivers.logger.info "Updated to IEDriverServer #{Webdrivers::IEdriver.current_version}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Webdrivers
4
- VERSION = '4.0.1'
4
+ VERSION = '4.1.0'
5
5
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Webdrivers::ChromeFinder do
6
+ let(:chrome_finder) { described_class }
7
+
8
+ context 'when the user relies on the gem to figure out the location of Chrome' do
9
+ it 'determines the location correctly based on the current OS' do
10
+ expect(chrome_finder.location).not_to be_nil
11
+ end
12
+ end
13
+
14
+ context 'when the user provides a path to the Chrome binary' do
15
+ it 'uses Selenium::WebDriver::Chrome.path when it is defined' do
16
+ Selenium::WebDriver::Chrome.path = chrome_finder.location
17
+ allow(chrome_finder).to receive(:win_location)
18
+ allow(chrome_finder).to receive(:mac_location)
19
+ allow(chrome_finder).to receive(:linux_location)
20
+ expect(chrome_finder.version).not_to be_nil
21
+ expect(chrome_finder).not_to have_received(:win_location)
22
+ expect(chrome_finder).not_to have_received(:mac_location)
23
+ expect(chrome_finder).not_to have_received(:linux_location)
24
+ end
25
+
26
+ it "uses ENV['WD_CHROME_PATH'] when it is defined" do
27
+ allow(ENV).to receive(:[]).with('WD_CHROME_PATH').and_return(chrome_finder.location)
28
+ allow(chrome_finder).to receive(:win_location)
29
+ allow(chrome_finder).to receive(:mac_location)
30
+ allow(chrome_finder).to receive(:linux_location)
31
+ expect(chrome_finder.version).not_to be_nil
32
+ expect(chrome_finder).not_to have_received(:win_location)
33
+ expect(chrome_finder).not_to have_received(:mac_location)
34
+ expect(chrome_finder).not_to have_received(:linux_location)
35
+ end
36
+
37
+ it 'uses Selenium::WebDriver::Chrome.path over WD_CHROME_PATH' do
38
+ Selenium::WebDriver::Chrome.path = chrome_finder.location
39
+ allow(ENV).to receive(:[]).with('WD_CHROME_PATH').and_return('my_wd_chrome_path')
40
+ expect(chrome_finder.version).not_to be_nil
41
+ expect(ENV).not_to have_received(:[]).with('WD_CHROME_PATH')
42
+ end
43
+ end
44
+ end