diff --git a/lib/cli.js b/lib/cli.js index 54a88d2bc..83f7c11f4 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,6 +1,6 @@ /** * The command line interface for interacting with the Protractor runner. - * It takes care of parsing the config file and command line options. + * It takes care of parsing command line options. * * Values from command line options override values from the config. */ @@ -67,13 +67,16 @@ if (argv.version) { process.exit(0); } -// WebDriver capabilities properties require dot notation. +// WebDriver capabilities properties require dot notation, but optimist parses +// that into an object. Re-flatten it. var flattenObject = function(obj) { var prefix = arguments[1] || ''; var out = arguments[2] || {}; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { - typeof obj[prop] === 'object' ? flattenObject(obj[prop], prefix + prop + '.', out) : out[prefix + prop] = obj[prop]; + typeof obj[prop] === 'object' ? + flattenObject(obj[prop], prefix + prop + '.', out) : + out[prefix + prop] = obj[prop]; } } return out; @@ -83,5 +86,27 @@ if (argv.capabilities) { argv.capabilities = flattenObject(argv.capabilities); } +/** + * Helper to resolve comma separated lists of file pattern strings relative to + * the cwd. + * + * @private + * @param {Array} list + */ +var processFilePatterns_ = function(list) { + var patterns = list.split(','); + patterns.forEach(function(spec, index, arr) { + arr[index] = path.resolve(process.cwd(), spec); + }); + return patterns; +}; + +if (argv.specs) { + argv.specs = processFilePatterns_(argv.specs); +} +if (argv.excludes) { + argv.excludes = processFilePatterns_(argv.excludes); +} + // Run the launcher require('./launcher').init(argv); diff --git a/lib/configParser.js b/lib/configParser.js index a638d675e..fddc7d00b 100644 --- a/lib/configParser.js +++ b/lib/configParser.js @@ -1,38 +1,38 @@ var path = require('path'), glob = require('glob'), - config_ = { - configDir: './', - jasmineNodeOpts: {} - }, - //this allows for ease of maintaining public apis of the config while still - // allowing for easy variable renames or future config api changes while - // supported backwards compatibility - configMap_ = { + util = require('util'), + protractor = require('./protractor.js'); + - //structure is internal name -> supported config apis - 'specs': ['specs'], - 'exclude': ['exclude'], - 'capabilities': ['capabilities'], - 'seleniumHost': ['seleniumAddress'], - 'rootElement': ['rootElement'], - 'baseUrl': ['baseUrl'], - 'timeout': ['allScriptsTimeout'], - 'browserParams': ['params'], - 'framework': ['framework'], - 'jasmineOpts': ['jasmineNodeOpts'], - 'mochaOpts': ['mochaOpts'], - 'seleniumLocal.jar': ['seleniumServerJar'], - 'seleniumLocal.args': ['seleniumArgs'], - 'seleniumLocal.port': ['seleniumPort'], - 'sauceAccount.user': ['sauceUser'], - 'sauceAccount.key': ['sauceKey'], - 'chromeDriver': ['chromeDriver'], - 'chromeOnly': ['chromeOnly'], - 'configDir': ['configDir'], - 'cucumberOpts.require': ['cucumberOpts.require'], - 'cucumberOpts.format': ['cucumberOpts.format'], - 'cucumberOpts.tags': ['cucumberOpts.tags'] - }; +module.exports = ConfigParser = function() { + // Default configuration. + this.config_= { + specs: [], + capabilities: { + browserName: 'chrome' + }, + multiCapabilities: [], + rootElement: 'body', + allScriptsTimeout: 11000, + params: {}, + framework: 'jasmine', + jasmineNodeOpts: { + isVerbose: false, + showColors: true, + includeStackTrace: true, + stackFilter: protractor.filterStackTrace, + defaultTimeoutInterval: (30 * 1000) + }, + seleniumArgs: [], + cucumberOpts: {}, + mochaOpts: { + ui: 'bdd', + reporter: 'list' + }, + chromeDriver: null, + configDir: './' + }; +}; /** * Merge config objects together. @@ -57,145 +57,76 @@ var merge_ = function(into, from) { /** * Resolve a list of file patterns into a list of individual file paths. * - * @param {Array/String} patterns - * @param {Boolean} opt_omitWarnings whether to omit did not match warnings + * @param {Array. | string} patterns + * @param {=boolean} opt_omitWarnings Whether to omit did not match warnings + * @param {=string} opt_relativeTo Path to resolve patterns against * * @return {Array} The resolved file paths. */ -var resolveFilePatterns = function(patterns, opt_omitWarnings) { +ConfigParser.resolveFilePatterns = + function(patterns, opt_omitWarnings, opt_relativeTo) { var resolvedFiles = []; + var cwd = opt_relativeTo || process.cwd(); patterns = (typeof patterns === 'string') ? [patterns] : patterns; if (patterns) { for (var i = 0; i < patterns.length; ++i) { - var matches = glob.sync(patterns[i], {cwd: config_.configDir}); + var matches = glob.sync(patterns[i], {cwd: cwd}); if (!matches.length && !opt_omitWarnings) { util.puts('Warning: pattern ' + patterns[i] + ' did not match any files.'); } for (var j = 0; j < matches.length; ++j) { - resolvedFiles.push(path.resolve(config_.configDir, matches[j])); + resolvedFiles.push(path.resolve(cwd, matches[j])); } } } return resolvedFiles; }; -/** - * Helper to resolve file pattern strings relative to the cwd - * - * @private - * @param {Array} list - */ -var processFilePatterns_ = function(list) { - var patterns = list.split(','); - patterns.forEach(function(spec, index, arr) { - arr[index] = path.resolve(process.cwd(), spec); - }); - return patterns; -}; /** * Add the options in the parameter config to this runner instance. * * @private * @param {Object} additionalConfig + * @param {string} relativeTo the file path to resolve paths against */ -var addConfig_ = function(additionalConfig) { +ConfigParser.prototype.addConfig_ = function(additionalConfig, relativeTo) { // All filepaths should be kept relative to the current config location. // This will not affect absolute paths. ['seleniumServerJar', 'chromeDriver', 'onPrepare'].forEach(function(name) { - if (additionalConfig[name] && additionalConfig.configDir && - typeof additionalConfig[name] === 'string') { + if (additionalConfig[name] && typeof additionalConfig[name] === 'string') { additionalConfig[name] = - path.resolve(additionalConfig.configDir, additionalConfig[name]); + path.resolve(relativeTo, additionalConfig[name]); } }); - // Make sure they're not trying to add in deprecated config vals + // Make sure they're not trying to add in deprecated config vals. if (additionalConfig.jasmineNodeOpts && additionalConfig.jasmineNodeOpts.specFolders) { throw new Error('Using config.jasmineNodeOpts.specFolders is deprecated ' + 'since Protractor 0.6.0. Please switch to config.specs.'); } - merge_(config_,additionalConfig); + merge_(this.config_, additionalConfig); }; - -/** - * Merges in passed in configuration data with existing class defaults - * @public - * @param {Object} config - A set of properties collected that will be merged - * with AbstractTestRunner defaults - */ -var loadConfig = function(configObj, configToLoad) { - - if (!configToLoad || !configObj) { - return; - } - - /* helper to set the correct value for string dot notation */ - function setConfig_(obj, str, val) { - str = str.split('.'); - while (str.length > 1) { - obj = obj[str.shift()]; - } - obj[str.shift()] = val; - } - - /* helper to retrieve the correct value for string dot notation */ - function getConfig_(obj, str) { - var arr = str.split("."); - while(arr.length && (obj = obj[arr.shift()])); - return obj; - } - - /* helper to determine whether a config value is empty based on type */ - function isEmpty_(val) { - return ( val !== null && - val !== '' && - val !== undefined && - !(val instanceof Array && - !val.length) && - !(val instanceof Object && - !Object.keys(val).length) - ); - } - - //object definition driven merging - var key,configDef,configAlias,i; - for (key in configMap_) { - - configDef = configMap_[key]; - for (i=0; i