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

Commit 77393d0

Browse files
awal112358juliemr
authored andcommitted
feat(runner/launcher): major runner updates to allow multiple capabilities
Adding simultaneous runner capability (grid-style), refactoring launch/runner init system, and abstracting out configParser module.
1 parent c1bbe49 commit 77393d0

File tree

10 files changed

+1172
-475
lines changed

10 files changed

+1172
-475
lines changed

lib/cli.js

+4-35
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ try {
2424

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

70-
71-
// Any file names should be resolved relative to the current working directory.
72-
var processFilePatterns = function(list) {
73-
var patterns = list.split(',');
74-
patterns.forEach(function(spec, index, arr) {
75-
arr[index] = path.resolve(process.cwd(), spec);
76-
});
77-
return patterns;
78-
}
79-
if (argv.specs) {
80-
argv.specs = processFilePatterns(argv.specs);
81-
}
82-
if (argv.exclude) {
83-
argv.exclude = processFilePatterns(argv.exclude);
84-
}
85-
8670
// WebDriver capabilities properties require dot notation.
8771
var flattenObject = function(obj) {
8872
var prefix = arguments[1] || '';
@@ -93,26 +77,11 @@ var flattenObject = function(obj) {
9377
}
9478
}
9579
return out;
96-
}
80+
};
9781

9882
if (argv.capabilities) {
9983
argv.capabilities = flattenObject(argv.capabilities);
10084
}
10185

102-
['seleniumServerJar', 'chromeDriver', 'onPrepare'].forEach(function(name) {
103-
if (argv[name]) {
104-
argv[name] = path.resolve(process.cwd(), argv[name]);
105-
}
106-
});
107-
108-
var configFilename = argv._[0];
109-
if (configFilename) {
110-
var configPath = path.resolve(process.cwd(), configFilename);
111-
var config = require(configPath).config;
112-
config.configDir = path.dirname(configPath);
113-
runner.addConfig(config);
114-
}
115-
116-
runner.addConfig(argv);
117-
118-
runner.runOnce();
86+
// Run the launcher
87+
require('./launcher').init(argv);

