intern
Version:
Intern. A next-generation code testing stack for JavaScript.
284 lines • 12.8 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", "body-parser", "express", "ws", "./common/util", "./node/util", "./middleware/instrument", "./middleware/unhandled", "./middleware/finalError", "./middleware/resolveSuites", "./middleware/post", "./middleware/filterUrl"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var body_parser_1 = require("body-parser");
var express_1 = tslib_1.__importDefault(require("express"));
var ws_1 = tslib_1.__importDefault(require("ws"));
var util_1 = require("./common/util");
var util_2 = require("./node/util");
var instrument_1 = tslib_1.__importDefault(require("./middleware/instrument"));
var unhandled_1 = tslib_1.__importDefault(require("./middleware/unhandled"));
var finalError_1 = tslib_1.__importDefault(require("./middleware/finalError"));
var resolveSuites_1 = tslib_1.__importDefault(require("./middleware/resolveSuites"));
var post_1 = tslib_1.__importDefault(require("./middleware/post"));
var filterUrl_1 = tslib_1.__importDefault(require("./middleware/filterUrl"));
var Server = (function () {
function Server(options) {
Object.assign(this, {
basePath: '.',
runInSync: false,
}, options);
this._wsPingTimers = [];
}
Object.defineProperty(Server.prototype, "stopped", {
get: function () {
return !this._httpServer;
},
enumerable: false,
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_1.default());
_this._sessions = {};
_this.executor.log('Listening for WebSocket connections on port', _this.socketPort);
var clientCount = 0;
wsServer = new ws_1.default.Server({ port: _this.socketPort });
wsServer.on('connection', function (client) {
clientCount++;
_this.executor.log("WebSocket client " + clientCount + " connected");
_this._handleWebSocket(client, clientCount);
});
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);
}
});
wsServer.on('close', function () {
for (var _i = 0, _a = _this._wsPingTimers; _i < _a.length; _i++) {
var timer = _a[_i];
clearInterval(timer);
}
});
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(filterUrl_1.default());
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_1.default.static(internPath, { fallthrough: false }));
app.use(instrument_1.default(context), express_1.default.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 = [];
httpServer.on('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, id) {
var _this = this;
var isAlive = true;
var timer = setInterval(function () {
if (!isAlive) {
client.terminate();
clearInterval(timer);
}
isAlive = false;
_this.executor.log('Sending ping to remote', id);
client.ping();
}, 30000);
this._wsPingTimers.push(timer);
client.on('pong', function () {
_this.executor.log('Received pong from remote', id);
isAlive = true;
});
client.on('message', function (data) {
_this.executor.log('Received WebSocket message from', id);
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, '] to', id);
client.send(JSON.stringify({ id: message.id }), function (error) {
if (error) {
_this.executor.emit('error', new Error("Error sending ack for [ " + message.id + " ] to " + id + ": " + error.message));
}
});
});
});
client.on('error', function (error) {
_this.executor.log("WebSocket client error for " + id + ":", error);
_this.executor.emit('error', error);
clearInterval(timer);
});
};
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