Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Commit

Permalink
feat(runner/launcher): major runner updates to allow multiple capabil…
Browse files Browse the repository at this point in the history
…ities

Adding simultaneous runner capability (grid-style), refactoring launch/runner init system,
and abstracting out configParser module.
  • Loading branch information
awal112358 authored and juliemr committed Feb 13, 2014
1 parent c1bbe49 commit 77393d0
Show file tree
Hide file tree
Showing 10 changed files with 1,172 additions and 475 deletions.
39 changes: 4 additions & 35 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ try {

var util = require('util');
var path = require('path');
var runner = require('./runner.js');
var child = require('child_process');
var argv = require('optimist').
usage('Usage: protractor [options] [configFile]\n' +
'The [options] object will override values from the config file.\n' +
Expand Down Expand Up @@ -67,22 +67,6 @@ if (argv.version) {
process.exit(0);
}


// Any file names should be resolved relative to the current working directory.
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.exclude) {
argv.exclude = processFilePatterns(argv.exclude);
}

// WebDriver capabilities properties require dot notation.
var flattenObject = function(obj) {
var prefix = arguments[1] || '';
Expand All @@ -93,26 +77,11 @@ var flattenObject = function(obj) {
}
}
return out;
}
};

if (argv.capabilities) {
argv.capabilities = flattenObject(argv.capabilities);
}

['seleniumServerJar', 'chromeDriver', 'onPrepare'].forEach(function(name) {
if (argv[name]) {
argv[name] = path.resolve(process.cwd(), argv[name]);
}
});

var configFilename = argv._[0];
if (configFilename) {
var configPath = path.resolve(process.cwd(), configFilename);
var config = require(configPath).config;
config.configDir = path.dirname(configPath);
runner.addConfig(config);
}

runner.addConfig(argv);

runner.runOnce();
// Run the launcher
require('./launcher').init(argv);
239 changes: 239 additions & 0 deletions lib/configParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
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_ = {

//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']
};

/**
* Merge config objects together.
*
* @private
* @param {Object} into
* @param {Object} from
*
* @return {Object} The 'into' config.
*/
var merge_ = function(into, from) {
for (var key in from) {
if (into[key] instanceof Object && !(into[key] instanceof Array)) {
merge_(into[key], from[key]);
} else {
into[key] = from[key];
}
}
return into;
};

/**
* 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
*
* @return {Array} The resolved file paths.
*/
var resolveFilePatterns = function(patterns, opt_omitWarnings) {
var resolvedFiles = [];

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});
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]));
}
}
}
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
*/
var addConfig_ = function(additionalConfig) {
// 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') {
additionalConfig[name] =
path.resolve(additionalConfig.configDir, additionalConfig[name]);
}
});

// 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);
};


/**
* 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<configDef.length; i++) {
configAlias = configDef[i];
var configVal = getConfig_(configToLoad,configAlias);
if (isEmpty_(configVal)) {
//override config default w/ passed in config
setConfig_(configObj,key,configVal);
}
}
}
};




/**
* Public function specialized towards merging in a file's config
*
* @public
* @param {String} filename
*/
var addFileConfig = function(filename) {
if (!filename) {
return;
}
var filePath = path.resolve(process.cwd(), filename);
var fileConfig = require(filePath).config;
fileConfig.configDir = path.dirname(filePath);
addConfig_(fileConfig);
};


/**
* Public function specialized towards merging in config from argv
*
* @public
* @param {Object} argv
*/
var addArgvConfig = function(argv) {
if (!argv) {
return;
}
// Interpret/parse spec include/exclude patterns
if (argv.specs) {
argv.specs = processFilePatterns_(argv.specs);
}
if (argv.exclude) {
argv.exclude = processFilePatterns(argv.exclude);
}

addConfig_(argv);
};


/**
* Public getter for the final, computed config object
*
* @public
* @return {Object} config
*/
var getConfig = function() {
return config_;
};


exports.addArgvConfig = addArgvConfig;
exports.addFileConfig = addFileConfig;
exports.getConfig = getConfig;
exports.loadConfig = loadConfig;
exports.resolveFilePatterns = resolveFilePatterns;
64 changes: 64 additions & 0 deletions lib/driverProviders/chrome.dp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* This is an implementation of the Chrome Driver Provider.
* It is responsible for setting up the account object, tearing
* it down, and setting up the driver correctly.
*/

var util = require('util'),
webdriver = require('selenium-webdriver'),
chrome = require('selenium-webdriver/chrome'),
q = require('q');

function ChromeDriverProvider(testRunner) {

this.testRunner_ = testRunner;
this.trConfig_ = this.testRunner_.getConfig();
this.driver_ = null;
}

/**
* setupEnv is responsible for configuring and launching (if applicable)
* the object's environment.
* @public
* @return promise
*/
ChromeDriverProvider.prototype.setupEnv = function() {
var deferred = q.defer();
util.puts('Using ChromeDriver directly...');
deferred.resolve();
return deferred.promise;
};

/**
* teardownEnv is responsible for destroying the environment and doing any
* associated cleanup.
* @public
* @param {runnerResult} runner
*/
ChromeDriverProvider.prototype.teardownEnv = function(runner) {
var deferred = q.defer(),
passed = runner.results().failedCount === 0,
exitCode = passed ? 0 : 1;

deferred.resolve(exitCode);
return deferred.promise;
};

/**
* getDriver is responsible for retrieving the webdriver for the runner.
* @public
* @return webdriver instance
*/
ChromeDriverProvider.prototype.getDriver = function() {
if (!this.driver_) {
var service = new chrome.ServiceBuilder(this.trConfig_.chromeDriver).build();
this.driver_ = chrome.createDriver(
new webdriver.Capabilities(this.trConfig_.capabilities), service);
}
return this.driver_;
};

//new instance w/ each include
module.exports = (function(testRunner) {
return new ChromeDriverProvider(testRunner);
});
Loading

1 comment on commit 77393d0

@rayeesnp
Copy link

Choose a reason for hiding this comment

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

any document for this ,I started working on protractor in local machine want to run those test cases in production environment

Please sign in to comment.