diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..70d6d17bc --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +Language: JavaScript +BasedOnStyle: Google +ColumnLimit: 80 diff --git a/.gitignore b/.gitignore index 7904c7cc3..68624e5e4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ built/ node_modules/ selenium/ testapp/inbrowsertest/ +typings/ website/bower_components/ website/build/ website/docgen/build/ diff --git a/gulpfile.js b/gulpfile.js index 844ac5adf..92b247d7b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,39 +1,52 @@ 'use strict'; var gulp = require('gulp'); +var clangFormat = require('clang-format'); +var gulpFormat = require('gulp-clang-format'); var runSequence = require('run-sequence'); -var spawnSync = require('child_process').spawnSync; +var spawn = require('child_process').spawn; + +var runSpawn = function(done, task, opt_arg) { + var child = spawn(task, opt_arg, {stdio: 'inherit'}); + child.on('close', function() { + done(); + }); +}; gulp.task('built:copy', function() { - var srcFiles = ['lib/**/*']; - var dist = 'built/'; - return gulp.src(srcFiles).pipe(gulp.dest(dist)); + return gulp.src(['lib/**/*','!lib/**/*.ts']) + .pipe(gulp.dest('built/')); +}); + +gulp.task('webdriver:update', function(done) { + runSpawn(done, 'bin/webdriver-manager', ['update']); +}); + +gulp.task('jslint', function(done) { + runSpawn(done, './node_modules/.bin/jshint', ['lib','spec', 'scripts']); +}); + +gulp.task('clang', function() { + return gulp.src(['lib/**/*.ts']) + .pipe(gulpFormat.checkFormat('file', clangFormat)) + .on('warning', function(e) { + console.log(e); + }); +}); + +gulp.task('typings', function(done) { + runSpawn(done, 'node_modules/.bin/typings', ['install']); }); -gulp.task('webdriver:update', function() { - var child = spawnSync('bin/webdriver-manager', ['update']); - if (child.stdout != null) { - console.log(child.stdout.toString()); - } - if (child.status !== 0) { - throw new Error('webdriver-manager update: child error'); - } +gulp.task('tsc', function(done) { + runSpawn(done, 'node_modules/typescript/bin/tsc'); }); -gulp.task('jslint', function() { - var child = spawnSync('./node_modules/.bin/jshint', ['lib','spec', 'scripts']); - if (child != null && child.stdout != null ) { - console.log(child.stdout.toString()); - } - if (child.status !== 0) { - throw new Error('jslint: child error'); - } +gulp.task('prepublish', function(done) { + runSequence(['typings', 'jslint', 'clang'],'tsc', 'built:copy', done); }); -gulp.task('pretest', function() { +gulp.task('pretest', function(done) { runSequence( - ['webdriver:update', 'jslint'], - 'built:copy' - ); + ['webdriver:update', 'typings', 'jslint', 'clang'], 'tsc', 'built:copy', done); }); -gulp.task('prepublish', ['built:copy']); diff --git a/lib/configParser.js b/lib/configParser.js deleted file mode 100644 index 62aff8ac4..000000000 --- a/lib/configParser.js +++ /dev/null @@ -1,232 +0,0 @@ -var path = require('path'), - glob = require('glob'), - log = require('./logger'), - helper = require('./util'); - -// Coffee is required here to enable config files written in coffee-script. -try { - require('coffee-script').register(); -} catch (e) { - // Intentionally blank - ignore if coffee-script is not available. -} - -// LiveScript is required here to enable config files written in LiveScript. -try { - require('LiveScript'); -} catch (e) { - // Intentionally blank - ignore if LiveScript is not available. -} - -var ConfigParser = function() { - // Default configuration. - this.config_ = { - specs: [], - multiCapabilities: [], - rootElement: 'body', - allScriptsTimeout: 11000, - getPageTimeout: 10000, - params: {}, - framework: 'jasmine', - jasmineNodeOpts: { - showColors: true, - stackFilter: helper.filterStackTrace, - defaultTimeoutInterval: (30 * 1000) - }, - seleniumArgs: [], - seleniumSessionId: null, - mochaOpts: { - ui: 'bdd', - reporter: 'list' - }, - chromeDriver: null, - configDir: './', - plugins: [], - skipSourceMapSupport: false - }; -}; - -/** - * 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) && - !(into[key] instanceof Function)) { - merge_(into[key], from[key]); - } else { - into[key] = from[key]; - } - } - return into; -}; - -/** - * Returns the item if it's an array or puts the item in an array - * if it was not one already. - */ -var makeArray = function(item) { - return Array.isArray(item) ? item : [item]; -}; - -/** - * Adds to an array all the elements in another array without adding any - * duplicates - * - * @param {Array} dest The array to add to - * @param {Array} src The array to copy from - */ -var union = function(dest, src) { - var elems = {}; - for (var key in dest) { - elems[dest[key]] = true; - } - for (key in src) { - if (!elems[src[key]]) { - dest.push(src[key]); - elems[src[key]] = true; - } - } -}; - -/** - * 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 {=string} opt_relativeTo Path to resolve patterns against - * - * @return {Array} The resolved file paths. - */ -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 fileName = patterns[i]; - var matches = glob.sync(fileName, {cwd: cwd}); - - if (!matches.length && !opt_omitWarnings) { - log.warn('pattern ' + patterns[i] + ' did not match any files.'); - } - for (var j = 0; j < matches.length; ++j) { - var resolvedPath = path.resolve(cwd, matches[j]); - resolvedFiles.push(resolvedPath); - } - } - } - return resolvedFiles; -}; - -/** - * Returns only the specs that should run currently based on `config.suite` - * - * @return {Array} An array of globs locating the spec files - */ -ConfigParser.getSpecs = function(config) { - var specs = []; - if (config.suite) { - config.suite.split(',').forEach(function(suite) { - var suiteList = config.suites[suite]; - if (suiteList == null) { - throw new Error('Unknown test suite: ' + suite); - } - union(specs, makeArray(suiteList)); - }); - return specs; - } - - if (config.specs.length > 0) { - return config.specs; - } - - Object.keys(config.suites || {}).forEach(function(suite) { - union(specs, makeArray(config.suites[suite])); - }); - return specs; -}; - -/** - * 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 - */ -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', 'firefoxPath', - 'frameworkPath']. - forEach(function(name) { - if (additionalConfig[name] && - typeof additionalConfig[name] === 'string') { - additionalConfig[name] = - path.resolve(relativeTo, additionalConfig[name]); - } - }); - - merge_(this.config_, additionalConfig); -}; - -/** - * Public function specialized towards merging in a file's config - * - * @public - * @param {String} filename - */ -ConfigParser.prototype.addFileConfig = function(filename) { - try { - if (!filename) { - return this; - } - var filePath = path.resolve(process.cwd(), filename); - var fileConfig = require(filePath).config; - if (!fileConfig) { - log.error('configuration file ' + filename + ' did not export a config ' + - 'object'); - } - fileConfig.configDir = path.dirname(filePath); - this.addConfig_(fileConfig, fileConfig.configDir); - } catch (e) { - log.error('failed loading configuration file ' + filename); - throw e; - } - return this; -}; - - -/** - * Public function specialized towards merging in config from argv - * - * @public - * @param {Object} argv - */ -ConfigParser.prototype.addConfig = function(argv) { - this.addConfig_(argv, process.cwd()); - return this; -}; - - -/** - * Public getter for the final, computed config object - * - * @public - * @return {Object} config - */ -ConfigParser.prototype.getConfig = function() { - return this.config_; -}; - -module.exports = ConfigParser; diff --git a/lib/configParser.ts b/lib/configParser.ts new file mode 100644 index 000000000..c261e728b --- /dev/null +++ b/lib/configParser.ts @@ -0,0 +1,239 @@ +import {resolve, dirname} from 'path'; +import {sync} from 'glob'; +import * as Logger from './logger'; + +// Coffee is required here to enable config files written in coffee-script. +try { + require('coffee-script').register(); +} catch (e) { + // Intentionally blank - ignore if coffee-script is not available. +} + +// LiveScript is required here to enable config files written in LiveScript. +try { + require('LiveScript'); +} catch (e) { + // Intentionally blank - ignore if LiveScript is not available. +} + +export interface Config { + specs: Array; + multiCapabilities: Array; + rootElement: string; + allScriptsTimeout: number; + getPageTimeout: number; + params: any; + framework: string; + jasmineNodeOpts: {showColors: boolean; defaultTimeoutInterval: number;}; + seleniumArgs: Array; + seleniumSessionId?: string; + mochaOpts: {ui: string; reporter: string;}; + chromeDriver?: string; + configDir: string; + plugins: Array; + skipSourceMapSupport: boolean; + suite?: string; + suites?: any; + troubleshoot?: boolean; +} + +export default class ConfigParser { + private config_: Config; + constructor() { + // Default configuration. + this.config_ = { + specs: [], + multiCapabilities: [], + rootElement: 'body', + allScriptsTimeout: 11000, + getPageTimeout: 10000, + params: {}, + framework: 'jasmine', + jasmineNodeOpts: {showColors: true, defaultTimeoutInterval: (30 * 1000)}, + seleniumArgs: [], + mochaOpts: {ui: 'bdd', reporter: 'list'}, + configDir: './', + plugins: [], + skipSourceMapSupport: false, + }; + } + + /** + * 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 {=string} opt_relativeTo Path to resolve patterns against + * + * @return {Array} The resolved file paths. + */ + public static resolveFilePatterns( + patterns: Array| string, opt_omitWarnings?: boolean, + opt_relativeTo?: string): Array { + let resolvedFiles: Array = []; + let cwd = opt_relativeTo || process.cwd(); + + patterns = (typeof patterns === 'string') ? [patterns] : patterns; + + if (patterns) { + for (let fileName of patterns) { + let matches = sync(fileName, {cwd}); + if (!matches.length && !opt_omitWarnings) { + Logger.warn('pattern ' + fileName + ' did not match any files.'); + } + for (let match of matches) { + let resolvedPath = resolve(cwd, match); + resolvedFiles.push(resolvedPath); + } + } + } + return resolvedFiles; + } + + /** + * Returns only the specs that should run currently based on `config.suite` + * + * @return {Array} An array of globs locating the spec files + */ + static getSpecs(config: Config): Array { + let specs: Array = []; + if (config.suite) { + config.suite.split(',').forEach((suite) => { + let suiteList = config.suites[suite]; + if (suiteList == null) { + throw new Error('Unknown test suite: ' + suite); + } + union(specs, makeArray(suiteList)); + }); + return specs; + } + + if (config.specs.length > 0) { + return config.specs; + } + + Object.keys(config.suites || {}).forEach((suite) => { + union(specs, makeArray(config.suites[suite])); + }); + return specs; + } + + /** + * 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 + */ + private addConfig_(additionalConfig: any, relativeTo: string): void { + // All filepaths should be kept relative to the current config location. + // This will not affect absolute paths. + ['seleniumServerJar', 'chromeDriver', 'onPrepare', 'firefoxPath', + 'frameworkPath'] + .forEach((name) => { + if (additionalConfig[name] && + typeof additionalConfig[name] === 'string') { + additionalConfig[name] = + resolve(relativeTo, additionalConfig[name]); + } + }); + + merge_(this.config_, additionalConfig); + } + + /** + * Public function specialized towards merging in a file's config + * + * @public + * @param {String} filename + */ + public addFileConfig(filename: string): ConfigParser { + try { + if (!filename) { + return this; + } + let filePath = resolve(process.cwd(), filename); + let fileConfig = require(filePath).config; + if (!fileConfig) { + Logger.error( + 'configuration file ' + filename + ' did not export a config ' + + 'object'); + } + fileConfig.configDir = dirname(filePath); + this.addConfig_(fileConfig, fileConfig.configDir); + } catch (e) { + Logger.error('failed loading configuration file ' + filename); + throw e; + } + return this; + } + + /** + * Public function specialized towards merging in config from argv + * + * @public + * @param {Object} argv + */ + public addConfig(argv: any): ConfigParser { + this.addConfig_(argv, process.cwd()); + return this; + } + + /** + * Public getter for the final, computed config object + * + * @public + * @return {Object} config + */ + public getConfig(): Config { return this.config_; } +} + +/** + * Merge config objects together. + * + * @private + * @param {Object} into + * @param {Object} from + * + * @return {Object} The 'into' config. + */ +let merge_ = function(into: any, from: any): any { + for (let key in from) { + if (into[key] instanceof Object && !(into[key] instanceof Array) && + !(into[key] instanceof Function)) { + merge_(into[key], from[key]); + } else { + // console.log(from[key].toString()); + into[key] = from[key]; + } + } + return into; +}; + +/** + * Returns the item if it's an array or puts the item in an array + * if it was not one already. + */ +let makeArray = function(item: any): any { + return Array.isArray(item) ? item : [item]; +}; + +/** + * Adds to an array all the elements in another array without adding any + * duplicates + * + * @param {Array} dest The array to add to + * @param {Array} src The array to copy from + */ +let union = function(dest: Array, src: Array): void { + let elems: any = {}; + for (let key in dest) { + elems[dest[key]] = true; + } + for (let key in src) { + if (!elems[src[key]]) { + dest.push(src[key]); + elems[src[key]] = true; + } + } +}; diff --git a/lib/driverProviders/browserstack.js b/lib/driverProviders/browserstack.js index 1cfc70d46..49d782f9b 100644 --- a/lib/driverProviders/browserstack.js +++ b/lib/driverProviders/browserstack.js @@ -5,7 +5,7 @@ */ var util = require('util'), - log = require('../logger.js'), + log = require('../logger'), request = require('request'), q = require('q'), DriverProvider = require('./driverProvider'); diff --git a/lib/driverProviders/local.js b/lib/driverProviders/local.js index 6de6ae573..810a9867a 100644 --- a/lib/driverProviders/local.js +++ b/lib/driverProviders/local.js @@ -7,7 +7,7 @@ * so that we only start the local selenium once per entire launch. */ var util = require('util'), - log = require('../logger.js'), + log = require('../logger'), path = require('path'), remote = require('selenium-webdriver/remote'), fs = require('fs'), diff --git a/lib/driverProviders/sauce.js b/lib/driverProviders/sauce.js index 90731961d..10acd37d2 100644 --- a/lib/driverProviders/sauce.js +++ b/lib/driverProviders/sauce.js @@ -5,7 +5,7 @@ */ var util = require('util'), - log = require('../logger.js'), + log = require('../logger'), SauceLabs = require('saucelabs'), q = require('q'), DriverProvider = require('./driverProvider'); diff --git a/lib/element.js b/lib/element.js index 7f07845f8..df9852476 100644 --- a/lib/element.js +++ b/lib/element.js @@ -1,5 +1,5 @@ var webdriver = require('selenium-webdriver'); -var log = require('./logger.js'); +var log = require('./logger'); var clientSideScripts = require('./clientsidescripts.js'); var WEB_ELEMENT_FUNCTIONS = [ diff --git a/lib/launcher.js b/lib/launcher.js index 60297f874..8a00e5ebe 100644 --- a/lib/launcher.js +++ b/lib/launcher.js @@ -4,7 +4,7 @@ */ 'use strict'; -var ConfigParser = require('./configParser'), +var ConfigParser = require('./configParser').default, TaskScheduler = require('./taskScheduler'), helper = require('./util'), log = require('./logger'), @@ -260,4 +260,3 @@ var init = function(configFile, additionalConfig) { }; exports.init = init; - diff --git a/lib/logger.js b/lib/logger.ts similarity index 52% rename from lib/logger.js rename to lib/logger.ts index 63a453f6e..d79f655e4 100644 --- a/lib/logger.js +++ b/lib/logger.ts @@ -1,3 +1,5 @@ +import {Config} from './configParser'; + /** * Utility functions for command line output logging from Protractor. * May be used in different processes, since the launcher spawns @@ -7,37 +9,30 @@ * should go through this file so that it can be customized. */ -var troubleshoot = false; +let troubleshoot: boolean = false; -var set = function(config) { +export function set(config: Config): void { troubleshoot = config.troubleshoot; -}; +} -var print = function(msg) { +export function print(msg: string): void { process.stdout.write(msg); -}; +} -var puts = function() { - console.log.apply(console, arguments); -}; +export function puts(...args: Array): void { + console.log.apply(console, args); +} -var debug = function(msg) { +export function debug(msg: string): void { if (troubleshoot) { - puts('DEBUG - ' + msg); + console.log('DEBUG - ' + msg); } -}; +} -var warn = function(msg) { +export function warn(msg: string): void { puts('WARNING - ' + msg); -}; +} -var error = function(msg) { +export function error(msg: string): void { puts('ERROR - ' + msg); -}; - -exports.set = set; -exports.print = print; -exports.puts = puts; -exports.debug = debug; -exports.warn = warn; -exports.error = error; +} diff --git a/lib/plugins.js b/lib/plugins.js index d2c60db1f..874f7ad94 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -1,6 +1,6 @@ var webdriver = require('selenium-webdriver'), q = require('q'), - ConfigParser = require('./configParser'), + ConfigParser = require('./configParser').default, log = require('./logger'); var PROMISE_TYPE = { diff --git a/lib/protractor.js b/lib/protractor.js index e9b21da4a..c096eb9d3 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -2,7 +2,7 @@ var util = require('util'); var url = require('url'); var webdriver = require('selenium-webdriver'); var helper = require('./util'); -var log = require('./logger.js'); +var log = require('./logger'); var ElementArrayFinder = require('./element').ElementArrayFinder; var ElementFinder = require('./element').ElementFinder; var build$ = require('./element').build$; diff --git a/lib/runner.js b/lib/runner.js index 51a4b73d9..4cc8eb89d 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -294,7 +294,7 @@ Runner.prototype.run = function() { // Do the framework setup here so that jasmine and mocha globals are // available to the onPrepare function. var frameworkPath = ''; - if (self.config_.framework === 'jasmine' || + if (self.config_.framework === 'jasmine' || self.config_.framework === 'jasmine2') { frameworkPath = './frameworks/jasmine.js'; } else if (self.config_.framework === 'mocha') { diff --git a/lib/runnerCli.js b/lib/runnerCli.js index 189e75766..708f74c60 100644 --- a/lib/runnerCli.js +++ b/lib/runnerCli.js @@ -3,7 +3,7 @@ * requested by the launcher. */ -var ConfigParser = require('./configParser'); +var ConfigParser = require('./configParser').default; var Runner = require('./runner'); var log = require('./logger'); diff --git a/lib/taskLogger.js b/lib/taskLogger.js index 5b66af983..c34b27660 100644 --- a/lib/taskLogger.js +++ b/lib/taskLogger.js @@ -1,5 +1,5 @@ var EOL = require('os').EOL; -var log = require('./logger.js'); +var log = require('./logger'); /** * Log output such that metadata are appended. diff --git a/lib/taskRunner.js b/lib/taskRunner.js index 8d0e9c17f..b3021dcdd 100644 --- a/lib/taskRunner.js +++ b/lib/taskRunner.js @@ -1,10 +1,10 @@ var child = require('child_process'); var q = require('q'); -var TaskLogger = require('./taskLogger.js'); +var TaskLogger = require('./taskLogger'); var EventEmitter = require('events').EventEmitter; var util = require('util'); -var log = require('./logger.js'); - +var log = require('./logger'); +var ConfigParser = require('./configParser').default; /** * A runner for running a specified task (capabilities + specs). @@ -100,7 +100,6 @@ TaskRunner.prototype.run = function() { return deferred.promise; } else { - var ConfigParser = require('./configParser'); var configParser = new ConfigParser(); if (this.configFile) { configParser.addFileConfig(this.configFile); diff --git a/lib/taskScheduler.js b/lib/taskScheduler.js index 9a0533f19..d94566405 100644 --- a/lib/taskScheduler.js +++ b/lib/taskScheduler.js @@ -4,7 +4,7 @@ */ 'use strict'; -var ConfigParser = require('./configParser'); +var ConfigParser = require('./configParser').default; // A queue of specs for a particular capacity var TaskQueue = function(capabilities, specLists) { diff --git a/lib/util.js b/lib/util.js deleted file mode 100644 index 714feabbc..000000000 --- a/lib/util.js +++ /dev/null @@ -1,75 +0,0 @@ -var q = require('q'), - path = require('path'); - -var STACK_SUBSTRINGS_TO_FILTER = [ - 'node_modules/jasmine/', - 'node_modules/selenium-webdriver', - 'at Module.', - 'at Object.Module.', - 'at Function.Module', - '(timers.js:', - 'jasminewd2/index.js', - 'protractor/lib/' -]; - - -/** - * Utility function that filters a stack trace to be more readable. It removes - * Jasmine test frames and webdriver promise resolution. - * @param {string} text Original stack trace. - * @return {string} - */ -exports.filterStackTrace = function(text) { - if (!text) { - return text; - } - var lines = text.split(/\n/).filter(function(line) { - for (var i = 0; i < STACK_SUBSTRINGS_TO_FILTER.length; ++i) { - if (line.indexOf(STACK_SUBSTRINGS_TO_FILTER[i]) !== -1) { - return false; - } - } - return true; - }); - return lines.join('\n'); -}; - -/** - * Internal helper for abstraction of polymorphic filenameOrFn properties. - * @param {object} filenameOrFn The filename or function that we will execute. - * @param {Array.}} args The args to pass into filenameOrFn. - * @return {q.Promise} A promise that will resolve when filenameOrFn completes. - */ -exports.runFilenameOrFn_ = function(configDir, filenameOrFn, args) { - return q.promise(function(resolve) { - if (filenameOrFn && - !(typeof filenameOrFn === 'string' || typeof filenameOrFn === 'function')) { - throw 'filenameOrFn must be a string or function'; - } - - if (typeof filenameOrFn === 'string') { - filenameOrFn = require(path.resolve(configDir, filenameOrFn)); - } - if (typeof filenameOrFn === 'function') { - var results = q.when(filenameOrFn.apply(null, args), null, function(err) { - err.stack = exports.filterStackTrace(err.stack); - throw err; - }); - resolve(results); - } else { - resolve(); - } - }); -}; - -/** - * Joins two logs of test results, each following the format of .run - * @param {object} log1 - * @param {object} log2 - * @return {object} The joined log - */ -exports.joinTestLogs = function(log1, log2) { - return {failedCount: log1.failedCount + log2.failedCount, - specResults: (log1.specResults || []).concat(log2.specResults || []) - }; -}; diff --git a/lib/util.ts b/lib/util.ts new file mode 100644 index 000000000..92c93fb74 --- /dev/null +++ b/lib/util.ts @@ -0,0 +1,73 @@ +import {Promise, when} from 'q'; +import {resolve} from 'path'; + +let STACK_SUBSTRINGS_TO_FILTER = [ + 'node_modules/jasmine/', 'node_modules/selenium-webdriver', 'at Module.', + 'at Object.Module.', 'at Function.Module', '(timers.js:', + 'jasminewd2/index.js', 'protractor/lib/' +]; + + +/** + * Utility function that filters a stack trace to be more readable. It removes + * Jasmine test frames and webdriver promise resolution. + * @param {string} text Original stack trace. + * @return {string} + */ +export function filterStackTrace(text: string): string { + if (!text) { + return text; + } + let lines = text.split(/\n/).filter((line) => { + for (let filter of STACK_SUBSTRINGS_TO_FILTER) { + if (line.indexOf(filter) !== -1) { + return false; + } + } + return true; + }); + return lines.join('\n'); +} + +/** + * Internal helper for abstraction of polymorphic filenameOrFn properties. + * @param {object} filenameOrFn The filename or function that we will execute. + * @param {Array.}} args The args to pass into filenameOrFn. + * @return {q.Promise} A promise that will resolve when filenameOrFn completes. + */ +export function runFilenameOrFn_( + configDir: string, filenameOrFn: any, args: Array): Promise { + return Promise((resolvePromise) => { + if (filenameOrFn && + !(typeof filenameOrFn === 'string' || + typeof filenameOrFn === 'function')) { + throw 'filenameOrFn must be a string or function'; + } + + if (typeof filenameOrFn === 'string') { + filenameOrFn = require(resolve(configDir, filenameOrFn)); + } + if (typeof filenameOrFn === 'function') { + let results = when(filenameOrFn.apply(null, args), null, (err) => { + err.stack = exports.filterStackTrace(err.stack); + throw err; + }); + resolvePromise(results); + } else { + resolvePromise(undefined); + } + }); +} + +/** + * Joins two logs of test results, each following the format of .run + * @param {object} log1 + * @param {object} log2 + * @return {object} The joined log + */ +export function joinTestLogs(log1: any, log2: any): any { + return { + failedCount: log1.failedCount + log2.failedCount, + specResults: (log1.specResults || []).concat(log2.specResults || []) + }; +} diff --git a/package.json b/package.json index cde05265e..4febd7a36 100644 --- a/package.json +++ b/package.json @@ -12,30 +12,34 @@ ], "author": "Julie Ralph ", "dependencies": { - "request": "~2.67.0", - "selenium-webdriver": "2.48.2", + "adm-zip": "0.4.7", + "glob": "~6.0", "jasminewd2": "0.0.8", "jasmine": "2.4.1", - "saucelabs": "~1.0.1", - "glob": "~6.0", - "adm-zip": "0.4.7", "optimist": "~0.6.0", "q": "1.4.1", + "request": "~2.67.0", + "saucelabs": "~1.0.1", + "selenium-webdriver": "2.48.2", "source-map-support": "~0.4.0" }, "devDependencies": { - "expect.js": "~0.3.1", + "body-parser": "1.14.2", "chai": "~3.4.1", "chai-as-promised": "~5.2.0", + "clang-format": "^1.0.34", + "expect.js": "~0.3.1", + "express": "~4.13.3", "gulp": "^3.9.1", + "gulp-clang-format": "^1.0.23", "jshint": "2.9.1", + "lodash": "^2.4.1", + "marked": "^0.3.3", "mocha": "2.3.4", - "express": "~4.13.3", - "body-parser": "1.14.2", "rimraf": "~2.5.0", "run-sequence": "^1.1.5", - "lodash": "^2.4.1", - "marked": "^0.3.3" + "typescript": "~1.8.0", + "typings": "~0.6.6" }, "repository": { "type": "git", @@ -49,8 +53,10 @@ "scripts": { "prepublish": "gulp prepublish", "pretest": "gulp pretest", + "start": "node testapp/scripts/web-server.js", "test": "node scripts/test.js", - "start": "node testapp/scripts/web-server.js" + "tsc": "./node_modules/typescript/bin/tsc", + "tsc:w": "./node_modules/typescript/bin/tsc -w" }, "license": "MIT", "version": "3.1.1" diff --git a/spec/unit/config_test.js b/spec/unit/config_test.js index b0296d1ba..23d488563 100644 --- a/spec/unit/config_test.js +++ b/spec/unit/config_test.js @@ -1,4 +1,4 @@ -var ConfigParser = require('../../lib/configParser'); +var ConfigParser = require('../../built/configParser').default; var path = require('path'); describe('the config parser', function() { diff --git a/spec/unit/runner_test.js b/spec/unit/runner_test.js index a646792a2..809031c6d 100644 --- a/spec/unit/runner_test.js +++ b/spec/unit/runner_test.js @@ -1,4 +1,4 @@ -var Runner = require('../../lib/runner'); +var Runner = require('../../built/runner'); var q = require('q'); describe('the Protractor runner', function() { diff --git a/spec/unit/taskScheduler_test.js b/spec/unit/taskScheduler_test.js index 32cb818a0..a18159ff1 100644 --- a/spec/unit/taskScheduler_test.js +++ b/spec/unit/taskScheduler_test.js @@ -1,5 +1,5 @@ -var TaskScheduler = require('../../lib/taskScheduler.js'); -var ConfigParser = require('../../lib/configParser'); +var TaskScheduler = require('../../built/taskScheduler.js'); +var ConfigParser = require('../../built/configParser').default; describe('the task scheduler', function() { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..1ea4a669f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": false, + "declaration": true, + "removeComments": false, + "noImplicitAny": false, + "outDir": "built/" + }, + "exclude": [ + "built", + "node_modules", + "testapp", + "typings/browser", + "typings/browser.d.ts", + "typings/main" + ] +} diff --git a/typings.json b/typings.json new file mode 100644 index 000000000..831edf1e2 --- /dev/null +++ b/typings.json @@ -0,0 +1,8 @@ +{ + "ambientDependencies": { + "Q": "github:DefinitelyTyped/DefinitelyTyped/q/Q.d.ts#717a5fdb079f8dd7c19f1b22f7f656dd990f0ccf", + "node": "github:DefinitelyTyped/DefinitelyTyped/node/node.d.ts#c2939250ec2d1ecdc82fce62afb9354dd14909ea", + "glob": "github:DefinitelyTyped/DefinitelyTyped/glob/glob.d.ts#c2939250ec2d1ecdc82fce62afb9354dd14909ea", + "minimatch": "github:DefinitelyTyped/DefinitelyTyped/minimatch/minimatch.d.ts#c2939250ec2d1ecdc82fce62afb9354dd14909ea" + } +}