Skip to content

Commit

Permalink
Merge pull request #1 from cdagli/master
Browse files Browse the repository at this point in the history
Formatter fixes applied & TravisCI integration & tests added
  • Loading branch information
cdagli authored May 2, 2018
2 parents 86252ee + eb40cc8 commit 18c39de
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 67 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language: node_js
node_js:
- "node"
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[![Build Status](https://travis-ci.org/cdagli/browser-driver-installer.svg?branch=master)](https://travis-ci.org/cdagli/browser-driver-installer)

# browser-driver-installer
Installs Chrome and Gecko drivers that match with the specified browser versions
14 changes: 7 additions & 7 deletions driverVersions.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"driverNPMPackageVersion": "2.29.0",
"driverVersion": "2.29"
},
"59": {
"59": {
"driverNPMPackageVersion": "2.30.0",
"driverVersion": "2.30"
},
Expand Down Expand Up @@ -58,25 +58,25 @@
}
},
"geckoDriverVersions": {
"55":{
"55": {
"driverNPMPackageVersion": "1.10.0",
"driverVersion": "0.19.1"
},
"56":{
"56": {
"driverNPMPackageVersion": "1.10.0",
"driverVersion": "0.19.1"
},
"57":{
"57": {
"driverNPMPackageVersion": "1.10.0",
"driverVersion": "0.19.1"
},
"58":{
"58": {
"driverNPMPackageVersion": "1.10.0",
"driverVersion": "0.19.1"
},
"59":{
"59": {
"driverNPMPackageVersion": "1.11.0",
"driverVersion": "0.20.1"
}
}
}
}
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#!/usr/bin/env node

const program = require('commander');
const driverInstaller = require('./installer').driverInstaller;

program
.option('--chrome-version [chromeVersion]', 'Chrome browser major version string e.g. 65')
.option('--chrome-driver-target-path [chromeDriverTargetPath]', 'Path to install Chrome driver executable')
.option('--firefox-version [firefoxVersion]', 'Firefox browser major version string e.g. 57')
.option('--firefox-driver-target-path [firefoxDriverTargetPath]', 'Path to install Firefox driver(geckoDriver) executable')
.option('--firefox-driver-target-path [firefoxDriverTargetPath]',
'Path to install Firefox driver(geckoDriver) executable')
.parse(process.argv);

driverInstaller(program.chromeVersion, program.chromeDriverTargetPath, program.firefoxVersion, program.firefoxDriverTargetPath);
121 changes: 79 additions & 42 deletions installer.js
Original file line number Diff line number Diff line change
@@ -1,110 +1,147 @@
'use strict';
'use strict';
/* eslint-disable no-console */

var runNpmChildProcess = require('./runNpmChildProcess');
var path = require('path');
var execSync = require('child_process').execSync;
var shell = require('shelljs');
const runNpmChildProcess = require('./runNpmChildProcess');
const path = require('path');
const execSync = require('child_process').execSync;
const shell = require('shelljs');

const TEMP_DIR = 'temp';
const TEMP_DIR = 'temp';
const CHROME_DRIVER_NAME = 'chromedriver';
const CHROME_DRIVER_BIN_PATH = path.join('node_modules', 'chromedriver', 'lib', 'chromedriver', CHROME_DRIVER_NAME);
const CHROME_DRIVER_VERSION_REGEX = new RegExp(/\w+ ([0-9]+.[0-9]+).+/);
const GECKO_DRIVER_NAME = 'geckodriver';
const GECKO_DRIVER_NAME = 'geckodriver';
const GECKO_DRIVER_BIN_PATH = path.join('node_modules', 'geckodriver', GECKO_DRIVER_NAME);
const GECKO_DRIVER_VERSION_REGEX = new RegExp(/\w+\s(\d+.\d+.\d+)/);
const BROWSER_MAJOR_VERSION_REGEX = new RegExp(/^(\d+)/);

function installDriverWithVersion(driverName, driverBinPath, installPath, versionObject)
{
if(checkDirectoryAndVersion(driverName, installPath, versionObject.driverVersion))
if (checkDirectoryAndVersion(driverName, installPath, versionObject.driverVersion))
{
return;
return false;
}

shell.mkdir('-p', TEMP_DIR);

runNpmChildProcess(['install', `${driverName}@${versionObject.driverNPMPackageVersion}`, '--prefix', TEMP_DIR]).then(function ()
return runNpmChildProcess(['install', `${driverName}@${versionObject.driverNPMPackageVersion}`, '--prefix',
TEMP_DIR
]).then(
function ()
{
shell.mkdir('-p', installPath);
console.log('package dependencies have been installed');
shell.cp('-n',path.join(TEMP_DIR, driverBinPath), installPath);
shell.cp('-n', path.join(TEMP_DIR, driverBinPath), installPath);
shell.rm('-rf', TEMP_DIR);
}, function (e)
console.log('package dependencies have been installed');
return true;
},
function (e)
{
console.log('package dependencies installation failed with error, details: ' + e.toString());
throw new Error('package dependencies installation failed with error, details: ' + e.toString());
});
}

function checkDirectoryAndVersion(driverName, installPath, driverExpectedVersion)
{
if (!shell.test('-e', installPath)) {
return false;
if (!shell.test('-e', installPath))
{
return false;
}
console.log(`Directory '${installPath}' does exist.`);
console.log(`Checking if the directory contains a ${driverName}...`);

if(!shell.test('-e', path.join(installPath, driverName))){
console.log(`Could not find the ${driverName} in the directory '${installPath}'. Attempting to install it...`);
return false;
if (!shell.test('-e', path.join(installPath, driverName)))
{
console.log(`Could not find the ${driverName} in the directory '${installPath}'. Attempting to install it...`);
return false;
}
console.log(`${driverName} found.`);
const driverMajorVersion = driverVersionString(driverName, installPath);
if(driverMajorVersion !== driverExpectedVersion)

console.log(`${driverName} found.`);
const driverMajorVersion = driverVersionString(driverName, installPath);
if (driverMajorVersion !== driverExpectedVersion)
{
console.log(`${driverName} expected version (${driverExpectedVersion}) does not match with the installed version (${driverMajorVersion}).`);
console.log(
`${driverName} expected version (${driverExpectedVersion}) does not match with the installed version (${driverMajorVersion}).`
);
console.log('Removing the old version...');
shell.rm(path.join(installPath, driverName));
return false;
return false;
}

console.log(`${driverName} version ${driverExpectedVersion} has already been installed!`);
return true;
return true;
}

function driverVersionString(driverName, installPath)
{
let versionOutput = null;
if(driverName === CHROME_DRIVER_NAME){
let versionOutput = null;
if (driverName === CHROME_DRIVER_NAME)
{
versionOutput = execSync(path.join(installPath, driverName) + ' -v').toString();
return versionOutput.match(CHROME_DRIVER_VERSION_REGEX)[1];
} else if (driverName === GECKO_DRIVER_NAME)
}
else if (driverName === GECKO_DRIVER_NAME)
{
versionOutput = execSync(path.join(installPath, driverName) + ' -V').toString();
return versionOutput.match(GECKO_DRIVER_VERSION_REGEX)[1];
} else {
}
else
{
throw new Error(`No driver exists with the name ${driverName}.`);
}
}

function driverInstaller(detectedChromeVersion, chromeDriverTargetPath, detectedFirefoxVersion, geckoDriverTargetPath){
function driverInstaller(detectedChromeVersion, chromeDriverTargetPath, detectedFirefoxVersion, geckoDriverTargetPath)
{
const browserVersionsObject = JSON.parse(shell.cat(path.resolve(__dirname, 'driverVersions.json')));
// ChromeDriver NPM package versions are defined according to https://github.com/giggio/node-chromedriver/releases
const chromeDriverVersions = browserVersionsObject.chromeDriverVersions;
const chromeDriverVersions = browserVersionsObject.chromeDriverVersions;

// GeckoDriver NPM package versions are defined according to https://github.com/mozilla/geckodriver/releases
const geckoDriverVersions = browserVersionsObject.geckoDriverVersions;

detectedChromeVersion = majorBrowserVersion(detectedChromeVersion);
detectedFirefoxVersion = majorBrowserVersion(detectedFirefoxVersion);

if(detectedChromeVersion && !chromeDriverVersions[detectedChromeVersion]){
console.log(`Failed to locate a version of ChromeDriver that matches the installed version of Chrome (${detectedChromeVersion}). Valid Chrome versions are: ${Object.keys(chromeDriverVersions).join(', ')}`)
} else if(detectedChromeVersion){
installDriverWithVersion(CHROME_DRIVER_NAME, CHROME_DRIVER_BIN_PATH, chromeDriverTargetPath, chromeDriverVersions[detectedChromeVersion]);
if (detectedChromeVersion && !chromeDriverVersions[detectedChromeVersion])
{
throw new Error(
`Failed to locate a version of ChromeDriver that matches the installed version of Chrome (${detectedChromeVersion}). Valid Chrome versions are: ${Object.keys(chromeDriverVersions).join(', ')}`
);
}
else if (detectedChromeVersion && typeof (chromeDriverTargetPath) === 'string')
{
return installDriverWithVersion(CHROME_DRIVER_NAME, CHROME_DRIVER_BIN_PATH, chromeDriverTargetPath,
chromeDriverVersions[detectedChromeVersion]);
}
else
{
console.log('No Chrome version or target path is provided. Skipping...');
}

if(detectedFirefoxVersion && !geckoDriverVersions[detectedFirefoxVersion]){
console.log(`Failed to locate a version of GeckoDriver that matches the installed version of Firefox (${detectedFirefoxVersion}). Valid Firefox versions are: ${Object.keys(geckoDriverVersions).join(', ')}`)
} else if(detectedFirefoxVersion){
installDriverWithVersion(GECKO_DRIVER_NAME, GECKO_DRIVER_BIN_PATH, geckoDriverTargetPath, geckoDriverVersions[detectedFirefoxVersion])
if (detectedFirefoxVersion && !geckoDriverVersions[detectedFirefoxVersion])
{
throw new Error(
`Failed to locate a version of GeckoDriver that matches the installed version of Firefox (${detectedFirefoxVersion}). Valid Firefox versions are: ${Object.keys(geckoDriverVersions).join(', ')}`
);
}
else if (detectedFirefoxVersion && (typeof geckoDriverTargetPath) === 'string')
{
return installDriverWithVersion(GECKO_DRIVER_NAME, GECKO_DRIVER_BIN_PATH, geckoDriverTargetPath,
geckoDriverVersions[
detectedFirefoxVersion]);
}
else
{
console.log('No Firefox version or target path is provided. Skipping...');
}

return false;
}

function majorBrowserVersion(browserVersionString)
{
return browserVersionString && browserVersionString.match(BROWSER_MAJOR_VERSION_REGEX)[0];
return (typeof browserVersionString) === 'string' && browserVersionString.match(BROWSER_MAJOR_VERSION_REGEX)[0];
}

module.exports.driverInstaller = driverInstaller;
76 changes: 76 additions & 0 deletions installer.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const chai = require('chai');
const installer = require('./installer');
const expect = require('chai').expect;
const sinon = require('sinon');
const chaiSinon = require('chai-sinon');
const shell = require('shelljs');
const path = require('path');

chai.use(chaiSinon);

describe('browserDriverInstaller', function ()
{
const DEFAULT_TIMEOUT_IN_MILLIS = 60000;
const DRIVER_OUTPUT_PATH = './output';

this.timeout(DEFAULT_TIMEOUT_IN_MILLIS);

beforeEach(function ()
{
sinon.spy(console, 'log');
});

afterEach(function ()
{
console.log.restore();
cleanTheOutput();
});

function cleanTheOutput()
{
shell.rm('-rf', DRIVER_OUTPUT_PATH);
}

it('should not attempt to install anything if one of the path, version or both parameters are not provided',
function ()
{
expect(installer.driverInstaller()).to.be.false;
expect(console.log).to.have.been.calledWith(
'No Chrome version or target path is provided. Skipping...');
expect(console.log).to.have.been.calledWith(
'No Firefox version or target path is provided. Skipping...');
});

it('should throw an error if the provided version does not included in the JSON file', function ()
{
const wrongVersionNumber = '1';
expect(function () { installer.driverInstaller(wrongVersionNumber, '/some/target/path'); }).to.throw(
/Failed to locate a version of ChromeDriver that matches the installed version of Chrome \(1\). Valid Chrome versions are:*/
);
});

it('should install the chromedriver to specified path if the version is included in the JSON file',
function ()
{
return installer.driverInstaller('54', DRIVER_OUTPUT_PATH).then(function ()
{
expect(shell.test('-e', path.resolve(DRIVER_OUTPUT_PATH, 'chromedriver'))).to.be.true;
});
});

it('should install the geckodriver to specified path if the version is included in the JSON file', function ()
{
return installer.driverInstaller(null, null, '55', DRIVER_OUTPUT_PATH).then(function ()
{
expect(shell.test('-e', path.resolve(DRIVER_OUTPUT_PATH, 'geckodriver'))).to.be.true;
});
});

it('should not install again if the wanted version is already installed', function ()
{
return installer.driverInstaller('54', DRIVER_OUTPUT_PATH).then(function ()
{
expect(installer.driverInstaller('54', DRIVER_OUTPUT_PATH)).to.be.false;
});
});
});
37 changes: 21 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
{
"bin": {
"browserDriverInstaller": "./index.js"
},
"name": "browser-driver-installer",
"version": "0.0.1",
"description": "Installs Chrome and Gecko drivers that matches with the specified browser versions",
"main": "index.js",
"dependencies": {
"commander": "2.15.1",
"shelljs": "0.8.1"
},
"scripts": {
"postinstall": "node ./index.js --chrome-version $CHROME_VERSION --chrome-driver-target-path $CHROMEDRIVER_PATH --firefox-version $FIREFOX_VERSION --firefox-driver-target-path $GECKODRIVER_PATH"
},
"author": "Unscrambl",
"license": "Apache-2.0"
"author": "Unscrambl",
"bin": {
"browserDriverInstaller": "./index.js"
},
"dependencies": {
"chai": "4.1.2",
"chai-sinon": "2.8.1",
"commander": "2.15.1",
"mocha": "5.1.1",
"shelljs": "0.8.1",
"sinon": "5.0.2"
},
"description": "Installs Chrome and Gecko drivers that matches with the specified browser versions",
"license": "Apache-2.0",
"main": "index.js",
"name": "browser-driver-installer",
"scripts": {
"postinstall": "node ./index.js --chrome-version $CHROME_VERSION --chrome-driver-target-path $CHROMEDRIVER_PATH --firefox-version $FIREFOX_VERSION --firefox-driver-target-path $GECKODRIVER_PATH",
"test": "node_modules/mocha/bin/mocha *.spec.js"
},
"version": "0.0.1"
}
3 changes: 2 additions & 1 deletion runNpmChildProcess.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ module.exports = runNpmChildProcess;
function runNpmChildProcess(args, cachePath)
{
args.push('--cache-min=600000', '--no-optional', '--loglevel=error');
if(cachePath){
if (cachePath)
{
args.push('--cache=' + cachePath);
}
return retryNpmProcessIfItFails(args, MAX_RETRY_COUNT);
Expand Down

0 comments on commit 18c39de

Please sign in to comment.