testcafe
Version:
Automated browser testing for the modern web development stack.
119 lines • 18.7 kB
JavaScript
;
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 pinkie_1 = __importDefault(require("pinkie"));
const time_limit_promise_1 = __importDefault(require("time-limit-promise"));
const promisify_event_1 = __importDefault(require("promisify-event"));
const lodash_1 = require("lodash");
const map_reverse_1 = __importDefault(require("map-reverse"));
const runtime_1 = require("../errors/runtime");
const types_1 = require("../errors/types");
const LOCAL_BROWSERS_READY_TIMEOUT = 2 * 60 * 1000;
const REMOTE_BROWSERS_READY_TIMEOUT = 6 * 60 * 1000;
class BrowserSet extends events_1.EventEmitter {
constructor(browserConnectionGroups) {
super();
this.RELEASE_TIMEOUT = 10000;
this.pendingReleases = [];
this.browserConnectionGroups = browserConnectionGroups;
this.browserConnections = lodash_1.flatten(browserConnectionGroups);
this.connectionsReadyTimeout = null;
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);
}
static async _waitIdle(bc) {
if (bc.idle || !bc.ready)
return;
await promisify_event_1.default(bc, 'idle');
}
static async _closeConnection(bc) {
if (bc.closed || !bc.ready)
return;
bc.close();
await promisify_event_1.default(bc, 'closed');
}
async _getReadyTimeout() {
const isLocalBrowser = connection => connection.provider.isLocalBrowser(connection.id, connection.browserInfo.browserName);
const remoteBrowsersExist = (await pinkie_1.default.all(this.browserConnections.map(isLocalBrowser))).indexOf(false) > -1;
return remoteBrowsersExist ? REMOTE_BROWSERS_READY_TIMEOUT : LOCAL_BROWSERS_READY_TIMEOUT;
}
_createPendingConnectionPromise(readyPromise, timeout, timeoutError) {
const timeoutPromise = new pinkie_1.default((_, reject) => {
this.connectionsReadyTimeout = setTimeout(() => reject(timeoutError), timeout);
});
return pinkie_1.default
.race([readyPromise, timeoutPromise])
.then(value => {
this.connectionsReadyTimeout.unref();
return value;
}, error => {
this.connectionsReadyTimeout.unref();
throw error;
});
}
async _waitConnectionsOpened() {
const connectionsReadyPromise = pinkie_1.default.all(this.browserConnections
.filter(bc => !bc.opened)
.map(bc => promisify_event_1.default(bc, 'opened')));
const timeoutError = new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotEstablishBrowserConnection);
const readyTimeout = await this._getReadyTimeout();
await this._createPendingConnectionPromise(connectionsReadyPromise, readyTimeout, timeoutError);
}
_checkForDisconnections() {
const disconnectedUserAgents = this.browserConnections
.filter(bc => bc.closed)
.map(bc => bc.userAgent);
if (disconnectedUserAgents.length)
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotRunAgainstDisconnectedBrowsers, disconnectedUserAgents.join(', '));
}
//API
static from(browserConnections) {
const browserSet = new BrowserSet(browserConnections);
const prepareConnection = pinkie_1.default.resolve()
.then(() => {
browserSet._checkForDisconnections();
return browserSet._waitConnectionsOpened();
})
.then(() => browserSet);
return pinkie_1.default
.race([
prepareConnection,
promisify_event_1.default(browserSet, 'error')
])
.catch(async (error) => {
await browserSet.dispose();
throw error;
});
}
releaseConnection(bc) {
if (this.browserConnections.indexOf(bc) < 0)
return pinkie_1.default.resolve();
lodash_1.pull(this.browserConnections, bc);
bc.removeListener('error', this.browserErrorHandler);
const appropriateStateSwitch = !bc.permanent ?
BrowserSet._closeConnection(bc) :
BrowserSet._waitIdle(bc);
const release = time_limit_promise_1.default(appropriateStateSwitch, this.RELEASE_TIMEOUT).then(() => lodash_1.pull(this.pendingReleases, release));
this.pendingReleases.push(release);
return release;
}
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.
if (this.connectionsReadyTimeout)
this.connectionsReadyTimeout.unref();
map_reverse_1.default(this.browserConnections, bc => this.releaseConnection(bc));
await pinkie_1.default.all(this.pendingReleases);
}
}
exports.default = BrowserSet;
module.exports = exports.default;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJvd3Nlci1zZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcnVubmVyL2Jyb3dzZXItc2V0LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsbUNBQXNDO0FBQ3RDLG9EQUE2QjtBQUM3Qiw0RUFBdUQ7QUFDdkQsc0VBQTZDO0FBQzdDLG1DQUF1RDtBQUN2RCw4REFBcUM7QUFDckMsK0NBQWlEO0FBQ2pELDJDQUFpRDtBQUVqRCxNQUFNLDRCQUE0QixHQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQ3BELE1BQU0sNkJBQTZCLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFFcEQsTUFBcUIsVUFBVyxTQUFRLHFCQUFZO0lBQ2hELFlBQWEsdUJBQXVCO1FBQ2hDLEtBQUssRUFBRSxDQUFDO1FBRVIsSUFBSSxDQUFDLGVBQWUsR0FBRyxLQUFLLENBQUM7UUFFN0IsSUFBSSxDQUFDLGVBQWUsR0FBRyxFQUFFLENBQUM7UUFFMUIsSUFBSSxDQUFDLHVCQUF1QixHQUFHLHVCQUF1QixDQUFDO1FBQ3ZELElBQUksQ0FBQyxrQkFBa0IsR0FBUSxnQkFBTyxDQUFDLHVCQUF1QixDQUFDLENBQUM7UUFFaEUsSUFBSSxDQUFDLHVCQUF1QixHQUFHLElBQUksQ0FBQztRQUVwQyxJQUFJLENBQUMsbUJBQW1CLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUU5RCxJQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUMsQ0FBQztRQUVoRixpR0FBaUc7UUFDakcsbUdBQW1HO1FBQ25HLElBQUksQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLGFBQUksQ0FBQyxDQUFDO0lB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QyxtQkFBbUIsQ0FBQyxDQUFDO1FBRXJELE1BQU0sc0JBQXNCLEdBQUcsQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDMUMsVUFBVSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDakMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUU3QixNQUFNLE9BQU8sR0FBRyw0QkFBcUIsQ0FBQyxzQkFBc0IsRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLGFBQU0sQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFFdEksSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFbkMsT0FBTyxPQUFPLENBQUM7SUFDbkIsQ0FBQztJQUVELEtBQUssQ0FBQyxPQUFPO1FBQ1QsZ0VBQWdFO1FBQ2hFLDhEQUE4RDtRQUM5RCw0RUFBNEU7UUFDNUUsNkRBQTZEO1FBQzdELElBQUksSUFBSSxDQUFDLHVCQUF1QjtZQUM1QixJQUFJLENBQUMsdUJBQXVCLENBQUMsS0FBSyxFQUFFLENBQUM7UUFFekMscUJBQVUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUV0RSxNQUFNLGdCQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUM1QyxDQUFDO0NBQ0o7QUE3SUQsNkJBNklDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRXZlbnRFbWl0dGVyIH0gZnJvbSAnZXZlbnRzJztcbmltcG9ydCBQcm9taXNlIGZyb20gJ3BpbmtpZSc7XG5pbXBvcnQgZ2V0VGltZUxpbWl0ZWRQcm9taXNlIGZyb20gJ3RpbWUtbGltaXQtcHJvbWlzZSc7XG5pbXBvcnQgcHJvbWlzaWZ5RXZlbnQgZnJvbSAncHJvbWlzaWZ5LWV2ZW50JztcbmltcG9ydCB7IG5vb3AsIHB1bGwgYXMgcmVtb3ZlLCBmbGF0dGVuIH0gZnJvbSAnbG9kYXNoJztcbmltcG9ydCBtYXBSZXZlcnNlIGZyb20gJ21hcC1yZXZlcnNlJztcbmltcG9ydCB7IEdlbmVyYWxFcnJvciB9IGZyb20gJy4uL2Vycm9ycy9ydW50aW1lJztcbmltcG9ydCB7IFJVTlRJTUVfRVJST1JTIH0gZnJvbSAnLi4vZXJyb3JzL3R5cGVzJztcblxuY29uc3QgTE9DQUxfQlJPV1NFUlNfUkVBRFlfVElNRU9VVCAgPSAyICogNjAgKiAxMDAwO1xuY29uc3QgUkVNT1RFX0JST1dTRVJTX1JFQURZX1RJTUVPVVQgPSA2ICogNjAgKiAxMDAwO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBCcm93c2VyU2V0IGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcbiAgICBjb25zdHJ1Y3RvciAoYnJvd3NlckNvbm5lY3Rpb25Hcm91cHMpIHtcbiAgICAgICAgc3VwZXIoKTtcblxuICAgICAgICB0aGlzLlJFTEVBU0VfVElNRU9VVCA9IDEwMDAwO1xuXG4gICAgICAgIHRoaXMucGVuZGluZ1JlbGVhc2VzID0gW107XG5cbiAgICAgICAgdGhpcy5icm93c2VyQ29ubmVjdGlvbkdyb3VwcyA9IGJyb3dzZXJDb25uZWN0aW9uR3JvdXBzO1xuICAgICAgICB0aGlzLmJyb3dzZXJDb25uZWN0aW9ucyAgICAgID0gZmxhdHRlbihicm93c2VyQ29ubmVjdGlvbkdyb3Vwcyk7XG5cbiAgICAgICAgdGhpcy5jb25uZWN0aW9uc1JlYWR5VGltZW91dCA9IG51bGw7XG5cbiAgICAgICAgdGhpcy5icm93c2VyRXJyb3JIYW5kbGVyID0gZXJyb3IgPT4gdGhpcy5lbWl0KCdlcnJvcicsIGVycm9yKTtcblxuICAgICAgICB0aGlzLmJyb3dzZXJDb25uZWN0aW9ucy5mb3JFYWNoKGJjID0+IGJjLm9uKCdlcnJvcicsIHRoaXMuYnJvd3NlckVycm9ySGFuZGxlcikpO1xuXG4gICAgICAgIC8vIE5PVEU6IFdlJ3JlIHNldHRpbmcgYW4gZW1wdHkgZXJyb3IgaGFuZGxlciwgYmVjYXVzZSBOb2RlIGtpbGxzIHRoZSBwcm9jZXNzIG9uIGFuICdlcnJvcicgZXZlbnRcbiAgICAgICAgLy8gaWYgdGhlcmUgaXMgbm8gaGFuZGxlci4gU2VlOiBodHRwczovL25vZGVqcy5vcmcvYXBpL2V2ZW50cy5odG1sI2V2ZW50c19jbGFzc19ldmVudHNfZXZlbnRlbWl0dGVyXG4gICAgICAgIHRoaXMub24oJ2Vycm9yJywgbm9vcCk7XG4gICAgfVxuXG4gICAgc3RhdGljIGFzeW5jIF93YWl0SWRsZSAoYmMpIHtcbiAgICAgICAgaWYgKGJjLmlkbGUgfHwgIWJjLnJlYWR5KVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIGF3YWl0IHByb21pc2lmeUV2ZW50KGJjLCAnaWRsZScpO1xuICAgIH1cblxuICAgIHN0YXRpYyBhc3luYyBfY2xvc2VDb25uZWN0aW9uIChiYykge1xuICAgICAgICBpZiAoYmMuY2xvc2VkIHx8ICFiYy5yZWFkeSlcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICBiYy5jbG9zZSgpO1xuXG4gICAgICAgIGF3YWl0IHByb21pc2lmeUV2ZW50KGJjLCAnY2xvc2VkJyk7XG4gICAgfVxuXG4gICAgYXN5bmMgX2dldFJlYWR5VGltZW91dCAoKSB7XG4gICAgICAgIGNvbnN0IGlzTG9jYWxCcm93c2VyICAgICAgPSBjb25uZWN0aW9uID0+IGNvbm5lY3Rpb24ucHJvdmlkZXIuaXNMb2NhbEJyb3dzZXIoY29ubmVjdGlvbi5pZCwgY29ubmVjdGlvbi5icm93c2VySW5mby5icm93c2VyTmFtZSk7XG4gICAgICAgIGNvbnN0IHJlbW90ZUJyb3dzZXJzRXhpc3QgPSAoYXdhaXQgUHJvbWlzZS5hbGwodGhpcy5icm93c2VyQ29ubmVjdGlvbnMubWFwKGlzTG9jYWxCcm93c2VyKSkpLmluZGV4T2YoZmFsc2UpID4gLTE7XG5cbiAgICAgICAgcmV0dXJuIHJlbW90ZUJyb3dzZXJzRXhpc3QgPyBSRU1PVEVfQlJPV1NFUlNfUkVBRFlfVElNRU9VVCA6IExPQ0FMX0JST1dTRVJTX1JFQURZX1RJTUVPVVQ7XG4gICAgfVxuXG4gICAgX2NyZWF0ZVBlbmRpbmdDb25uZWN0aW9uUHJvbWlzZSAocmVhZHlQcm9taXNlLCB0aW1lb3V0LCB0aW1lb3V0RXJyb3IpIHtcbiAgICAgICAgY29uc3QgdGltZW91dFByb21pc2UgPSBuZXcgUHJvbWlzZSgoXywgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb25zUmVhZHlUaW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QodGltZW91dEVycm9yKSwgdGltZW91dCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiBQcm9taXNlXG4gICAgICAgICAgICAucmFjZShbcmVhZHlQcm9taXNlLCB0aW1lb3V0UHJvbWlzZV0pXG4gICAgICAgICAgICAudGhlbihcbiAgICAgICAgICAgICAgICB2YWx1ZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbnNSZWFkeVRpbWVvdXQudW5yZWYoKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgZXJyb3IgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb25zUmVhZHlUaW1lb3V0LnVucmVmKCk7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgfVxuXG4gICAgYXN5bmMgX3dhaXRDb25uZWN0aW9uc09wZW5lZCAoKSB7XG4gICAgICAgIGNvbnN0IGNvbm5lY3Rpb25zUmVhZHlQcm9taXNlID0gUHJvbWlzZS5hbGwoXG4gICAgICAgICAgICB0aGlzLmJyb3dzZXJDb25uZWN0aW9uc1xuICAgICAgICAgICAgICAgIC5maWx0ZXIoYmMgPT4gIWJjLm9wZW5lZClcbiAgICAgICAgICAgICAgICAubWFwKGJjID0+IHByb21pc2lmeUV2ZW50KGJjLCAnb3BlbmVkJykpXG4gICAgICAgICk7XG5cbiAgICAgICAgY29uc3QgdGltZW91dEVycm9yID0gbmV3IEdlbmVyYWxFcnJvcihSVU5USU1FX0VSUk9SUy5jYW5ub3RFc3RhYmxpc2hCcm93c2VyQ29ubmVjdGlvbik7XG4gICAgICAgIGNvbnN0IHJlYWR5VGltZW91dCA9IGF3YWl0IHRoaXMuX2dldFJlYWR5VGltZW91dCgpO1xuXG4gICAgICAgIGF3YWl0IHRoaXMuX2NyZWF0ZVBlbmRpbmdDb25uZWN0aW9uUHJvbWlzZShjb25uZWN0aW9uc1JlYWR5UHJvbWlzZSwgcmVhZHlUaW1lb3V0LCB0aW1lb3V0RXJyb3IpO1xuICAgIH1cblxuICAgIF9jaGVja0ZvckRpc2Nvbm5lY3Rpb25zICgpIHtcbiAgICAgICAgY29uc3QgZGlzY29ubmVjdGVkVXNlckFnZW50cyA9IHRoaXMuYnJvd3NlckNvbm5lY3Rpb25zXG4gICAgICAgICAgICAuZmlsdGVyKGJjID0+IGJjLmNsb3NlZClcbiAgICAgICAgICAgIC5tYXAoYmMgPT4gYmMudXNlckFnZW50KTtcblxuICAgICAgICBpZiAoZGlzY29ubmVjdGVkVXNlckFnZW50cy5sZW5ndGgpXG4gICAgICAgICAgICB0aHJvdyBuZXcgR2VuZXJhbEVycm9yKFJVTlRJTUVfRVJST1JTLmNhbm5vdFJ1bkFnYWluc3REaXNjb25uZWN0ZWRCcm93c2VycywgZGlzY29ubmVjdGVkVXNlckFnZW50cy5qb2luKCcsICcpKTtcbiAgICB9XG5cblxuICAgIC8vQVBJXG4gICAgc3RhdGljIGZyb20gKGJyb3dzZXJDb25uZWN0aW9ucykge1xuICAgICAgICBjb25zdCBicm93c2VyU2V0ID0gbmV3IEJyb3dzZXJTZXQoYnJvd3NlckNvbm5lY3Rpb25zKTtcblxuICAgICAgICBjb25zdCBwcmVwYXJlQ29ubmVjdGlvbiA9IFByb21pc2UucmVzb2x2ZSgpXG4gICAgICAgICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgYnJvd3NlclNldC5fY2hlY2tGb3JEaXNjb25uZWN0aW9ucygpO1xuICAgICAgICAgICAgICAgIHJldHVybiBicm93c2VyU2V0Ll93YWl0Q29ubmVjdGlvbnNPcGVuZWQoKTtcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAudGhlbigoKSA9PiBicm93c2VyU2V0KTtcblxuICAgICAgICByZXR1cm4gUHJvbWlzZVxuICAgICAgICAgICAgLnJhY2UoW1xuICAgICAgICAgICAgICAgIHByZXBhcmVDb25uZWN0aW9uLFxuICAgICAgICAgICAgICAgIHByb21pc2lmeUV2ZW50KGJyb3dzZXJTZXQsICdlcnJvcicpXG4gICAgICAgICAgICBdKVxuICAgICAgICAgICAgLmNhdGNoKGFzeW5jIGVycm9yID0+IHtcbiAgICAgICAgICAgICAgICBhd2FpdCBicm93c2VyU2V0LmRpc3Bvc2UoKTtcblxuICAgICAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmVsZWFzZUNvbm5lY3Rpb24gKGJjKSB7XG4gICAgICAgIGlmICh0aGlzLmJyb3dzZXJDb25uZWN0aW9ucy5pbmRleE9mKGJjKSA8IDApXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG5cbiAgICAgICAgcmVtb3ZlKHRoaXMuYnJvd3NlckNvbm5lY3Rpb25zLCBiYyk7XG5cbiAgICAgICAgYmMucmVtb3ZlTGlzdGVuZXIoJ2Vycm9yJywgdGhpcy5icm93c2VyRXJyb3JIYW5kbGVyKTtcblxuICAgICAgICBjb25zdCBhcHByb3ByaWF0ZVN0YXRlU3dpdGNoID0gIWJjLnBlcm1hbmVudCA/XG4gICAgICAgICAgICBCcm93c2VyU2V0Ll9jbG9zZUNvbm5lY3Rpb24oYmMpIDpcbiAgICAgICAgICAgIEJyb3dzZXJTZXQuX3dhaXRJZGxlKGJjKTtcblxuICAgICAgICBjb25zdCByZWxlYXNlID0gZ2V0VGltZUxpbWl0ZWRQcm9taXNlKGFwcHJvcHJpYXRlU3RhdGVTd2l0Y2gsIHRoaXMuUkVMRUFTRV9USU1FT1VUKS50aGVuKCgpID0+IHJlbW92ZSh0aGlzLnBlbmRpbmdSZWxlYXNlcywgcmVsZWFzZSkpO1xuXG4gICAgICAgIHRoaXMucGVuZGluZ1JlbGVhc2VzLnB1c2gocmVsZWFzZSk7XG5cbiAgICAgICAgcmV0dXJuIHJlbGVhc2U7XG4gICAgfVxuXG4gICAgYXN5bmMgZGlzcG9zZSAoKSB7XG4gICAgICAgIC8vIE5PVEU6IFdoZW4gYnJvd3NlckNvbm5lY3Rpb24gaXMgY2FuY2VsbGVkLCBpdCBpcyByZW1vdmVkIGZyb21cbiAgICAgICAgLy8gdGhlIHRoaXMuY29ubmVjdGlvbnMgYXJyYXksIHdoaWNoIGxlYWRzIHRvIHNoaWZ0aW5nIGluZGV4ZXNcbiAgICAgICAgLy8gdG93YXJkcyB0aGUgYmVnaW5uaW5nLiBTbywgd2UgbXVzdCBjb3B5IHRoZSBhcnJheSBpbiBvcmRlciB0byBpdGVyYXRlIGl0LFxuICAgICAgICAvLyBvciB3ZSBjYW4gcGVyZm9ybSBpdGVyYXRpb24gZnJvbSB0aGUgZW5kIHRvIHRoZSBiZWdpbm5pbmcuXG4gICAgICAgIGlmICh0aGlzLmNvbm5lY3Rpb25zUmVhZHlUaW1lb3V0KVxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uc1JlYWR5VGltZW91dC51bnJlZigpO1xuXG4gICAgICAgIG1hcFJldmVyc2UodGhpcy5icm93c2VyQ29ubmVjdGlvbnMsIGJjID0+IHRoaXMucmVsZWFzZUNvbm5lY3Rpb24oYmMpKTtcblxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbCh0aGlzLnBlbmRpbmdSZWxlYXNlcyk7XG4gICAgfVxufVxuIl19