diff --git a/.jscsrc b/.jscsrc index 259c56b..8a4dfe5 100644 --- a/.jscsrc +++ b/.jscsrc @@ -51,7 +51,6 @@ "mark": "'", "escape": true }, - "validateIndentation": "\t", "disallowMixedSpacesAndTabs": true, "disallowTrailingWhitespace": true, "disallowKeywordsOnNewLine": [ "else", "catch" ], diff --git a/CHANGELOG.md b/CHANGELOG.md index 99f0b73..16b5753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/index.js b/index.js index f81d529..ccf92df 100644 --- a/index.js +++ b/index.js @@ -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' ), @@ -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 ) { diff --git a/lib/darwin/canary.js b/lib/darwin/chrome-canary.js similarity index 100% rename from lib/darwin/canary.js rename to lib/darwin/chrome-canary.js diff --git a/lib/darwin/index.js b/lib/darwin/index.js index c92c4ca..8762623 100644 --- a/lib/darwin/index.js +++ b/lib/darwin/index.js @@ -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' ); diff --git a/lib/detect.js b/lib/detect.js index 6fdf258..3f690a4 100644 --- a/lib/detect.js +++ b/lib/detect.js @@ -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' }; /** @@ -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 ); @@ -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) { + 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 ) { @@ -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 ); } ); } @@ -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); + }); };