UNPKG

selenium-standalone

Version:

installs a `selenium-standalone` command line to install and start a standalone selenium server

288 lines (233 loc) 6.77 kB
module.exports = install; var async = require('async'); var crypto = require('crypto'); var fs = require('fs'); var merge = require('lodash').merge; var mkdirp = require('mkdirp'); var path = require('path'); var request = require('request'); var computeDownloadUrls = require('./compute-download-urls'); var computeFsPaths = require('./compute-fs-paths'); var defaultConfig = require('./default-config'); var noop = require('./noop'); function install(opts, cb) { var total = 0; var progress = 0; var startedRequests = 0; var expectedRequests = 3; if (typeof opts === 'function') { cb = opts; opts = {}; } var logger = opts.logger || noop; if (!opts.version) { opts.version = defaultConfig.version; } if (!opts.baseURL) { opts.baseURL = defaultConfig.baseURL; } opts.progressCb = opts.progressCb || noop; opts.drivers = merge(defaultConfig.drivers, opts.drivers || {}); logger('----------'); logger('selenium-standalone installation starting'); logger('----------'); logger(''); var fsPaths = computeFsPaths({ seleniumVersion: opts.version, drivers: opts.drivers }); var urls = computeDownloadUrls({ seleniumVersion: opts.version, seleniumBaseURL: opts.baseURL, drivers: opts.drivers, }); if (process.platform !== 'win32') { delete fsPaths.ie; delete urls.ie; expectedRequests -= 1; } logInstallSummary(logger, fsPaths, urls); async.series([ createDirs.bind(null, fsPaths), download.bind(null, { urls: urls, fsPaths: fsPaths }), chmodChromeDr.bind(null, fsPaths.chrome.installPath), asyncLogEnd.bind(null, logger), ], cb); function onlyInstallMissingFiles(opts, cb) { async.series([ checksum.bind(null, opts.to), etag.bind(null, opts.from) ], function (error, results) { if (error) { return cb(error); } // File already exists. Prevent download/installation. if (results[0] === results[1]) { logger('---'); logger('File from ' + opts.from + ' has already been downloaded'); expectedRequests -= 1; return cb(); } opts.installer.call(null, { to: opts.to, from: opts.from }, cb); }); } function download(opts, cb) { var installers = [{ installer: installSelenium, from: opts.urls.selenium, to: opts.fsPaths.selenium.downloadPath }, { installer: installChromeDr, from: opts.urls.chrome, to: opts.fsPaths.chrome.downloadPath }]; if (process.platform === 'win32') { installers.push({ installer: installIeDr, from: opts.urls.ie, to: opts.fsPaths.ie.downloadPath }); } var steps = installers.map(function (opts) { return onlyInstallMissingFiles.bind(null, opts); }); async.parallel(steps, cb); } function installSelenium(opts, cb) { getDownloadStream(opts.from, function(err, stream) { if (err) { return cb(err); } stream .pipe(fs.createWriteStream(opts.to)) .once('error', cb.bind(null, new Error('Could not write to ' + opts.to))) .once('finish', cb); }); } function installChromeDr(opts, cb) { installZippedFile(opts.from, opts.to, cb); } function installIeDr(opts, cb) { installZippedFile(opts.from, opts.to, cb); } function installZippedFile(from, to, cb) { var unzip = require('unzip'); getDownloadStream(from, function(err, stream) { if (err) { return cb(err); } var extractPath = path.join(path.dirname(to), path.basename(to, '.zip')); // Store downloaded compressed file stream.pipe(fs.createWriteStream(to)); // Uncompress downloaded file stream.pipe(unzip.Parse()) .once('entry', function(file) { file .pipe(fs.createWriteStream(extractPath)) .once('error', cb.bind(null, new Error('Could not write to ' + to))) .once('finish', cb); }) .once('error', cb.bind(null, new Error('Could not unzip ' + from))); }); } function getDownloadStream(downloadUrl, cb) { var r = request(downloadUrl) .on('response', function(res) { startedRequests += 1; if (res.statusCode !== 200) { return cb(new Error('Could not download ' + downloadUrl)); } res.on('data', function(chunk) { progress += chunk.length; updateProgressPercentage(chunk.length); }); total += parseInt(res.headers['content-length'], 10); cb(null, res); }) .once('error', function(error) { cb(new Error('Could not download ' + downloadUrl + ': ' + error)); }); // initiate request r.end(); } function updateProgressPercentage(chunk) { if (expectedRequests === startedRequests) { opts.progressCb(total, progress, chunk); } } } function asyncLogEnd(logger, cb) { setImmediate(function() { logger(''); logger(''); logger('-----'); logger('selenium-standalone installation finished'); logger('-----'); cb(); }); } function createDirs(paths, cb) { var installDirectories = Object .keys(paths) .map(function(name) { return paths[name].installPath; }); async.eachSeries( installDirectories.map(basePath), mkdirp, cb ); } function basePath(fullPath) { return path.dirname(fullPath); } function chmodChromeDr(where, cb) { fs.chmod(where, '0755', cb); } function logInstallSummary(logger, paths, urls) { ['selenium', 'chrome', 'ie'].forEach(function log(name) { if (!paths[name]) { return; } logger('---'); logger(name + ' install:'); logger('from: ' + urls[name]); logger('to: ' + paths[name].installPath); }); } function checksum (filepath, cb) { if (!fs.existsSync(filepath)) { return cb(); } var hash = crypto.createHash('md5'); var stream = fs.createReadStream(filepath); stream.on('data', function (data) { hash.update(data, 'utf8'); }).on('end', function () { cb(null, hash.digest('hex')); }).once('error', cb); } function unquote (str, quoteChar) { quoteChar = quoteChar || '"'; if (str[0] === quoteChar && str[str.length - 1] === quoteChar) { return str.slice(1, str.length - 1); } return str; } function etag (url, cb) { request.head(url).on('response', function (res) { if (res.statusCode !== 200) { return cb(new Error('Could not request headers from ' + url + ': ', res.statusCodestatusCode)); } cb(null, unquote(res.headers.etag)); }).once('error', function (err) { cb(new Error('Could not request headers from ' + url + ': ' + err)); }); }