lib/configParser.js

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
var path = require('path'),
2+
glob = require('glob'),
3+
config_ = {
4+
configDir: './',
5+
jasmineNodeOpts: {}
6+
},
7+
//this allows for ease of maintaining public apis of the config while still
8+
// allowing for easy variable renames or future config api changes while
9+
// supported backwards compatibility
10+
configMap_ = {
11+
12+
//structure is internal name -> supported config apis
13+
'specs': ['specs'],
14+
'exclude': ['exclude'],
15+
'capabilities': ['capabilities'],
16+
'seleniumHost': ['seleniumAddress'],
17+
'rootElement': ['rootElement'],
18+
'baseUrl': ['baseUrl'],
19+
'timeout': ['allScriptsTimeout'],
20+
'browserParams': ['params'],
21+
'framework': ['framework'],
22+
'jasmineOpts': ['jasmineNodeOpts'],
23+
'mochaOpts': ['mochaOpts'],
24+
'seleniumLocal.jar': ['seleniumServerJar'],
25+
'seleniumLocal.args': ['seleniumArgs'],
26+
'seleniumLocal.port': ['seleniumPort'],
27+
'sauceAccount.user': ['sauceUser'],
28+
'sauceAccount.key': ['sauceKey'],
29+
'chromeDriver': ['chromeDriver'],
30+
'chromeOnly': ['chromeOnly'],
31+
'configDir': ['configDir'],
32+
'cucumberOpts.require': ['cucumberOpts.require'],
33+
'cucumberOpts.format': ['cucumberOpts.format'],
34+
'cucumberOpts.tags': ['cucumberOpts.tags']
35+
};
36+
37+
/**
38+
* Merge config objects together.
39+
*
40+
* @private
41+
* @param {Object} into
42+
* @param {Object} from
43+
*
44+
* @return {Object} The 'into' config.
45+
*/
46+
var merge_ = function(into, from) {
47+
for (var key in from) {
48+
if (into[key] instanceof Object && !(into[key] instanceof Array)) {
49+
merge_(into[key], from[key]);
50+
} else {
51+
into[key] = from[key];
52+
}
53+
}
54+
return into;
55+
};
56+
57+
/**
58+
* Resolve a list of file patterns into a list of individual file paths.
59+
*
60+
* @param {Array/String} patterns
61+
* @param {Boolean} opt_omitWarnings whether to omit did not match warnings
62+
*
63+
* @return {Array} The resolved file paths.
64+
*/
65+
var resolveFilePatterns = function(patterns, opt_omitWarnings) {
66+
var resolvedFiles = [];
67+
68+
patterns = (typeof patterns === 'string') ?
69+
[patterns] : patterns;
70+
71+
if (patterns) {
72+
for (var i = 0; i < patterns.length; ++i) {
73+
var matches = glob.sync(patterns[i], {cwd: config_.configDir});
74+
if (!matches.length && !opt_omitWarnings) {
75+
util.puts('Warning: pattern ' + patterns[i] + ' did not match any files.');
76+
}
77+
for (var j = 0; j < matches.length; ++j) {
78+
resolvedFiles.push(path.resolve(config_.configDir, matches[j]));
79+
}
80+
}
81+
}
82+
return resolvedFiles;
83+
};
84+
85+
/**
86+
* Helper to resolve file pattern strings relative to the cwd
87+
*
88+
* @private
89+
* @param {Array} list
90+
*/
91+
var processFilePatterns_ = function(list) {
92+
var patterns = list.split(',');
93+
patterns.forEach(function(spec, index, arr) {
94+
arr[index] = path.resolve(process.cwd(), spec);
95+
});
96+
return patterns;
97+
};
98+
99+
/**
100+
* Add the options in the parameter config to this runner instance.
101+
*
102+
* @private
103+
* @param {Object} additionalConfig
104+
*/
105+
var addConfig_ = function(additionalConfig) {
106+
// All filepaths should be kept relative to the current config location.
107+
// This will not affect absolute paths.
108+
['seleniumServerJar', 'chromeDriver', 'onPrepare'].forEach(function(name) {
109+
if (additionalConfig[name] && additionalConfig.configDir &&
110+
typeof additionalConfig[name] === 'string') {
111+
additionalConfig[name] =
112+
path.resolve(additionalConfig.configDir, additionalConfig[name]);
113+
}
114+
});
115+
116+
// Make sure they're not trying to add in deprecated config vals
117+
if (additionalConfig.jasmineNodeOpts &&
118+
additionalConfig.jasmineNodeOpts.specFolders) {
119+
throw new Error('Using config.jasmineNodeOpts.specFolders is deprecated ' +
120+
'since Protractor 0.6.0. Please switch to config.specs.');
121+
}
122+
merge_(config_,additionalConfig);
123+
};
124+
125+
126+
/**
127+
* Merges in passed in configuration data with existing class defaults
128+
* @public
129+
* @param {Object} config - A set of properties collected that will be merged
130+
* with AbstractTestRunner defaults
131+
*/
132+
var loadConfig = function(configObj, configToLoad) {
133+
134+
if (!configToLoad || !configObj) {
135+
return;
136+
}
137+
138+
/* helper to set the correct value for string dot notation */
139+
function setConfig_(obj, str, val) {
140+
str = str.split('.');
141+
while (str.length > 1) {
142+
obj = obj[str.shift()];
143+
}
144+
obj[str.shift()] = val;
145+
}
146+
147+
/* helper to retrieve the correct value for string dot notation */
148+
function getConfig_(obj, str) {
149+
var arr = str.split(".");
150+
while(arr.length && (obj = obj[arr.shift()]));
151+
return obj;
152+
}
153+
154+
/* helper to determine whether a config value is empty based on type */
155+
function isEmpty_(val) {
156+
return ( val !== null &&
157+
val !== '' &&
158+
val !== undefined &&
159+
!(val instanceof Array &&
160+
!val.length) &&
161+
!(val instanceof Object &&
162+
!Object.keys(val).length)
163+
);
164+
}
165+
166+
//object definition driven merging
167+
var key,configDef,configAlias,i;
168+
for (key in configMap_) {
169+
170+
configDef = configMap_[key];
171+
for (i=0; i<configDef.length; i++) {
172+
configAlias = configDef[i];
173+
var configVal = getConfig_(configToLoad,configAlias);
174+
if (isEmpty_(configVal)) {
175+
//override config default w/ passed in config
176+
setConfig_(configObj,key,configVal);
177+
}
178+
}
179+
}
180+
};
181+
182+
183+
184+
185+
/**
186+
* Public function specialized towards merging in a file's config
187+
*
188+
* @public
189+
* @param {String} filename
190+
*/
191+
var addFileConfig = function(filename) {
192+
if (!filename) {
193+
return;
194+
}
195+
var filePath = path.resolve(process.cwd(), filename);
196+
var fileConfig = require(filePath).config;
197+
fileConfig.configDir = path.dirname(filePath);
198+
addConfig_(fileConfig);
199+
};
200+
201+
202+
/**
203+
* Public function specialized towards merging in config from argv
204+
*
205+
* @public
206+
* @param {Object} argv
207+
*/
208+
var addArgvConfig = function(argv) {
209+
if (!argv) {
210+
return;
211+
}
212+
// Interpret/parse spec include/exclude patterns
213+
if (argv.specs) {
214+
argv.specs = processFilePatterns_(argv.specs);
215+
}
216+
if (argv.exclude) {
217+
argv.exclude = processFilePatterns(argv.exclude);
218+
}
219+
220+
addConfig_(argv);
221+
};
222+
223+
224+
/**
225+
* Public getter for the final, computed config object
226+
*
227+
* @public
228+
* @return {Object} config
229+
*/
230+
var getConfig = function() {
231+
return config_;
232+
};
233+
234+
235+
exports.addArgvConfig = addArgvConfig;
236+
exports.addFileConfig = addFileConfig;
237+
exports.getConfig = getConfig;
238+
exports.loadConfig = loadConfig;
239+
exports.resolveFilePatterns = resolveFilePatterns;

