intern
Version:
Intern. A next-generation code testing stack for JavaScript.
640 lines • 32.1 kB
JavaScript
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "tslib", "fs", "path", "resolve", "istanbul-lib-coverage", "istanbul-lib-instrument", "istanbul-lib-source-maps", "istanbul-lib-hook", "@dojo/shim/global", "@dojo/core/async/Task", "@dojo/core/lang", "@theintern/leadfoot/Command", "@theintern/leadfoot/Server", "@theintern/digdug/SeleniumTunnel", "@theintern/digdug/BrowserStackTunnel", "@theintern/digdug/SauceLabsTunnel", "@theintern/digdug/TestingBotTunnel", "@theintern/digdug/CrossBrowserTestingTunnel", "@theintern/digdug/NullTunnel", "./Executor", "../common/path", "../common/util", "../node/util", "../node/ErrorFormatter", "../ProxiedSession", "../Environment", "../resolveEnvironments", "../Server", "../Suite", "../RemoteSuite", "../common/console", "../reporters/Pretty", "../reporters/Runner", "../reporters/Simple", "../reporters/JUnit", "../reporters/Cobertura", "../reporters/JsonCoverage", "../reporters/HtmlCoverage", "../reporters/Lcov", "../reporters/Benchmark", "../reporters/TeamCity"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var fs_1 = require("fs");
var path_1 = require("path");
var resolve_1 = require("resolve");
var istanbul_lib_coverage_1 = require("istanbul-lib-coverage");
var istanbul_lib_instrument_1 = require("istanbul-lib-instrument");
var istanbul_lib_source_maps_1 = require("istanbul-lib-source-maps");
var istanbul_lib_hook_1 = require("istanbul-lib-hook");
var global_1 = require("@dojo/shim/global");
var Task_1 = require("@dojo/core/async/Task");
var lang_1 = require("@dojo/core/lang");
var Command_1 = require("@theintern/leadfoot/Command");
var Server_1 = require("@theintern/leadfoot/Server");
var SeleniumTunnel_1 = require("@theintern/digdug/SeleniumTunnel");
var BrowserStackTunnel_1 = require("@theintern/digdug/BrowserStackTunnel");
var SauceLabsTunnel_1 = require("@theintern/digdug/SauceLabsTunnel");
var TestingBotTunnel_1 = require("@theintern/digdug/TestingBotTunnel");
var CrossBrowserTestingTunnel_1 = require("@theintern/digdug/CrossBrowserTestingTunnel");
var NullTunnel_1 = require("@theintern/digdug/NullTunnel");
var Executor_1 = require("./Executor");
var path_2 = require("../common/path");
var util_1 = require("../common/util");
var util_2 = require("../node/util");
var ErrorFormatter_1 = require("../node/ErrorFormatter");
var ProxiedSession_1 = require("../ProxiedSession");
var Environment_1 = require("../Environment");
var resolveEnvironments_1 = require("../resolveEnvironments");
var Server_2 = require("../Server");
var Suite_1 = require("../Suite");
var RemoteSuite_1 = require("../RemoteSuite");
var console = require("../common/console");
var Pretty_1 = require("../reporters/Pretty");
var Runner_1 = require("../reporters/Runner");
var Simple_1 = require("../reporters/Simple");
var JUnit_1 = require("../reporters/JUnit");
var Cobertura_1 = require("../reporters/Cobertura");
var JsonCoverage_1 = require("../reporters/JsonCoverage");
var HtmlCoverage_1 = require("../reporters/HtmlCoverage");
var Lcov_1 = require("../reporters/Lcov");
var Benchmark_1 = require("../reporters/Benchmark");
var TeamCity_1 = require("../reporters/TeamCity");
var process = global_1.default.process;
var Node = (function (_super) {
tslib_1.__extends(Node, _super);
function Node(options) {
var _this = _super.call(this, {
basePath: process.cwd() + path_1.sep,
capabilities: {},
coverage: [],
environments: [],
functionalCoverage: true,
functionalSuites: [],
functionalTimeouts: {},
instrumenterOptions: {},
maxConcurrency: Infinity,
name: 'node',
reporters: [],
runInSync: false,
serveOnly: false,
serverPort: 9000,
serverUrl: '',
socketPort: 9001,
tunnel: 'selenium',
tunnelOptions: { tunnelId: String(Date.now()) }
}) || this;
_this._sourceMaps = istanbul_lib_source_maps_1.createSourceMapStore();
_this._instrumentedMaps = istanbul_lib_source_maps_1.createSourceMapStore();
_this._errorFormatter = new ErrorFormatter_1.default(_this);
_this._coverageMap = istanbul_lib_coverage_1.createCoverageMap();
_this.registerReporter('pretty', function (options) { return new Pretty_1.default(_this, options); });
_this.registerReporter('simple', function (options) { return new Simple_1.default(_this, options); });
_this.registerReporter('runner', function (options) { return new Runner_1.default(_this, options); });
_this.registerReporter('benchmark', function (options) { return new Benchmark_1.default(_this, options); });
_this.registerReporter('junit', function (options) { return new JUnit_1.default(_this, options); });
_this.registerReporter('jsoncoverage', function (options) { return new JsonCoverage_1.default(_this, options); });
_this.registerReporter('htmlcoverage', function (options) { return new HtmlCoverage_1.default(_this, options); });
_this.registerReporter('lcov', function (options) { return new Lcov_1.default(_this, options); });
_this.registerReporter('cobertura', function (options) { return new Cobertura_1.default(_this, options); });
_this.registerReporter('teamcity', function (options) { return new TeamCity_1.default(_this, options); });
_this.registerTunnel('null', NullTunnel_1.default);
_this.registerTunnel('selenium', SeleniumTunnel_1.default);
_this.registerTunnel('saucelabs', SauceLabsTunnel_1.default);
_this.registerTunnel('browserstack', BrowserStackTunnel_1.default);
_this.registerTunnel('testingbot', TestingBotTunnel_1.default);
_this.registerTunnel('cbt', CrossBrowserTestingTunnel_1.default);
if (options) {
_this.configure(options);
}
process.on('unhandledRejection', function (reason, promise) {
console.warn('Unhandled rejection:', reason, promise);
_this.emit('error', reason);
});
process.on('uncaughtException', function (reason) {
console.warn('Unhandled error:', reason);
_this.emit('error', reason);
});
_this.on('coverage', function (message) {
_this._coverageMap.merge(message.coverage);
});
return _this;
}
Object.defineProperty(Node.prototype, "coverageMap", {
get: function () {
return this._coverageMap;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Node.prototype, "environment", {
get: function () {
return 'node';
},
enumerable: true,
configurable: true
});
Object.defineProperty(Node.prototype, "instrumentedMapStore", {
get: function () {
return this._instrumentedMaps;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Node.prototype, "sourceMapStore", {
get: function () {
return this._sourceMaps;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Node.prototype, "hasCoveredFiles", {
get: function () {
return this._coverageFiles && this._coverageFiles.length > 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Node.prototype, "suites", {
get: function () {
var suites = [];
if (this._rootSuite.tests.length > 0) {
suites.push(this._rootSuite);
}
if (this._sessionSuites) {
suites.push.apply(suites, this._sessionSuites);
}
return suites;
},
enumerable: true,
configurable: true
});
Node.prototype.addSuite = function (factory) {
if (this._loadingFunctionalSuites) {
this._sessionSuites.forEach(factory);
}
else {
_super.prototype.addSuite.call(this, factory);
}
};
Node.prototype.getTunnel = function (name) {
return this.getPlugin("tunnel." + name);
};
Node.prototype.instrumentCode = function (code, filename) {
this.log('Instrumenting', filename);
var sourceMap = util_2.readSourceMap(filename, code);
if (sourceMap) {
this._sourceMaps.registerMap(filename, sourceMap);
}
try {
var instrumenter = this._instrumenter;
var newCode = instrumenter.instrumentSync(code, path_1.normalize(filename), sourceMap);
this._coverageMap.addFileCoverage(instrumenter.lastFileCoverage());
this._instrumentedMaps.registerMap(filename, instrumenter.lastSourceMap());
return newCode;
}
catch (error) {
this.emit('warning', "Error instrumenting " + filename + ": " + error.message);
return code;
}
};
Node.prototype.loadScript = function (script) {
var scripts = Array.isArray(script) ? script : [script];
try {
for (var _i = 0, scripts_1 = scripts; _i < scripts_1.length; _i++) {
var script_1 = scripts_1[_i];
var file = path_1.resolve(script_1);
if (fs_1.existsSync(file)) {
require(file);
}
else {
require(resolve_1.sync(script_1, { basedir: process.cwd() }));
}
}
}
catch (error) {
return Task_1.default.reject(error);
}
return Task_1.default.resolve();
};
Node.prototype.registerTunnel = function (name, Ctor) {
this.registerPlugin('tunnel', name, function () { return Ctor; });
};
Node.prototype.shouldInstrumentFile = function (filename) {
return this._coverageFiles
? this._coverageFiles.indexOf(filename) !== -1
: false;
};
Node.prototype._afterRun = function () {
var _this = this;
return _super.prototype._afterRun.call(this).finally(function () {
_this._removeInstrumentationHooks();
var promises = [];
if (_this.server) {
promises.push(_this.server
.stop()
.then(function () { return _this.emit('serverEnd', _this.server); }));
}
if (_this.tunnel) {
var tunnel_1 = _this.tunnel;
promises.push(tunnel_1
.stop()
.then(function () { return _this.emit('tunnelStop', { tunnel: tunnel_1 }); }));
}
return Promise.all(promises).then(function () { }, function (error) { return _this.emit('error', error); });
});
};
Node.prototype._beforeRun = function () {
var _this = this;
return _super.prototype._beforeRun.call(this).then(function () {
var config = _this.config;
var suite = _this._rootSuite;
suite.grep = config.grep;
suite.timeout = config.defaultTimeout;
suite.bail = config.bail;
if ((config.environments.filter(isRemoteEnvironment).length > 0 &&
config.functionalSuites.length +
config.browser.suites.length >
0) ||
config.serveOnly) {
var serverTask = new Task_1.default(function (resolve, reject) {
var server = new Server_2.default({
basePath: config.basePath,
executor: _this,
port: config.serverPort,
runInSync: config.runInSync,
socketPort: config.socketPort
});
server
.start()
.then(function () {
_this.server = server;
return _this.emit('serverStart', server);
})
.then(resolve, reject);
});
if (config.serveOnly) {
return serverTask.then(function () {
return new Task_1.default(function (resolve) {
process.on('SIGINT', function () {
resolve(true);
});
});
});
}
return serverTask
.then(function () {
var tunnelOptions = config.tunnelOptions;
if (config.tunnel === 'browserstack') {
var options = tunnelOptions;
options.servers = options.servers || [];
options.servers.push(config.serverUrl);
}
if ('proxy' in config && !('proxy' in tunnelOptions)) {
tunnelOptions.proxy = config.proxy;
}
var TunnelConstructor = _this.getTunnel(config.tunnel);
var tunnel = (_this.tunnel = new TunnelConstructor(_this.config.tunnelOptions));
tunnel.on('downloadprogress', function (progress) {
_this.emit('tunnelDownloadProgress', {
tunnel: tunnel,
progress: progress
});
});
tunnel.on('status', function (status) {
_this.emit('tunnelStatus', {
tunnel: tunnel,
status: status.status
});
});
config.capabilities = lang_1.deepMixin(tunnel.extraCapabilities, config.capabilities);
return _this._createSessionSuites().then(function () {
return tunnel
.start()
.then(function () {
return _this.emit('tunnelStart', { tunnel: tunnel });
});
});
})
.then(function () {
return false;
});
}
return false;
});
};
Node.prototype._createSessionSuites = function () {
var _this = this;
var tunnel = this.tunnel;
var config = this.config;
var leadfootServer = new Server_1.default(tunnel.clientUrl, {
proxy: 'proxy' in config ? config.proxy : tunnel.proxy
});
var executor = this;
var InitializedProxiedSession = (function (_super) {
tslib_1.__extends(InitializedProxiedSession, _super);
function InitializedProxiedSession() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.executor = executor;
_this.coverageVariable = config.coverageVariable;
_this.baseUrl = config.functionalBaseUrl || config.serverUrl;
return _this;
}
return InitializedProxiedSession;
}(ProxiedSession_1.default));
leadfootServer.sessionConstructor = InitializedProxiedSession;
return tunnel.getEnvironments().then(function (tunnelEnvironments) {
_this._sessionSuites = resolveEnvironments_1.default(config.capabilities, config.environments.filter(isRemoteEnvironment), tunnelEnvironments).map(function (environmentType) {
var session;
var suite = new Suite_1.default({
name: String(environmentType),
publishAfterSetup: true,
grep: config.grep,
bail: config.bail,
tests: [],
timeout: config.defaultTimeout,
executor: _this,
before: function () {
var _this = this;
executor.log('Creating session for', environmentType);
return leadfootServer
.createSession(environmentType)
.then(function (_session) {
session = _session;
_this.executor.log('Created session:', session.capabilities);
var remote = new Command_1.default(session);
remote.environmentType = new Environment_1.default(session.capabilities);
_this.remote = remote;
_this.sessionId = remote.session.sessionId;
_this.name = remote.environmentType.toString();
var timeouts = config.functionalTimeouts;
var promise = Promise.resolve();
if (timeouts.executeAsync != null) {
promise = promise.then(function () {
return remote.setExecuteAsyncTimeout(timeouts.executeAsync);
});
_this.executor.log('Set remote executeAsync timeout to ', timeouts.executeAsync);
}
if (timeouts.find != null) {
promise = promise.then(function () {
return remote.setFindTimeout(timeouts.find);
});
_this.executor.log('Set remote find timeout to ', timeouts.find);
}
if (timeouts.pageLoad != null) {
promise = promise.then(function () {
return remote.setPageLoadTimeout(timeouts.pageLoad);
});
_this.executor.log('Set remote pageLoad timeout to ', timeouts.pageLoad);
}
return promise;
});
},
after: function () {
var _this = this;
var remote = this.remote;
if (remote) {
var endSession = function () {
function hasError(suite) {
if (suite.error != null ||
suite.numFailedTests > 0) {
return true;
}
return suite.tests
.filter(Suite_1.isSuite)
.some(hasError);
}
return tunnel.sendJobState(remote.session.sessionId, { success: !hasError(_this) });
};
if (config.leaveRemoteOpen === true ||
(config.leaveRemoteOpen === 'fail' &&
this.numFailedTests > 0)) {
return endSession();
}
return remote.quit().finally(endSession);
}
}
});
if (config.browser.suites.length > 0) {
suite.add(new RemoteSuite_1.default());
}
return suite;
});
});
};
Node.prototype._loadFunctionalSuites = function () {
var _this = this;
this._loadingFunctionalSuites = true;
var suites = this.config.functionalSuites;
if (this.config.functionalCoverage !== false &&
!this._unhookRequire &&
this.hasCoveredFiles) {
this._setInstrumentationHooks();
}
return Task_1.default.resolve(this._loader(suites))
.then(function () {
_this.log('Loaded functional suites:', suites);
})
.finally(function () {
_this._loadingFunctionalSuites = false;
});
};
Node.prototype._loadSuites = function () {
if (!this.config.environments.some(isLocalEnvironment) ||
this.config.serveOnly) {
return Task_1.default.resolve();
}
if (this.hasCoveredFiles) {
this._setInstrumentationHooks();
}
return _super.prototype._loadSuites.call(this);
};
Node.prototype._processOption = function (key, value) {
if (!util_1.processOption(key, value, this.config, this)) {
_super.prototype._processOption.call(this, key, value);
}
};
Node.prototype._resolveConfig = function () {
var _this = this;
return _super.prototype._resolveConfig.call(this).then(function () {
var config = _this.config;
if (config.environments.length === 0) {
_this.log("Adding default 'node' environment");
config.environments.push({ browserName: 'node' });
}
if (!config.internPath) {
config.internPath = path_1.dirname(path_1.dirname(__dirname));
if (config.internPath.indexOf(process.cwd()) !== 0) {
config.internPath = path_1.dirname(resolve_1.sync('intern', {
basedir: process.cwd()
}));
}
}
['basePath', 'internPath'].forEach(function (property) {
var key = property;
config[key] = path_2.normalizePathEnding(path_1.resolve(config[key]), path_1.sep);
});
if (config.benchmarkConfig) {
config.reporters.push({
name: 'benchmark',
options: config.benchmarkConfig
});
}
_this._instrumentBasePath = config.basePath;
_this._coverageFiles = [];
if (config.coverage) {
_this._coverageFiles = util_2.expandFiles(config.coverage).map(function (path) {
return path_1.resolve(path);
});
}
if (!config.serverUrl) {
config.serverUrl = "http://localhost:" + config.serverPort + "/";
}
if (config.connectTimeout == null) {
config.connectTimeout = 30000;
}
if (config.heartbeatInterval == null) {
var idleTimeout = config.capabilities['idle-timeout'];
config.heartbeatInterval =
idleTimeout == null ? 60 : idleTimeout;
}
['serverUrl', 'functionalBaseUrl'].forEach(function (key) {
var property = key;
if (config[property]) {
config[property] = config[property].replace(/\/*$/, '/');
}
});
if (!config.capabilities.name) {
config.capabilities.name = 'intern';
}
if (config.capabilities['idle-timeout'] == null) {
config.capabilities['idle-timeout'] = config.heartbeatInterval;
}
var buildId = process.env.TRAVIS_COMMIT || process.env.BUILD_TAG;
if (buildId) {
config.capabilities.build = buildId;
}
config.functionalSuites = util_2.expandFiles(config.functionalSuites);
config.node.suites = util_2.expandFiles(config.suites.concat(config.node.suites));
config.browser.suites = util_2.expandFiles(config.suites.concat(config.browser.suites));
delete config.suites;
_this._instrumenter = istanbul_lib_instrument_1.createInstrumenter(lang_1.mixin({}, tslib_1.__assign({ coverageVariable: config.coverageVariable }, config.instrumenterOptions), {
preserveComments: true,
produceSourceMap: true
}));
});
};
Node.prototype._runTests = function () {
var _this = this;
var testTask;
return new Task_1.default(function (resolve, reject) {
if (_this._rootSuite.tests.length > 0) {
testTask = _this._rootSuite.run();
}
else {
testTask = Task_1.default.resolve();
}
testTask
.then(function () {
if (!_this._sessionSuites) {
return;
}
return _this._loadFunctionalSuites().then(function () { return (testTask = _this._runRemoteTests()); });
})
.then(resolve, reject);
}, function () {
if (testTask && testTask.state === 1) {
testTask.cancel();
}
}).finally(function () {
var coveredFiles = _this._coverageMap.files();
var uncoveredFiles = _this._coverageFiles.filter(function (filename) {
return coveredFiles.indexOf(filename) === -1;
});
uncoveredFiles.forEach(function (filename) {
try {
var code = fs_1.readFileSync(filename, { encoding: 'utf8' });
_this.instrumentCode(code, filename);
}
catch (_error) { }
});
});
};
Node.prototype._runRemoteTests = function () {
var _this = this;
var config = this.config;
var sessionSuites = this._sessionSuites;
var queue = new FunctionQueue(config.maxConcurrency || Infinity);
this.log('Running', sessionSuites.length, 'suites with maxConcurrency', config.maxConcurrency);
var runTask = new Task_1.default(function (resolve, reject) {
Task_1.default.all(sessionSuites.map(function (suite) {
_this.log('Queueing suite', suite.name);
return queue.enqueue(function () {
_this.log('Running suite', suite.name);
return suite.run();
});
})).then(resolve, reject);
}, function () {
_this.log('Canceling remote tests');
queue.clear();
});
return runTask.then(function () { }).finally(function () {
if (config.functionalCoverage !== false) {
_this.log('Emitting functional coverage');
return _this._emitCoverage('functional tests');
}
});
};
Node.prototype._setInstrumentationHooks = function () {
var _this = this;
istanbul_lib_hook_1.hookRunInThisContext(function (filename) { return _this.shouldInstrumentFile(filename); }, function (code, filename) { return _this.instrumentCode(code, filename); });
this._unhookRequire = istanbul_lib_hook_1.hookRequire(function (filename) { return _this.shouldInstrumentFile(filename); }, function (code, filename) { return _this.instrumentCode(code, filename); });
};
Node.prototype._removeInstrumentationHooks = function () {
istanbul_lib_hook_1.unhookRunInThisContext();
if (this._unhookRequire) {
this._unhookRequire();
this._unhookRequire = undefined;
}
};
return Node;
}(Executor_1.default));
exports.default = Node;
var FunctionQueue = (function () {
function FunctionQueue(maxConcurrency) {
this.maxConcurrency = maxConcurrency;
this.queue = [];
this.activeTasks = [];
this.funcTasks = [];
}
FunctionQueue.prototype.enqueue = function (func) {
var _this = this;
var funcTask = new Task_1.default(function (resolve, reject) {
_this.queue.push({ func: func, resolve: resolve, reject: reject });
});
this.funcTasks.push(funcTask);
if (this.activeTasks.length < this.maxConcurrency) {
this.next();
}
return funcTask;
};
FunctionQueue.prototype.clear = function () {
this.activeTasks.forEach(function (task) { return task.cancel(); });
this.funcTasks.forEach(function (task) { return task.cancel(); });
this.activeTasks = [];
this.funcTasks = [];
this.queue = [];
};
FunctionQueue.prototype.next = function () {
var _this = this;
if (this.queue.length > 0) {
var _a = this.queue.shift(), func = _a.func, resolve_2 = _a.resolve, reject = _a.reject;
var task_1 = func()
.then(resolve_2, reject)
.finally(function () {
util_1.pullFromArray(_this.activeTasks, task_1);
_this.next();
});
this.activeTasks.push(task_1);
}
};
return FunctionQueue;
}());
function isRemoteEnvironment(environment) {
return environment.browserName !== 'node';
}
function isLocalEnvironment(environment) {
return !isRemoteEnvironment(environment);
}
});
//# sourceMappingURL=Node.js.map