intern
Version:
Intern. A next-generation code testing stack for JavaScript.
261 lines • 11.5 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", "body-parser", "express", "ws", "@dojo/core/aspect", "@dojo/core/lang", "./common/util", "./node/util", "./middleware/instrument", "./middleware/unhandled", "./middleware/finalError", "./middleware/resolveSuites", "./middleware/post"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var body_parser_1 = require("body-parser");
var express = require("express");
var WebSocket = require("ws");
var aspect_1 = require("@dojo/core/aspect");
var lang_1 = require("@dojo/core/lang");
var util_1 = require("./common/util");
var util_2 = require("./node/util");
var instrument_1 = require("./middleware/instrument");
var unhandled_1 = require("./middleware/unhandled");
var finalError_1 = require("./middleware/finalError");
var resolveSuites_1 = require("./middleware/resolveSuites");
var post_1 = require("./middleware/post");
var Server = (function () {
function Server(options) {
lang_1.mixin(this, {
basePath: '.',
runInSync: false
}, options);
}
Object.defineProperty(Server.prototype, "stopped", {
get: function () {
return !this._httpServer;
},
enumerable: true,
configurable: true
});
Server.prototype.start = function () {
var _this = this;
var startupError;
var wsServer;
var httpServer;
return new Promise(function (resolve, reject) {
var app = (_this._app = express());
_this._sessions = {};
_this.executor.log('Listening for WebSocket connections on port', _this.socketPort);
wsServer = new WebSocket.Server({ port: _this.socketPort });
wsServer.on('connection', function (client) {
_this.executor.log('WebSocket connection opened:', client);
_this._handleWebSocket(client);
});
wsServer.on('error', function (error) {
if (util_2.isErrnoException(error) && error.code === 'EADDRINUSE') {
var err = new Error("Something is already listening on the websocket server port (" + _this.socketPort + ")");
err.code = error.code;
err.errno = error.errno;
reject(err);
}
else if (!_this._wsServer) {
reject(error);
}
else {
_this.executor.emit('error', error);
}
});
var context = Object.create(null, {
stopped: {
enumerable: true,
get: function () { return _this.stopped; }
},
basePath: {
enumerable: true,
get: function () { return _this.basePath; }
},
executor: {
enumerable: true,
get: function () { return _this.executor; }
},
handleMessage: {
enumerable: false,
writable: false,
configurable: false,
value: function (message) { return _this._handleMessage(message); }
}
});
Object.defineProperty(app.request, 'intern', {
enumerable: true,
get: function () { return context; }
});
Object.defineProperty(app.response, 'intern', {
enumerable: true,
get: function () { return context; }
});
app.use(body_parser_1.json({ limit: '1mb' }), body_parser_1.urlencoded({ extended: true }));
app.use(function (request, _response, next) {
_this.executor.log(request.method + " request for " + request.url);
return next();
});
var internPath = _this.executor.config.internPath;
app.use([
"/" + internPath + "__resolveSuites__",
'/__intern/__resolveSuites__'
], resolveSuites_1.default(context));
app.use('/__intern', express.static(internPath, { fallthrough: false }));
app.use(instrument_1.default(context), express.static(_this.basePath), post_1.default(context), unhandled_1.default(), finalError_1.default());
httpServer = app.listen(_this.port, resolve);
httpServer.on('error', function (error) {
if (util_2.isErrnoException(error) && error.code === 'EADDRINUSE') {
var err = new Error("Something is already listening on the server port (" + _this.port + ")");
err.code = error.code;
err.errno = error.errno;
reject(err);
}
else if (!_this._httpServer) {
reject(error);
}
else {
_this.executor.emit('error', error);
}
});
var sockets = [];
aspect_1.after(httpServer, 'close', function () {
var socket;
while ((socket = sockets.pop())) {
socket.destroy();
}
});
httpServer.on('connection', function (socket) {
sockets.push(socket);
_this.executor.log('HTTP connection opened,', sockets.length, 'open connections');
socket.on('close', function () {
var index = sockets.indexOf(socket);
index !== -1 && sockets.splice(index, 1);
_this.executor.log('HTTP connection closed,', sockets.length, 'open connections');
});
});
})
.then(function () {
_this._wsServer = wsServer;
_this._httpServer = httpServer;
})
.catch(function (error) {
startupError = error;
try {
wsServer.close();
}
catch (_error) { }
try {
httpServer.close();
}
catch (_error) { }
})
.then(function () {
if (startupError) {
throw startupError;
}
});
};
Server.prototype.stop = function () {
var _this = this;
this.executor.log('Stopping server...');
var promises = [];
if (this._app && this._httpServer) {
promises.push(new Promise(function (resolve) {
_this._httpServer.close(resolve);
}).then(function () {
_this.executor.log('Stopped http server');
_this._app = _this._httpServer = undefined;
}));
}
if (this._wsServer) {
promises.push(new Promise(function (resolve) {
_this._wsServer.close(resolve);
}).then(function () {
_this.executor.log('Stopped ws server');
_this._wsServer = undefined;
}));
}
return Promise.all(promises);
};
Server.prototype.subscribe = function (sessionId, listener) {
var listeners = this._getSession(sessionId).listeners;
listeners.push(listener);
return {
destroy: function () {
this.destroy = function () { };
util_1.pullFromArray(listeners, listener);
}
};
};
Server.prototype._getSession = function (sessionId) {
var session = this._sessions[sessionId];
if (!session) {
session = this._sessions[sessionId] = { listeners: [] };
}
return session;
};
Server.prototype._handleMessage = function (message) {
var _this = this;
this.executor.log('Processing message [', message.id, '] for ', message.sessionId, ': ', message.name);
var promise = this._publish(message);
if (getShouldWait(this.runInSync, message)) {
return promise;
}
promise.catch(function (error) {
_this.executor.emit('error', error);
});
return resolvedPromise;
};
Server.prototype._handleWebSocket = function (client) {
var _this = this;
client.on('message', function (data) {
_this.executor.log('Received WebSocket message');
var message = JSON.parse(data.toString());
_this._handleMessage(message)
.catch(function (error) { return _this.executor.emit('error', error); })
.then(function () {
_this.executor.log('Sending ack for [', message.id, ']');
client.send(JSON.stringify({ id: message.id }), function (error) {
if (error) {
_this.executor.emit('error', new Error("Error sending ack for [ " + message.id + " ]: " + error.message));
}
});
});
});
client.on('error', function (error) {
_this.executor.log('WebSocket client error:', error);
_this.executor.emit('error', error);
});
};
Server.prototype._publish = function (message) {
var listeners = this._getSession(message.sessionId).listeners;
return Promise.all(listeners.map(function (listener) { return listener(message.name, message.data); }));
};
return Server;
}());
exports.default = Server;
var resolvedPromise = Promise.resolve();
function getShouldWait(waitMode, message) {
var eventName = message.name;
if (eventName === 'runEnd') {
return false;
}
var shouldWait = false;
if (waitMode === 'fail') {
if ((eventName === 'testEnd' && message.data.error) ||
(eventName === 'suiteEnd' && message.data.error) ||
eventName === 'error') {
shouldWait = true;
}
}
else if (waitMode === true) {
shouldWait = true;
}
else if (Array.isArray(waitMode) && waitMode.indexOf(eventName) !== -1) {
shouldWait = true;
}
return shouldWait;
}
});
//# sourceMappingURL=Server.js.map