lib/driverProviders/chrome.dp.js

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* This is an implementation of the Chrome Driver Provider.
3+
* It is responsible for setting up the account object, tearing
4+
* it down, and setting up the driver correctly.
5+
*/
6+
7+
var util = require('util'),
8+
webdriver = require('selenium-webdriver'),
9+
chrome = require('selenium-webdriver/chrome'),
10+
q = require('q');
11+
12+
function ChromeDriverProvider(testRunner) {
13+
14+
this.testRunner_ = testRunner;
15+
this.trConfig_ = this.testRunner_.getConfig();
16+
this.driver_ = null;
17+
}
18+
19+
/**
20+
* setupEnv is responsible for configuring and launching (if applicable)
21+
* the object's environment.
22+
* @public
23+
* @return promise
24+
*/
25+
ChromeDriverProvider.prototype.setupEnv = function() {
26+
var deferred = q.defer();
27+
util.puts('Using ChromeDriver directly...');
28+
deferred.resolve();
29+
return deferred.promise;
30+
};
31+
32+
/**
33+
* teardownEnv is responsible for destroying the environment and doing any
34+
* associated cleanup.
35+
* @public
36+
* @param {runnerResult} runner
37+
*/
38+
ChromeDriverProvider.prototype.teardownEnv = function(runner) {
39+
var deferred = q.defer(),
40+
passed = runner.results().failedCount === 0,
41+
exitCode = passed ? 0 : 1;
42+
43+
deferred.resolve(exitCode);
44+
return deferred.promise;
45+
};
46+
47+
/**
48+
* getDriver is responsible for retrieving the webdriver for the runner.
49+
* @public
50+
* @return webdriver instance
51+
*/
52+
ChromeDriverProvider.prototype.getDriver = function() {
53+
if (!this.driver_) {
54+
var service = new chrome.ServiceBuilder(this.trConfig_.chromeDriver).build();
55+
this.driver_ = chrome.createDriver(
56+
new webdriver.Capabilities(this.trConfig_.capabilities), service);
57+
}
58+
return this.driver_;
59+
};
60+
61+
//new instance w/ each include
62+
module.exports = (function(testRunner) {
63+
return new ChromeDriverProvider(testRunner);
64+
});

0 commit comments

Comments
 (0)