118 lines
21 KiB
JavaScript
118 lines
21 KiB
JavaScript
|
"use strict";
|
||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
|
};
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const events_1 = require("events");
|
||
|
const time_limit_promise_1 = __importDefault(require("time-limit-promise"));
|
||
|
const promisify_event_1 = __importDefault(require("promisify-event"));
|
||
|
const lodash_1 = require("lodash");
|
||
|
const runtime_1 = require("../errors/runtime");
|
||
|
const types_1 = require("../errors/types");
|
||
|
const status_1 = __importDefault(require("../browser/connection/status"));
|
||
|
const get_hints_1 = __importDefault(require("../browser/connection/get-hints"));
|
||
|
const string_1 = require("../utils/string");
|
||
|
const RELEASE_TIMEOUT = 10000;
|
||
|
const COUNT_OWN_AND_OUTER_LISTENERS = 3;
|
||
|
class BrowserSet extends events_1.EventEmitter {
|
||
|
constructor(browserConnectionGroups, options) {
|
||
|
super();
|
||
|
this._pendingReleases = [];
|
||
|
this.browserConnectionGroups = browserConnectionGroups;
|
||
|
this._browserConnections = (0, lodash_1.flatten)(browserConnectionGroups);
|
||
|
this._options = options;
|
||
|
this._browserErrorHandler = (error) => this.emit('error', error);
|
||
|
this._browserConnections.forEach(bc => bc.on('error', this._browserErrorHandler));
|
||
|
// NOTE: We're setting an empty error handler, because Node kills the process on an 'error' event
|
||
|
// if there is no handler. See: https://nodejs.org/api/events.html#events_class_events_eventemitter
|
||
|
this.on('error', lodash_1.noop);
|
||
|
this.setMaxListeners(COUNT_OWN_AND_OUTER_LISTENERS + this._browserConnections.length);
|
||
|
}
|
||
|
static async _waitIdle(bc) {
|
||
|
if (bc.idle || !bc.isReady())
|
||
|
return;
|
||
|
await (0, promisify_event_1.default)(bc, 'idle');
|
||
|
}
|
||
|
static async _closeConnection(bc) {
|
||
|
if (bc.status === status_1.default.closed || bc.status === status_1.default.closing)
|
||
|
return;
|
||
|
await bc.close();
|
||
|
}
|
||
|
async _waitConnectionOpened(bc) {
|
||
|
const openedTimeout = this._options.browserInitTimeout || await bc.getDefaultBrowserInitTimeout();
|
||
|
const timeoutErr = new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotEstablishBrowserConnection);
|
||
|
const openedOrError = Promise.race([
|
||
|
(0, promisify_event_1.default)(this, 'error'),
|
||
|
(0, promisify_event_1.default)(bc, 'opened'),
|
||
|
]);
|
||
|
return (0, time_limit_promise_1.default)(openedOrError, openedTimeout, { rejectWith: timeoutErr });
|
||
|
}
|
||
|
async _waitConnectionsOpened() {
|
||
|
return Promise.all(this._browserConnections
|
||
|
.filter(bc => bc.status !== status_1.default.opened)
|
||
|
.map(notOpenedConnection => this._waitConnectionOpened(notOpenedConnection)));
|
||
|
}
|
||
|
_checkForDisconnections() {
|
||
|
const disconnectedUserAgents = this._browserConnections
|
||
|
.filter(bc => bc.status === status_1.default.closed)
|
||
|
.map(bc => bc.userAgent);
|
||
|
if (disconnectedUserAgents.length)
|
||
|
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotRunAgainstDisconnectedBrowsers, disconnectedUserAgents.join(', '));
|
||
|
}
|
||
|
async prepareConnections() {
|
||
|
await this._checkForDisconnections();
|
||
|
await this._waitConnectionsOpened();
|
||
|
}
|
||
|
// NOTE: creates and prepares BrowserSet instance with given browser connections
|
||
|
static async from(browserConnections, opts) {
|
||
|
const browserSet = new BrowserSet(browserConnections, opts);
|
||
|
try {
|
||
|
const prepareConnections = browserSet.prepareConnections();
|
||
|
const browserSetError = (0, promisify_event_1.default)(browserSet, 'error');
|
||
|
await Promise.race([prepareConnections, browserSetError]);
|
||
|
return browserSet;
|
||
|
}
|
||
|
catch (e) {
|
||
|
const finalError = e.code === types_1.RUNTIME_ERRORS.cannotEstablishBrowserConnection
|
||
|
? browserSet.createBrowserConnectionError(e)
|
||
|
: e;
|
||
|
await browserSet.dispose();
|
||
|
throw finalError;
|
||
|
}
|
||
|
}
|
||
|
createBrowserConnectionError(error) {
|
||
|
const notOpenedConnections = this._browserConnections.filter(bc => bc.status !== status_1.default.opened);
|
||
|
const numOfAllConnections = this._browserConnections.length;
|
||
|
const numOfNotOpenedConnections = notOpenedConnections.length;
|
||
|
const listOfNotOpenedConnections = (0, string_1.createList)(notOpenedConnections.map(bc => bc.browserInfo.alias));
|
||
|
const listOfHints = (0, string_1.createList)((0, get_hints_1.default)(this._browserConnections, this._options));
|
||
|
return new runtime_1.BrowserConnectionError(error.message, numOfNotOpenedConnections, numOfAllConnections, listOfNotOpenedConnections, listOfHints);
|
||
|
}
|
||
|
async releaseConnection(bc) {
|
||
|
if (!this._browserConnections.includes(bc))
|
||
|
return;
|
||
|
(0, lodash_1.pull)(this._browserConnections, bc);
|
||
|
bc.removeListener('error', this._browserErrorHandler);
|
||
|
const appropriateStateSwitch = bc.permanent ?
|
||
|
BrowserSet._waitIdle(bc) :
|
||
|
BrowserSet._closeConnection(bc);
|
||
|
const release = (0, time_limit_promise_1.default)(appropriateStateSwitch, RELEASE_TIMEOUT)
|
||
|
.then(() => (0, lodash_1.pull)(this._pendingReleases, release));
|
||
|
this._pendingReleases.push(release);
|
||
|
return release; // eslint-disable-line consistent-return
|
||
|
}
|
||
|
async dispose() {
|
||
|
// NOTE: When browserConnection is cancelled, it is removed from
|
||
|
// the this.connections array, which leads to shifting indexes
|
||
|
// towards the beginning. So, we must copy the array in order to iterate it,
|
||
|
// or we can perform iteration from the end to the beginning.
|
||
|
await this._browserConnections.reduceRight(async (_, bc) => {
|
||
|
await this.releaseConnection(bc);
|
||
|
return bc;
|
||
|
}, Promise.resolve({}));
|
||
|
await Promise.all(this._pendingReleases);
|
||
|
}
|
||
|
}
|
||
|
exports.default = BrowserSet;
|
||
|
module.exports = exports.default;
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJvd3Nlci1zZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcnVubmVyL2Jyb3dzZXItc2V0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsbUNBQXNDO0FBQ3RDLDRFQUF1RDtBQUN2RCxzRUFBNkM7QUFDN0MsbUNBSWdCO0FBRWhCLCtDQUF5RTtBQUN6RSwyQ0FBaUQ7QUFFakQsMEVBQW1FO0FBRW5FLGdGQUF3RTtBQUN4RSw0Q0FBNkM7QUFFN0MsTUFBTSxlQUFlLEdBQUcsS0FBSyxDQUFDO0FBRTlCLE1BQU0sNkJBQTZCLEdBQUcsQ0FBQyxDQUFDO0FBRXhDLE1BQXFCLFVBQVcsU0FBUSxxQkFBWTtJQU9oRCxZQUFvQix1QkFBOEMsRUFBRSxPQUEwQjtRQUMxRixLQUFLLEVBQUUsQ0FBQztRQUVSLElBQUksQ0FBQyxnQkFBZ0IsR0FBVyxFQUFFLENBQUM7UUFDbkMsSUFBSSxDQUFDLHVCQUF1QixHQUFJLHVCQUF1QixDQUFDO1FBQ3hELElBQUksQ0FBQyxtQkFBbUIsR0FBUSxJQUFBLGdCQUFPLEVBQUMsdUJBQXVCLENBQUMsQ0FBQztRQUNqRSxJQUFJLENBQUMsUUFBUSxHQUFtQixPQUFPLENBQUM7UUFFeEMsSUFBSSxDQUFDLG9CQUFvQixHQUFHLENBQUMsS0FBWSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUV4RSxJQUFJLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUMsQ0FBQztRQUVsRixpR0FBaUc7UUFDakcsbUdBQW1HO1FBQ25HLElBQUksQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLGFBQUksQ0FBQyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxlQUFlLENBQUMsNkJBQTZCLEdBQUcsSUFBSSxD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
|