webdrvr
Version:
NPM wrapper for Selenium Webdriver including Chromedriver / IEDriver / IOSDriver / Ghostdriver
266 lines (233 loc) • 8.11 kB
JavaScript
/* Adapted from https://github.com/Obvious/phantomjs/blob/master/install.js */
var fs = require('fs');
var httpModule = {
http: require('follow-redirects').http,
https: require('follow-redirects').https
};
var path = require('path');
var url = require('url');
var util = require('util');
var AdmZip = require('adm-zip');
var kew = require('kew');
var mkdirp = require('mkdirp');
var npmconf = require('npmconf');
var webdriver = require('./lib/index.js');
npmconf.load(function(err, conf) {
if (err) {
console.log('Error loading npm config');
console.error(err);
process.exit(1);
return;
}
if (!webdriver.platform) {
console.log('Unexpected platform or architecture:', process.platform, process.arch);
process.exit(1);
}
var tmpPath = findSuitableTempDirectory(conf.get('tmp'));
var promise = kew.resolve(true);
if (!fs.existsSync(path.join(tmpPath, path.basename(webdriver.selenium.downloadUrl)))) {
promise = promise.then(function() {
return downloadFile(
conf.get('proxy'),
webdriver.selenium.downloadUrl,
path.join(tmpPath, path.basename(webdriver.selenium.downloadUrl))
);
});
}
promise = promise.then(function() {
return copyFile(
path.join(tmpPath, path.basename(webdriver.selenium.downloadUrl)),
webdriver.selenium.path
);
});
if (!fs.existsSync(path.join(tmpPath, path.basename(webdriver.chromedriver.downloadUrl)))) {
promise = promise.then(function() {
return downloadFile(
conf.get('proxy'),
webdriver.chromedriver.downloadUrl,
path.join(tmpPath, path.basename(webdriver.chromedriver.downloadUrl))
);
});
}
promise = promise.then(function() {
return extractDownload(
path.join(tmpPath, path.basename(webdriver.chromedriver.downloadUrl)),
path.dirname(webdriver.chromedriver.path)
);
})
.then(function() {
return fixFilePermissions(webdriver.chromedriver.path);
});
if (webdriver.iedriver.downloadUrl) {
if (!fs.existsSync(path.join(tmpPath, path.basename(webdriver.iedriver.downloadUrl)))) {
promise = promise.then(function() {
return downloadFile(
conf.get('proxy'),
webdriver.iedriver.downloadUrl,
path.join(tmpPath, path.basename(webdriver.iedriver.downloadUrl))
);
});
}
promise.then(function() {
return extractDownload(
path.join(tmpPath, path.basename(webdriver.iedriver.downloadUrl)),
path.dirname(webdriver.iedriver.path)
);
})
.then(function() {
return fixFilePermissions(webdriver.iedriver.path);
});
}
if (webdriver.iosdriver.downloadUrl) {
if (!fs.existsSync(path.join(tmpPath, path.basename(webdriver.iosdriver.downloadUrl)))) {
promise = promise.then(function() {
return downloadFile(
conf.get('proxy'),
webdriver.iosdriver.downloadUrl,
path.join(tmpPath, path.basename(webdriver.iosdriver.downloadUrl))
);
});
}
promise = promise.then(function() {
return copyFile(
path.join(tmpPath, path.basename(webdriver.iosdriver.downloadUrl)),
webdriver.iosdriver.path
);
});
}
promise.then(function() {
console.log([
'Done. Selenium, Chromedriver',
webdriver.iedriver.downloadUrl ? ', IEDriver' : '',
webdriver.iosdriver.downloadUrl ? ', IOSDriver' : '',
' available at ',
path.dirname(webdriver.selenium.path)
].join(''));
})
.fail(function(err) {
console.error('Installation of Selenium, Chromedriver, IEDriver or IOSDriver failed', err.stack);
process.exit(1);
})
});
function findSuitableTempDirectory(npmTmpDir) {
var now = Date.now();
var candidateTmpDirs = [
process.env.TMPDIR || npmTmpDir,
'/tmp',
path.join(process.cwd(), 'tmp')
];
for (var i = 0; i < candidateTmpDirs.length; i++) {
var candidatePath = path.join(candidateTmpDirs[i], 'webdrvr');
try {
mkdirp.sync(candidatePath, '0777');
var testFile = path.join(candidatePath, now + '.tmp');
fs.writeFileSync(testFile, 'test');
fs.unlinkSync(testFile);
return candidatePath;
} catch (e) {
console.log(candidatePath, 'is not writable:', e.message);
}
}
console.error('Cannot find a writable tmp directory');
process.exit(1);
}
function downloadFile(proxy, downloadUrl, downloadPath) {
console.log('Downloading', downloadUrl);
console.log('Saving to', downloadPath);
return requestBinary(getRequestOptions(proxy, downloadUrl), downloadPath);
}
function copyFile(fromFilePath, toFilePath) {
var deferred = kew.defer();
var toFileFolder = path.dirname(toFilePath);
// ensure that the destination folder exists
try {
fs.mkdirSync(toFileFolder);
} catch(error) {}
var writeStream = fs.createWriteStream(toFilePath);
writeStream.on('open', function() {
fs.createReadStream(fromFilePath).pipe(writeStream);
}).on('finish', function() {
deferred.resolve(true);
}).on('error', function(error) {
deferred.reject(new Error('Error copying file: ' + error));
});
return deferred;
}
function getRequestOptions(proxyUrl, downloadUrl) {
if (proxyUrl) {
var options = url.parse(proxyUrl);
options.path = downloadUrl;
options.headers = { Host: url.parse(downloadUrl).host };
// If going through proxy, spoof the User-Agent, since may commerical proxies block blank or unknown agents in headers
options.headers['User-Agent'] = 'curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5'
// Turn basic authorization into proxy-authorization.
if (options.auth) {
options.headers['Proxy-Authorization'] = 'Basic ' + new Buffer(options.auth).toString('base64');
delete options.auth;
}
return options;
} else {
return url.parse(downloadUrl);
}
}
function requestBinary(requestOptions, filePath) {
var deferred = kew.defer();
var count = 0;
var notifiedCount = 0;
var filePath = filePath;
var tmpFilePath = filePath + '.tmp';
var outFile = fs.openSync(tmpFilePath, 'w');
var protocol = requestOptions.protocol.slice(0, requestOptions.protocol.length - 1);
var client = httpModule[protocol].get(requestOptions, function(response) {
var status = response.statusCode;
console.log('Receiving...');
if (status === 200) {
response.addListener('data', function(data) {
fs.writeSync(outFile, data, 0, data.length, null);
count += data.length;
if ((count - notifiedCount) > 800000) {
console.log('Received ' + Math.floor(count / 1024) + 'K...');
notifiedCount = count;
}
})
response.addListener('end', function() {
console.log('Received ' + Math.floor(count / 1024) + 'K total.');
fs.closeSync(outFile);
// just write the final file when download was completed
copyFile(tmpFilePath, filePath).then(function() {
deferred.resolve(true);
}).fail(function(error) {
deferred.reject(error);
});
})
} else {
client.abort();
deferred.reject(new Error('Error with http request: ' + util.inspect(response.headers)));
}
});
return deferred.promise;
}
function extractDownload(filePath, extractToPath) {
var deferred = kew.defer();
var options = {cwd: extractToPath};
console.log('Extracting zip contents of ' + filePath);
try {
var zip = new AdmZip(filePath);
zip.extractAllTo(extractToPath, true);
deferred.resolve(true);
} catch (err) {
deferred.reject(new Error('Error extracting archive ' + err.stack));
}
return deferred.promise;
}
function fixFilePermissions(filePath) {
// Check that the binary is user-executable and fix it if it isn't (problems with unzip library)
if (process.platform != 'win32') {
var stat = fs.statSync(filePath);
// 64 == 0100 (no octal literal in strict mode)
if (!(stat.mode & 64)) {
console.log('Fixing file permissions of ' + filePath);
fs.chmodSync(filePath, '755');
}
}
}