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

Commit 6848180

Browse files
committed
refactor(launcher): some launcher clean-ups and refactors
Simplify the ConfigParser and remove the config map for now. Add unit tests for the ConfigParser and runner, and an integration test for driverProviders. Make sure the promises returned by the runner all chain nicely. Make sure the exit code for the entire launcher process is correct. Change the config so that multiConfig set must be explicit - this prevents weird issues from merging objects into arrays.
1 parent 77393d0 commit 6848180

17 files changed

+949
-808
lines changed

lib/cli.js

+28-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* The command line interface for interacting with the Protractor runner.
3-
* It takes care of parsing the config file and command line options.
3+
* It takes care of parsing command line options.
44
*
55
* Values from command line options override values from the config.
66
*/
@@ -67,13 +67,16 @@ if (argv.version) {
6767
process.exit(0);
6868
}
6969

70-
// WebDriver capabilities properties require dot notation.
70+
// WebDriver capabilities properties require dot notation, but optimist parses
71+
// that into an object. Re-flatten it.
7172
var flattenObject = function(obj) {
7273
var prefix = arguments[1] || '';
7374
var out = arguments[2] || {};
7475
for (var prop in obj) {
7576
if (obj.hasOwnProperty(prop)) {
76-
typeof obj[prop] === 'object' ? flattenObject(obj[prop], prefix + prop + '.', out) : out[prefix + prop] = obj[prop];
77+
typeof obj[prop] === 'object' ?
78+
flattenObject(obj[prop], prefix + prop + '.', out) :
79+
out[prefix + prop] = obj[prop];
7780
}
7881
}
7982
return out;
@@ -83,5 +86,27 @@ if (argv.capabilities) {
8386
argv.capabilities = flattenObject(argv.capabilities);
8487
}
8588

89+
/**
90+
* Helper to resolve comma separated lists of file pattern strings relative to
91+
* the cwd.
92+
*
93+
* @private
94+
* @param {Array} list
95+
*/
96+
var processFilePatterns_ = function(list) {
97+
var patterns = list.split(',');
98+
patterns.forEach(function(spec, index, arr) {
99+
arr[index] = path.resolve(process.cwd(), spec);
100+
});
101+
return patterns;
102+
};
103+
104+
if (argv.specs) {
105+
argv.specs = processFilePatterns_(argv.specs);
106+
}
107+
if (argv.excludes) {
108+
argv.excludes = processFilePatterns_(argv.excludes);
109+
}
110+
86111
// Run the launcher
87112
require('./launcher').init(argv);

lib/configParser.js

+55-141
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
11
var path = require('path'),
22
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_ = {
3+
util = require('util'),
4+
protractor = require('./protractor.js');
5+
116

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-
};
7+
module.exports = ConfigParser = function() {
8+
// Default configuration.
9+
this.config_= {
10+
specs: [],
11+
capabilities: {
12+
browserName: 'chrome'
13+
},
14+
multiCapabilities: [],
15+
rootElement: 'body',
16+
allScriptsTimeout: 11000,
17+
params: {},
18+
framework: 'jasmine',
19+
jasmineNodeOpts: {
20+
isVerbose: false,
21+
showColors: true,
22+
includeStackTrace: true,
23+
stackFilter: protractor.filterStackTrace,
24+
defaultTimeoutInterval: (30 * 1000)
25+
},
26+
seleniumArgs: [],
27+
cucumberOpts: {},
28+
mochaOpts: {
29+
ui: 'bdd',
30+
reporter: 'list'
31+
},
32+
chromeDriver: null,
33+
configDir: './'
34+
};
35+
};
3636

3737
/**
3838
* Merge config objects together.
@@ -57,145 +57,76 @@ var merge_ = function(into, from) {
5757
/**
5858
* Resolve a list of file patterns into a list of individual file paths.
5959
*
60-
* @param {Array/String} patterns
61-
* @param {Boolean} opt_omitWarnings whether to omit did not match warnings
60+
* @param {Array.<string> | string} patterns
61+
* @param {=boolean} opt_omitWarnings Whether to omit did not match warnings
62+
* @param {=string} opt_relativeTo Path to resolve patterns against
6263
*
6364
* @return {Array} The resolved file paths.
6465
*/
65-
var resolveFilePatterns = function(patterns, opt_omitWarnings) {
66+
ConfigParser.resolveFilePatterns =
67+
function(patterns, opt_omitWarnings, opt_relativeTo) {
6668
var resolvedFiles = [];
69+
var cwd = opt_relativeTo || process.cwd();
6770

6871
patterns = (typeof patterns === 'string') ?
6972
[patterns] : patterns;
7073

7174
if (patterns) {
7275
for (var i = 0; i < patterns.length; ++i) {
73-
var matches = glob.sync(patterns[i], {cwd: config_.configDir});
76+
var matches = glob.sync(patterns[i], {cwd: cwd});
7477
if (!matches.length && !opt_omitWarnings) {
7578
util.puts('Warning: pattern ' + patterns[i] + ' did not match any files.');
7679
}
7780
for (var j = 0; j < matches.length; ++j) {
78-
resolvedFiles.push(path.resolve(config_.configDir, matches[j]));
81+
resolvedFiles.push(path.resolve(cwd, matches[j]));
7982
}
8083
}
8184
}
8285
return resolvedFiles;
8386
};
8487

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-
};
9888

9989
/**
10090
* Add the options in the parameter config to this runner instance.
10191
*
10292
* @private
10393
* @param {Object} additionalConfig
94+
* @param {string} relativeTo the file path to resolve paths against
10495
*/
105-
var addConfig_ = function(additionalConfig) {
96+
ConfigParser.prototype.addConfig_ = function(additionalConfig, relativeTo) {
10697
// All filepaths should be kept relative to the current config location.
10798
// This will not affect absolute paths.
10899
['seleniumServerJar', 'chromeDriver', 'onPrepare'].forEach(function(name) {
109-
if (additionalConfig[name] && additionalConfig.configDir &&
110-
typeof additionalConfig[name] === 'string') {
100+
if (additionalConfig[name] && typeof additionalConfig[name] === 'string') {
111101
additionalConfig[name] =
112-
path.resolve(additionalConfig.configDir, additionalConfig[name]);
102+
path.resolve(relativeTo, additionalConfig[name]);
113103
}
114104
});
115105

116-
// Make sure they're not trying to add in deprecated config vals
106+
// Make sure they're not trying to add in deprecated config vals.
117107
if (additionalConfig.jasmineNodeOpts &&
118108
additionalConfig.jasmineNodeOpts.specFolders) {
119109
throw new Error('Using config.jasmineNodeOpts.specFolders is deprecated ' +
120110
'since Protractor 0.6.0. Please switch to config.specs.');
121111
}
122-
merge_(config_,additionalConfig);
112+
merge_(this.config_, additionalConfig);
123113
};
124114

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-
185115
/**
186116
* Public function specialized towards merging in a file's config
187117
*
188118
* @public
189119
* @param {String} filename
190120
*/
191-
var addFileConfig = function(filename) {
121+
ConfigParser.prototype.addFileConfig = function(filename) {
192122
if (!filename) {
193-
return;
123+
return;
194124
}
195125
var filePath = path.resolve(process.cwd(), filename);
196126
var fileConfig = require(filePath).config;
197127
fileConfig.configDir = path.dirname(filePath);
198-
addConfig_(fileConfig);
128+
this.addConfig_(fileConfig, fileConfig.configDir);
129+
return this;
199130
};
200131

201132

@@ -205,19 +136,9 @@ var addFileConfig = function(filename) {
205136
* @public
206137
* @param {Object} argv
207138
*/
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);
139+
ConfigParser.prototype.addConfig = function(argv) {
140+
this.addConfig_(argv, process.cwd());
141+
return this;
221142
};
222143

223144

@@ -227,13 +148,6 @@ var addArgvConfig = function(argv) {
227148
* @public
228149
* @return {Object} config
229150
*/
230-
var getConfig = function() {
231-
return config_;
151+
ConfigParser.prototype.getConfig = function() {
152+
return this.config_;
232153
};
233-
234-
235-
exports.addArgvConfig = addArgvConfig;
236-
exports.addFileConfig = addFileConfig;
237-
exports.getConfig = getConfig;
238-
exports.loadConfig = loadConfig;
239-
exports.resolveFilePatterns = resolveFilePatterns;

0 commit comments

Comments
 (0)