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

Commit

Permalink
feat(typescript): adding typescript to protractor
Browse files Browse the repository at this point in the history
Converting a 3 files over to typescript.

Adding an `npm prepublish` step that will use gulp to download the typings, transpile the files with tscto the built/ directory and copy the rest of the javascript files from lib/ to the built/ folder.

Also adding scripts to package.json for `npm run tsc` and `npm run tsc:w` for transpiling help.
cnishina committed Feb 16, 2016

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent a7734f8 commit 9608201
Showing 26 changed files with 434 additions and 385 deletions.
3 changes: 3 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Language: JavaScript
BasedOnStyle: Google
ColumnLimit: 80
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ built/
node_modules/
selenium/
testapp/inbrowsertest/
typings/
website/bower_components/
website/build/
website/docgen/build/
63 changes: 38 additions & 25 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -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']);
232 changes: 0 additions & 232 deletions lib/configParser.js

This file was deleted.

239 changes: 239 additions & 0 deletions lib/configParser.ts
Original file line number Diff line number Diff line change
@@ -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<string>;
multiCapabilities: Array<any>;
rootElement: string;
allScriptsTimeout: number;
getPageTimeout: number;
params: any;
framework: string;
jasmineNodeOpts: {showColors: boolean; defaultTimeoutInterval: number;};
seleniumArgs: Array<any>;
seleniumSessionId?: string;
mochaOpts: {ui: string; reporter: string;};
chromeDriver?: string;
configDir: string;
plugins: Array<any>;
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> | 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>| string, opt_omitWarnings?: boolean,
opt_relativeTo?: string): Array<string> {
let resolvedFiles: Array<string> = [];
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<string> {
let specs: Array<string> = [];
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<string>} dest The array to add to
* @param {Array<string>} src The array to copy from
*/
let union = function(dest: Array<string>, src: Array<string>): 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;
}
}
};
2 changes: 1 addition & 1 deletion lib/driverProviders/browserstack.js
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
*/

var util = require('util'),
log = require('../logger.js'),
log = require('../logger'),
request = require('request'),
q = require('q'),
DriverProvider = require('./driverProvider');
2 changes: 1 addition & 1 deletion lib/driverProviders/local.js
Original file line number Diff line number Diff line change
@@ -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'),
2 changes: 1 addition & 1 deletion lib/driverProviders/sauce.js
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
*/

var util = require('util'),
log = require('../logger.js'),
log = require('../logger'),
SauceLabs = require('saucelabs'),
q = require('q'),
DriverProvider = require('./driverProvider');
2 changes: 1 addition & 1 deletion lib/element.js
Original file line number Diff line number Diff line change
@@ -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 = [
3 changes: 1 addition & 2 deletions lib/launcher.js
Original file line number Diff line number Diff line change
@@ -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;

39 changes: 17 additions & 22 deletions lib/logger.js → lib/logger.ts
Original file line number Diff line number Diff line change
@@ -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<any>): 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;
}
2 changes: 1 addition & 1 deletion lib/plugins.js
Original file line number Diff line number Diff line change
@@ -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 = {
2 changes: 1 addition & 1 deletion lib/protractor.js
Original file line number Diff line number Diff line change
@@ -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$;
2 changes: 1 addition & 1 deletion lib/runner.js
Original file line number Diff line number Diff line change
@@ -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') {
2 changes: 1 addition & 1 deletion lib/runnerCli.js
Original file line number Diff line number Diff line change
@@ -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');

2 changes: 1 addition & 1 deletion lib/taskLogger.js
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 3 additions & 4 deletions lib/taskRunner.js
Original file line number Diff line number Diff line change
@@ -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);
2 changes: 1 addition & 1 deletion lib/taskScheduler.js
Original file line number Diff line number Diff line change
@@ -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) {
75 changes: 0 additions & 75 deletions lib/util.js

This file was deleted.

73 changes: 73 additions & 0 deletions lib/util.ts
Original file line number Diff line number Diff line change
@@ -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.<object>}} 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<any>): Promise<any> {
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 <framework>.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 || [])
};
}
28 changes: 17 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -12,30 +12,34 @@
],
"author": "Julie Ralph <ju.ralph@gmail.com>",
"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"
2 changes: 1 addition & 1 deletion spec/unit/config_test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var ConfigParser = require('../../lib/configParser');
var ConfigParser = require('../../built/configParser').default;
var path = require('path');

describe('the config parser', function() {
2 changes: 1 addition & 1 deletion spec/unit/runner_test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var Runner = require('../../lib/runner');
var Runner = require('../../built/runner');
var q = require('q');

describe('the Protractor runner', function() {
4 changes: 2 additions & 2 deletions spec/unit/taskScheduler_test.js
Original file line number Diff line number Diff line change
@@ -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() {

20 changes: 20 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
8 changes: 8 additions & 0 deletions typings.json
Original file line number Diff line number Diff line change
@@ -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"
}
}

0 comments on commit 9608201

Please sign in to comment.