Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser map refactor #15

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .jscsrc
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"mark": "'",
"escape": true
},
"validateIndentation": "\t",
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"disallowKeywordsOnNewLine": [ "else", "catch" ],
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
## Upcoming Release

#### Breaking changes
- None yet
- Type `phantom` is now `phantomjs`, which is a little more consistent
- The property "name" is more descriptive for variant channels, e.g. "chrome-canary"

#### User features
- None yet
Expand Down
8 changes: 4 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var path = require( 'path' ),
_ = require( 'lodash' ),
pick = require('lodash').pick,
configModule = require( './lib/config' ),
detect = require( './lib/detect' ),
run = require( './lib/run' ),
Expand Down Expand Up @@ -75,14 +75,14 @@ function getLauncher( configFile, callback ) {
getLauncher.detect = function( callback ) {
detect( function( browsers ) {
callback( browsers.map( function( browser ) {
return _.pick( browser, [ 'name', 'version', 'type', 'command' ] );
} ) );
return pick( browser, [ 'name', 'version', 'type', 'command' ])
}));
} );
};

/**
* Update the browsers cache and create new profiles if necessary
* @param {String} configDir Path to the configuration file
* @param {String} configFile Path to the configuration file
* @param {Function} callback Callback function
*/
getLauncher.update = function( configFile, callback ) {
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion lib/darwin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ exports.safari = require( './safari' );
exports.firefox = require( './firefox' );
exports.chrome = exports[ 'google-chrome' ] = require( './chrome' );
exports.chromium = require( './chromium' );
exports.canary = exports[ 'chrome-canary' ] = exports[ 'google-chrome-canary' ] = require( './canary' );
exports['chrome-canary'] = require( './chrome-canary' );
exports.opera = require( './opera' );
exports.phantomjs = require( './phantomjs' );
274 changes: 151 additions & 123 deletions lib/detect.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,47 @@
var spawn = require( 'child_process' ).spawn,
winDetect = require( 'win-detect-browsers' ),
darwin = require( './darwin' ),
extend = require( 'lodash' ).extend,
browsers = {
'google-chrome': {
name: 'chrome',
re: /Google Chrome (\S+)/,
type: 'chrome',
profile: true
},
'google-chrome-canary': {
name: 'canary',
re: /Google Chrome (\S+)/,
type: 'chrome',
profile: true
configProperties = ['profile', 'headless'],
browsers = { // List of commands and version regex for Linux and Mac
chrome: {
regex: /Google Chrome (\S+)/,
profile: true,
variants: {
'chrome': [ 'google-chrome', 'google-chrome-stable' ],
'chrome-beta': 'google-chrome-beta',
'chrome-canary': 'google-chrome-canary'
}
},
'chromium': {
name: 'chromium',
re: /Chromium (\S+)/,
type: 'chrome',
profile: true
chromium: {
regex: /Chromium (\S+)/,
profile: true,
variants: {
'chromium': [ 'chromium', 'chromium-browser' ]
}
},
'chromium-browser': {
name: 'chromium',
re: /Chromium (\S+)/,
type: 'chrome',
profile: true
firefox: {
regex: /Mozilla Firefox (\S+)/,
profile: true,
variants: {
'firefox': 'firefox',
'firefox-developer': 'firefox-developer'
}
},
'firefox': {
name: 'firefox',
re: /Mozilla Firefox (\S+)/,
type: 'firefox',
profile: true
phantomjs: {
regex: /(\S+)/,
profile: false,
headless: true
},
'phantomjs': {
name: 'phantomjs',
re: /(\S+)/,
type: 'phantom',
headless: true,
safari: {
profile: false
},
'safari': {
name: 'safari',
type: 'safari',
ie: {
profile: false
},
'ie': {
name: 'ie',
type: 'ie',
profile: false
},
'opera': {
name: 'opera',
re: /Opera (\S+)/,
type: 'opera',
image: 'opera.exe',
opera: {
regex: /Opera (\S+)/,
profile: true
}
},
winDetectMap = {
chrome: 'google-chrome',
chromium: 'chromium-browser'
};

/**
Expand All @@ -71,13 +52,15 @@ var spawn = require( 'child_process' ).spawn,
function detectWindows( callback ) {
winDetect( function( found ) {
var available = found.map( function( browser ) {
var br = browsers[ winDetectMap[ browser.name ] || browser.name ];

return extend( {}, {
var configBrowser = browsers[browser.name];
return {
name: browser.name,
type: browser.name,
command: browser.path,
version: browser.version
}, br || {} );
version: browser.version,
profile: configBrowser.profile,
headless: configBrowser.headless
};
} );

callback( available );
Expand All @@ -90,45 +73,41 @@ function detectWindows( callback ) {
* @param {String} name Name of a browser
* @param {Function} callback Callback function
*/
function checkDarwin( name, callback ) {
if ( darwin[ name ] ) {
if ( darwin[ name ].all ) {
darwin[ name ].all( function( err, available ) {
if ( err ) {
callback( 'failed to get version for ' + name );
} else {
callback( err, available );
}
} );
} else {
darwin[ name ].version( function( err, version ) {
if ( version ) {
darwin[ name ].path( function( err, p ) {
if ( err ) {
return callback( 'failed to get path for ' + name );
}

callback( null, version, p );
} );
} else {
callback( 'failed to get version for ' + name );
}
} );
}
function checkDarwin(name, callback) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just removed the surrounding if statement and moved the manual call of checkOthers to the main detection function

if (darwin[name].all) {
darwin[name].all(function (err, available) {
if (err) {
callback('failed to get version for ' + name);
} else {
callback(err, available);
}
});
} else {
checkOthers( name, callback );
darwin[name].version(function (err, version) {
if (version) {
darwin[name].path(function (err, p) {
if (err) {
return callback('failed to get path for ' + name);
}

callback(null, version, p);
});
} else {
callback('failed to get version for ' + name);
}
});
}
}

/**
* Check if the given browser is available (on Unix systems).
* Pass its version to the callback function if found.
* @param {String} name Name of a browser
* @param {String} command Name of a browser
* @param {RegExp} regex Version matching regex
* @param {Function} callback Callback function
*/
function checkOthers( name, callback ) {
var process = spawn( name, [ '--version' ] ),
re = browsers[ name ].re,
function checkOthers( command, regex, callback ) {
var process = spawn( command, [ '--version' ] ),
data = '';

process.stdout.on( 'data', function( buf ) {
Expand All @@ -149,13 +128,9 @@ function checkOthers( name, callback ) {
return callback( 'not installed' );
}

var m = re.exec( data );

if ( m ) {
callback( null, m[ 1 ] );
} else {
callback( null, data.trim() );
}
var match = regex.exec( data );
var version = match ? match[ 1 ] : data.trim();
callback( null, version );
} );
}

Expand All @@ -165,49 +140,102 @@ function checkOthers( name, callback ) {
* @param {Function} callback Callback function
*/
module.exports = function detect( callback ) {
var available = [],
names,
check;

if ( process.platform === 'win32' ) {
if (process.platform === 'win32' ) {
return detectWindows( callback );
} else if ( process.platform === 'darwin' ) {
check = checkDarwin;
} else {
check = checkOthers;
}

names = Object.keys( browsers );
var isOsx = process.platform === 'darwin';
var flatBrowsers = [];

function useDarwinCheck(name) {
return isOsx && darwin[ name ];
}

function next() {
var name = names.shift();
Object.keys(browsers).forEach(function(type) {
var multiBrowser = browsers[type];
var regex = multiBrowser.regex;

function pushCheck(func, name, type, command) {
var properties = {};
properties.name = name;
properties.type = type;
properties.command = command;
configProperties.forEach(function(property) {
properties[property] = multiBrowser[property];
});
flatBrowsers.push({func, properties});
}

if ( !name ) {
return callback( available );
if (typeof browsers[type].variants === 'undefined') {
var check = useDarwinCheck(type) ?
checkDarwin.bind(undefined, type) :
checkOthers.bind(undefined, type, regex);
pushCheck(check, type, type, type);
return;
}

var br = browsers[ name ];
Object.keys(multiBrowser.variants).forEach(function(name) {
var variant = multiBrowser.variants[name];

if ( useDarwinCheck( name ) ) {
pushCheck(checkDarwin.bind(undefined, name), type, name);
return;
}

// if variant is a single command
if (typeof variant === 'string') {
variant = [variant];
}

check( name, function( err, v, p ) {
if ( err === null ) {
if ( Array.isArray( v ) ) {
v.forEach( function( item ) {
available.push( extend( {}, br, {
command: item.path,
version: item.version
} ) );
// variant must be a list of commands
variant.forEach(function(command) {
pushCheck(checkOthers.bind(undefined, command, regex), type, name, command);
});
});
});

var checksComplete = 0;
var available = [];

flatBrowsers.forEach(function(flatBrowser) {
var properties = flatBrowser.properties;
function cb( err, version, path ) {
if ( !err ) {
if ( Array.isArray( version ) ) {
// If `version` is an array, then it's actually a "darwin check" returning multiple browswers.
// See `darwin/firefox.js`
version.forEach( function( item ) {
var next = {
name: properties.name,
type: properties.type,
version: item.version,
command: item.path || properties.command
};
configProperties.forEach(function(property) {
next[property] = properties[property];
});
available.push(next);
} );
} else {
available.push( extend( {}, br, {
command: p || name,
version: v
} ) );
var next = {
name: properties.name,
type: properties.type,
version: version,
command: path || properties.command
};
configProperties.forEach(function(property) {
next[property] = properties[property];
});
available.push(next);
}
}

next();
} );
}
checksComplete++;
if (checksComplete === flatBrowsers.length) {
callback(available);
}
}

next();
flatBrowser.func(cb);
});
};