testcafe
Version:
Automated browser testing for the modern web development stack.
272 lines • 36.5 kB
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 pinkie_1 = __importDefault(require("pinkie"));
const mustache_1 = __importDefault(require("mustache"));
const lodash_1 = require("lodash");
const useragent_1 = require("useragent");
const read_file_relative_1 = require("read-file-relative");
const promisify_event_1 = __importDefault(require("promisify-event"));
const nanoid_1 = __importDefault(require("nanoid"));
const command_1 = __importDefault(require("./command"));
const status_1 = __importDefault(require("./status"));
const runtime_1 = require("../../errors/runtime");
const types_1 = require("../../errors/types");
const IDLE_PAGE_TEMPLATE = read_file_relative_1.readSync('../../client/browser/idle-page/index.html.mustache');
const connections = {};
class BrowserConnection extends events_1.EventEmitter {
constructor(gateway, browserInfo, permanent) {
super();
this.HEARTBEAT_TIMEOUT = 2 * 60 * 1000;
this.BROWSER_RESTART_TIMEOUT = 60 * 1000;
this.id = BrowserConnection._generateId();
this.jobQueue = [];
this.initScriptsQueue = [];
this.browserConnectionGateway = gateway;
this.disconnectionPromise = null;
this.testRunAborted = false;
this.browserInfo = browserInfo;
this.browserInfo.userAgent = '';
this.browserInfo.userAgentProviderMetaInfo = '';
this.provider = browserInfo.provider;
this.permanent = permanent;
this.closing = false;
this.closed = false;
this.ready = false;
this.opened = false;
this.idle = true;
this.heartbeatTimeout = null;
this.pendingTestRunUrl = null;
this.url = `${gateway.domain}/browser/connect/${this.id}`;
this.idleUrl = `${gateway.domain}/browser/idle/${this.id}`;
this.forcedIdleUrl = `${gateway.domain}/browser/idle-forced/${this.id}`;
this.initScriptUrl = `${gateway.domain}/browser/init-script/${this.id}`;
this.heartbeatRelativeUrl = `/browser/heartbeat/${this.id}`;
this.statusRelativeUrl = `/browser/status/${this.id}`;
this.statusDoneRelativeUrl = `/browser/status-done/${this.id}`;
this.heartbeatUrl = `${gateway.domain}${this.heartbeatRelativeUrl}`;
this.statusUrl = `${gateway.domain}${this.statusRelativeUrl}`;
this.statusDoneUrl = `${gateway.domain}${this.statusDoneRelativeUrl}`;
this.on('error', () => {
this._forceIdle();
this.close();
});
connections[this.id] = this;
this.browserConnectionGateway.startServingConnection(this);
process.nextTick(() => this._runBrowser());
}
static _generateId() {
return nanoid_1.default(7);
}
async _runBrowser() {
try {
await this.provider.openBrowser(this.id, this.url, this.browserInfo.browserName);
if (!this.ready)
await promisify_event_1.default(this, 'ready');
this.opened = true;
this.emit('opened');
}
catch (err) {
this.emit('error', new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.unableToOpenBrowser, this.browserInfo.providerName + ':' + this.browserInfo.browserName, err.stack));
}
}
async _closeBrowser() {
if (!this.idle)
await promisify_event_1.default(this, 'idle');
try {
await this.provider.closeBrowser(this.id);
}
catch (err) {
// NOTE: A warning would be really nice here, but it can't be done while log is stored in a task.
}
}
_forceIdle() {
if (!this.idle) {
this.switchingToIdle = false;
this.idle = true;
this.emit('idle');
}
}
_createBrowserDisconnectedError() {
return new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.browserDisconnected, this.userAgent);
}
_waitForHeartbeat() {
this.heartbeatTimeout = setTimeout(() => {
const err = this._createBrowserDisconnectedError();
this.opened = false;
this.testRunAborted = true;
this.emit('disconnected', err);
this._restartBrowserOnDisconnect(err);
}, this.HEARTBEAT_TIMEOUT);
}
async _getTestRunUrl(needPopNext) {
if (needPopNext || !this.pendingTestRunUrl)
this.pendingTestRunUrl = await this._popNextTestRunUrl();
return this.pendingTestRunUrl;
}
async _popNextTestRunUrl() {
while (this.hasQueuedJobs && !this.currentJob.hasQueuedTestRuns)
this.jobQueue.shift();
return this.hasQueuedJobs ? await this.currentJob.popNextTestRunUrl(this) : null;
}
static getById(id) {
return connections[id] || null;
}
async _restartBrowser() {
this.ready = false;
this._forceIdle();
let resolveTimeout = null;
let isTimeoutExpired = false;
let timeout = null;
const restartPromise = this._closeBrowser()
.then(() => this._runBrowser());
const timeoutPromise = new pinkie_1.default(resolve => {
resolveTimeout = resolve;
timeout = setTimeout(() => {
isTimeoutExpired = true;
resolve();
}, this.BROWSER_RESTART_TIMEOUT);
});
pinkie_1.default.race([restartPromise, timeoutPromise])
.then(() => {
clearTimeout(timeout);
if (isTimeoutExpired)
this.emit('error', this._createBrowserDisconnectedError());
else
resolveTimeout();
});
}
_restartBrowserOnDisconnect(err) {
let resolveFn = null;
let rejectFn = null;
this.disconnectionPromise = new pinkie_1.default((resolve, reject) => {
resolveFn = resolve;
rejectFn = () => {
reject(err);
};
setTimeout(() => {
rejectFn();
});
})
.then(() => {
return this._restartBrowser();
})
.catch(e => {
this.emit('error', e);
});
this.disconnectionPromise.resolve = resolveFn;
this.disconnectionPromise.reject = rejectFn;
}
async processDisconnection(disconnectionThresholdExceedeed) {
const { resolve, reject } = this.disconnectionPromise;
if (disconnectionThresholdExceedeed)
reject();
else
resolve();
}
addWarning(...args) {
if (this.currentJob)
this.currentJob.warningLog.addWarning(...args);
}
setProviderMetaInfo(str) {
this.browserInfo.userAgentProviderMetaInfo = str;
}
get userAgent() {
let userAgent = this.browserInfo.userAgent;
if (this.browserInfo.userAgentProviderMetaInfo)
userAgent += ` (${this.browserInfo.userAgentProviderMetaInfo})`;
return userAgent;
}
get hasQueuedJobs() {
return !!this.jobQueue.length;
}
get currentJob() {
return this.jobQueue[0];
}
// API
runInitScript(code) {
return new pinkie_1.default(resolve => this.initScriptsQueue.push({ code, resolve }));
}
addJob(job) {
this.jobQueue.push(job);
}
removeJob(job) {
lodash_1.pull(this.jobQueue, job);
}
close() {
if (this.closed || this.closing)
return;
this.closing = true;
this._closeBrowser()
.then(() => {
this.browserConnectionGateway.stopServingConnection(this);
clearTimeout(this.heartbeatTimeout);
delete connections[this.id];
this.ready = false;
this.closed = true;
this.emit('closed');
});
}
establish(userAgent) {
this.ready = true;
const parsedUserAgent = useragent_1.parse(userAgent);
this.browserInfo.userAgent = parsedUserAgent.toString();
this.browserInfo.fullUserAgent = userAgent;
this.browserInfo.parsedUserAgent = parsedUserAgent;
this._waitForHeartbeat();
this.emit('ready');
}
heartbeat() {
clearTimeout(this.heartbeatTimeout);
this._waitForHeartbeat();
return {
code: this.closing ? status_1.default.closing : status_1.default.ok,
url: this.closing ? this.idleUrl : ''
};
}
renderIdlePage() {
return mustache_1.default.render(IDLE_PAGE_TEMPLATE, {
userAgent: this.userAgent,
statusUrl: this.statusUrl,
heartbeatUrl: this.heartbeatUrl,
initScriptUrl: this.initScriptUrl,
retryTestPages: !!this.browserConnectionGateway.retryTestPages
});
}
getInitScript() {
const initScriptPromise = this.initScriptsQueue[0];
return { code: initScriptPromise ? initScriptPromise.code : null };
}
handleInitScriptResult(data) {
const initScriptPromise = this.initScriptsQueue.shift();
if (initScriptPromise)
initScriptPromise.resolve(JSON.parse(data));
}
isHeadlessBrowser() {
return this.provider.isHeadlessBrowser(this.id);
}
async reportJobResult(status, data) {
await this.provider.reportJobResult(this.id, status, data);
}
async getStatus(isTestDone) {
if (!this.idle && !isTestDone) {
this.idle = true;
this.emit('idle');
}
if (this.opened) {
const testRunUrl = await this._getTestRunUrl(isTestDone || this.testRunAborted);
this.testRunAborted = false;
if (testRunUrl) {
this.idle = false;
return { cmd: command_1.default.run, url: testRunUrl };
}
}
return { cmd: command_1.default.idle, url: this.idleUrl };
}
}
exports.default = BrowserConnection;
module.exports = exports.default;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYnJvd3Nlci9jb25uZWN0aW9uL2luZGV4LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsbUNBQXNDO0FBQ3RDLG9EQUE2QjtBQUM3Qix3REFBZ0M7QUFDaEMsbUNBQXdDO0FBQ3hDLHlDQUFvRDtBQUNwRCwyREFBc0Q7QUFDdEQsc0VBQTZDO0FBQzdDLG9EQUE0QjtBQUM1Qix3REFBZ0M7QUFDaEMsc0RBQThCO0FBQzlCLGtEQUFvRDtBQUNwRCw4Q0FBb0Q7QUFFcEQsTUFBTSxrQkFBa0IsR0FBRyw2QkFBSSxDQUFDLG9EQUFvRCxDQUFDLENBQUM7QUFDdEYsTUFBTSxXQUFXLEdBQVUsRUFBRSxDQUFDO0FBRzlCLE1BQXFCLGlCQUFrQixTQUFRLHFCQUFZO0lBQ3ZELFlBQWEsT0FBTyxFQUFFLFdBQVcsRUFBRSxTQUFTO1FBQ3hDLEtBQUssRUFBRSxDQUFDO1FBRVIsSUFBSSxDQUFDLGlCQUFpQixHQUFTLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO1FBQzdDLElBQUksQ0FBQyx1QkFBdUIsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO1FBRXpDLElBQUksQ0FBQyxFQUFFLEdBQXlCLGlCQUFpQixDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2hFLElBQUksQ0FBQyxRQUFRLEdBQW1CLEVBQUUsQ0FBQztRQUNuQyxJQUFJLENBQUMsZ0JBQWdCLEdBQVcsRUFBRSxDQUFDO1FBQ25DLElBQUksQ0FBQyx3QkFBd0IsR0FBRyxPQUFPLENBQUM7UUFDeEMsSUFBSSxDQUFDLG9CQUFvQixHQUFPLElBQUksQ0FBQztRQUNyQyxJQUFJLENBQUMsY0FBYyxHQUFhLEtBQUssQ0FBQztRQUV0QyxJQUFJLENBQUMsV0FBVyxHQUE2QixXQUFXLENBQUM7UUFDekQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxTQUFTLEdBQW1CLEVBQUUsQ0FBQztRQUNoRCxJQUFJLENBQUMs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QUFDO2FBQ2hEO1NBQ0o7UUFFRCxPQUFPLEVBQUUsR0FBRyxFQUFFLGlCQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDcEQsQ0FBQztDQUNKO0FBNVVELG9DQTRVQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEV2ZW50RW1pdHRlciB9IGZyb20gJ2V2ZW50cyc7XG5pbXBvcnQgUHJvbWlzZSBmcm9tICdwaW5raWUnO1xuaW1wb3J0IE11c3RhY2hlIGZyb20gJ211c3RhY2hlJztcbmltcG9ydCB7IHB1bGwgYXMgcmVtb3ZlIH0gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IHBhcnNlIGFzIHBhcnNlVXNlckFnZW50IH0gZnJvbSAndXNlcmFnZW50JztcbmltcG9ydCB7IHJlYWRTeW5jIGFzIHJlYWQgfSBmcm9tICdyZWFkLWZpbGUtcmVsYXRpdmUnO1xuaW1wb3J0IHByb21pc2lmeUV2ZW50IGZyb20gJ3Byb21pc2lmeS1ldmVudCc7XG5pbXBvcnQgbmFub2lkIGZyb20gJ25hbm9pZCc7XG5pbXBvcnQgQ09NTUFORCBmcm9tICcuL2NvbW1hbmQnO1xuaW1wb3J0IFNUQVRVUyBmcm9tICcuL3N0YXR1cyc7XG5pbXBvcnQgeyBHZW5lcmFsRXJyb3IgfSBmcm9tICcuLi8uLi9lcnJvcnMvcnVudGltZSc7XG5pbXBvcnQgeyBSVU5USU1FX0VSUk9SUyB9IGZyb20gJy4uLy4uL2Vycm9ycy90eXBlcyc7XG5cbmNvbnN0IElETEVfUEFHRV9URU1QTEFURSA9IHJlYWQoJy4uLy4uL2NsaWVudC9icm93c2VyL2lkbGUtcGFnZS9pbmRleC5odG1sLm11c3RhY2hlJyk7XG5jb25zdCBjb25uZWN0aW9ucyAgICAgICAgPSB7fTtcblxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBCcm93c2VyQ29ubmVjdGlvbiBleHRlbmRzIEV2ZW50RW1pdHRlciB7XG4gICAgY29uc3RydWN0b3IgKGdhdGV3YXksIGJyb3dzZXJJbmZvLCBwZXJtYW5lbnQpIHtcbiAgICAgICAgc3VwZXIoKTtcblxuICAgICAgICB0aGlzLkhFQVJUQkVBVF9USU1FT1VUICAgICAgID0gMiAqIDYwICogMTAwMDtcbiAgICAgICAgdGhpcy5CUk9XU0VSX1JFU1RBUlRfVElNRU9VVCA9IDYwICogMTAwMDtcblxuICAgICAgICB0aGlzLmlkICAgICAgICAgICAgICAgICAgICAgICA9IEJyb3dzZXJDb25uZWN0aW9uLl9nZW5lcmF0ZUlkKCk7XG4gICAgICAgIHRoaXMuam9iUXVldWUgICAgICAgICAgICAgICAgID0gW107XG4gICAgICAgIHRoaXMuaW5pdFNjcmlwdHNRdWV1ZSAgICAgICAgID0gW107XG4gICAgICAgIHRoaXMuYnJvd3NlckNvbm5lY3Rpb25HYXRld2F5ID0gZ2F0ZXdheTtcbiAgICAgICAgdGhpcy5kaXNjb25uZWN0aW9uUHJvbWlzZSAgICAgPSBudWxsO1xuICAgICAgICB0aGlzLnRlc3RSdW5BYm9ydGVkICAgICAgICAgICA9IGZhbHNlO1xuXG4gICAgICAgIHRoaXMuYnJvd3NlckluZm8gICAgICAgICAgICAgICAgICAgICAgICAgICA9IGJyb3dzZXJJbmZvO1xuICAgICAgICB0aGlzLmJyb3dzZXJJbmZvLnVzZXJBZ2VudCAgICAgICAgICAgICAgICAgPSAnJztcbiAgICAgICAgdGhpcy5icm93c2VySW5mby51c2VyQWdlbnRQcm92aWRlck1ldGFJbmZvID0gJyc7XG5cbiAgICAgICAgdGhpcy5wcm92aWRlciA9IGJyb3dzZXJJbmZvLnByb3ZpZGVyO1xuXG4gICAgICAgIHRoaXMucGVybWFuZW50ICAgICAgICAgPSBwZXJtYW5lbnQ7XG4gICAgICAgIHRoaXMuY2xvc2luZyAgICAgICAgICAgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5jbG9zZWQgICAgICAgICAgICA9IGZhbHNlO1xuICAgICAgICB0aGlzLnJlYWR5ICAgICAgICAgICAgID0gZmFsc2U7XG4gICAgICAgIHRoaXMub3BlbmVkICAgICAgICAgICAgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5pZGxlICAgICAgICAgICAgICA9IHRydWU7XG4gICAgICAgIHRoaXMuaGVhcnRiZWF0VGltZW91dCAgPSBudWxsO1xuICAgICAgICB0aGlzLnBlbmRpbmdUZXN0UnVuVXJsID0gbnVsbDtcblxuICAgICAgICB0aGlzLnVybCAgICAgICAgICAgPSBgJHtnYXRld2F5LmRvbWFpbn0vYnJvd3Nlci9jb25uZWN0LyR7dGhpcy5pZH1gO1xuICAgICAgICB0aGlzLmlkbGVVcmwgICAgICAgPSBgJHtnYXRld2F5LmRvbWFpbn0vYnJvd3Nlci9pZGxlLyR7dGhpcy5pZH1gO1xuICAgICAgICB0aGlzLmZvcmNlZElkbGVVcmwgPSBgJHtnYXRld2F5LmRvbWFpbn0vYnJvd3Nlci9pZGxlLWZvcmNlZC8ke3RoaXMuaWR9YDtcbiAgICAgICAgdGhpcy5pbml0U2NyaXB0VXJsID0gYCR7Z2F0ZXdheS5kb21haW59L2Jyb3dzZXIvaW5pdC1zY3JpcHQvJHt0aGlzLmlkfWA7XG5cbiAgICAgICAgdGhpcy5oZWFydGJlYXRSZWxhdGl2ZVVybCAgPSBgL2Jyb3dzZXIvaGVhcnRiZWF0LyR7dGhpcy5pZH1gO1xuICAgICAgICB0aGlzLnN0YXR1c1JlbGF0aXZlVXJsICAgICA9IGAvYnJvd3Nlci9zdGF0dXMvJHt0aGlzLmlkfWA7XG4gICAgICAgIHRoaXMuc3RhdHVzRG9uZVJlbGF0aXZlVXJsID0gYC9icm93c2VyL3N0YXR1cy1kb25lLyR7dGhpcy5pZH1gO1xuXG4gICAgICAgIHRoaXMuaGVhcnRiZWF0VXJsICA9IGAke2dhdGV3YXkuZG9tYWlufSR7dGhpcy5oZWFydGJlYXRSZWxhdGl2ZVVybH1gO1xuICAgICAgICB0aGlzLnN0YXR1c1VybCAgICAgPSBgJHtnYXRld2F5LmRvbWFpbn0ke3RoaXMuc3RhdHVzUmVsYXRpdmVVcmx9YDtcbiAgICAgICAgdGhpcy5zdGF0dXNEb25lVXJsID0gYCR7Z2F0ZXdheS5kb21haW59JHt0aGlzLnN0YXR1c0RvbmVSZWxhdGl2ZVVybH1gO1xuXG4gICAgICAgIHRoaXMub24oJ2Vycm9yJywgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5fZm9yY2VJZGxlKCk7XG4gICAgICAgICAgICB0aGlzLmNsb3NlKCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNvbm5lY3Rpb25zW3RoaXMuaWRdID0gdGhpcztcblxuICAgICAgICB0aGlzLmJyb3dzZXJDb25uZWN0aW9uR2F0ZXdheS5zdGFydFNlcnZpbmdDb25uZWN0aW9uKHRoaXMpO1xuXG4gICAgICAgIHByb2Nlc3MubmV4dFRpY2soKCkgPT4gdGhpcy5fcnVuQnJvd3NlcigpKTtcbiAgICB9XG5cbiAgICBzdGF0aWMgX2dlbmVyYXRlSWQgKCkge1xuICAgICAgICByZXR1cm4gbmFub2lkKDcpO1xuICAgIH1cblxuICAgIGFzeW5jIF9ydW5Ccm93c2VyICgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucHJvdmlkZXIub3BlbkJyb3dzZXIodGhpcy5pZCwgdGhpcy51cmwsIHRoaXMuYnJvd3NlckluZm8uYnJvd3Nlck5hbWUpO1xuXG4gICAgICAgICAgICBpZiAoIXRoaXMucmVhZHkpXG4gICAgICAgICAgICAgICAgYXdhaXQgcHJvbWlzaWZ5RXZlbnQodGhpcywgJ3JlYWR5Jyk7XG5cbiAgICAgICAgICAgIHRoaXMub3BlbmVkID0gdHJ1ZTtcbiAgICAgICAgICAgIHRoaXMuZW1pdCgnb3BlbmVkJyk7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgdGhpcy5lbWl0KCdlcnJvcicsIG5ldyBHZW5lcmFsRXJyb3IoXG4gICAgICAgICAgICAgICAgUlVOVElNRV9FUlJPUlMudW5hYmxlVG9PcGVuQnJvd3NlcixcbiAgICAgICAgICAgICAgICB0aGlzLmJyb3dzZXJJbmZvLnByb3ZpZGVyTmFtZSArICc6JyArIHRoaXMuYnJvd3NlckluZm8uYnJvd3Nlck5hbWUsXG4gICAgICAgICAgICAgICAgZXJyLnN0YWNrXG4gICAgICAgICAgICApKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGFzeW5jIF9jbG9zZUJyb3dzZXIgKCkge1xuICAgICAgICBpZiAoIXRoaXMuaWRsZSlcbiAgICAgICAgICAgIGF3YWl0IHByb21pc2lmeUV2ZW50KHRoaXMsICdpZGxlJyk7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucHJvdmlkZXIuY2xvc2VCcm93c2VyKHRoaXMuaWQpO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgIC8vIE5PVEU6IEEgd2FybmluZyB3b3VsZCBiZSByZWFsbHkgbmljZSBoZXJlLCBidXQgaXQgY2FuJ3QgYmUgZG9uZSB3aGlsZSBsb2cgaXMgc3RvcmVkIGluIGEgdGFzay5cbiAgICAgICAgfVxuICAgIH1cblxuICAgIF9mb3JjZUlkbGUgKCkge1xuICAgICAgICBpZiAoIXRoaXMuaWRsZSkge1xuICAgICAgICAgICAgdGhpcy5zd2l0Y2hpbmdUb0lkbGUgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuaWRsZSAgICAgICAgICAgID0gdHJ1ZTtcbiAgICAgICAgICAgIHRoaXMuZW1pdCgnaWRsZScpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgX2NyZWF0ZUJyb3dzZXJEaXNjb25uZWN0ZWRFcnJvciAoKSB7XG4gICAgICAgIHJldHVybiBuZXcgR2VuZXJhbEVycm9yKFJVTlRJTUVfRVJST1JTLmJyb3dzZXJEaXNjb25uZWN0ZWQsIHRoaXMudXNlckFnZW50KTtcbiAgICB9XG5cbiAgICBfd2FpdEZvckhlYXJ0YmVhdCAoKSB7XG4gICAgICAgIHRoaXMuaGVhcnRiZWF0VGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgZXJyID0gdGhpcy5fY3JlYXRlQnJvd3NlckRpc2Nvbm5lY3RlZEVycm9yKCk7XG5cbiAgICAgICAgICAgIHRoaXMub3BlbmVkICAgICAgICAgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMudGVzdFJ1bkFib3J0ZWQgPSB0cnVlO1xuXG4gICAgICAgICAgICB0aGlzLmVtaXQoJ2Rpc2Nvbm5lY3RlZCcsIGVycik7XG5cbiAgICAgICAgICAgIHRoaXMuX3Jlc3RhcnRCcm93c2VyT25EaXNjb25uZWN0KGVycik7XG4gICAgICAgIH0sIHRoaXMuSEVBUlRCRUFUX1RJTUVPVVQpO1xuICAgIH1cblxuICAgIGFzeW5jIF9nZXRUZXN0UnVuVXJsIChuZWVkUG9wTmV4dCkge1xuICAgICAgICBpZiAobmVlZFBvcE5leHQgfHwgIXRoaXMucGVuZGluZ1Rlc3RSdW5VcmwpXG4gICAgICAgICAgICB0aGlzLnBlbmRpbmdUZXN0UnVuVXJsID0gYXdhaXQgdGhpcy5fcG9wTmV4dFRlc3RSdW5VcmwoKTtcblxuICAgICAgICByZXR1cm4gdGhpcy5wZW5kaW5nVGVzdFJ1blVybDtcbiAgICB9XG5cbiAgICBhc3luYyBfcG9wTmV4dFRlc3RSdW5VcmwgKCkge1xuICAgICAgICB3aGlsZSAodGhpcy5oYXNRdWV1ZWRKb2JzICYmICF0aGlzLmN1cnJlbnRKb2IuaGFzUXVldWVkVGVzdFJ1bnMpXG4gICAgICAgICAgICB0aGlzLmpvYlF1ZXVlLnNoaWZ0KCk7XG5cbiAgICAgICAgcmV0dXJuIHRoaXMuaGFzUXVldWVkSm9icyA/IGF3YWl0IHRoaXMuY3VycmVudEpvYi5wb3BOZXh0VGVzdFJ1blVybCh0aGlzKSA6IG51bGw7XG4gICAgfVxuXG4gICAgc3RhdGljIGdldEJ5SWQgKGlkKSB7XG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uc1tpZF0gfHwgbnVsbDtcbiAgICB9XG5cbiAgICBhc3luYyBfcmVzdGFydEJyb3dzZXIgKCkge1xuICAgICAgICB0aGlzLnJlYWR5ID0gZmFsc2U7XG5cbiAgICAgICAgdGhpcy5fZm9yY2VJZGxlKCk7XG5cbiAgICAgICAgbGV0IHJlc29sdmVUaW1lb3V0ICAgPSBudWxsO1xuICAgICAgICBsZXQgaXNUaW1lb3V0RXhwaXJlZCA9IGZhbHNlO1xuICAgICAgICBsZXQgdGltZW91dCAgICAgICAgICA9IG51bGw7XG5cbiAgICAgICAgY29uc3QgcmVzdGFydFByb21pc2UgPSB0aGlzLl9jbG9zZUJyb3dzZXIoKVxuICAgICAgICAgICAgLnRoZW4oKCkgPT4gdGhpcy5fcnVuQnJvd3NlcigpKTtcblxuICAgICAgICBjb25zdCB0aW1lb3V0UHJvbWlzZSA9IG5ldyBQcm9taXNlKHJlc29sdmUgPT4ge1xuICAgICAgICAgICAgcmVzb2x2ZVRpbWVvdXQgPSByZXNvbHZlO1xuXG4gICAgICAgICAgICB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgaXNUaW1lb3V0RXhwaXJlZCA9IHRydWU7XG5cbiAgICAgICAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgICAgICB9LCB0aGlzLkJST1dTRVJfUkVTVEFSVF9USU1FT1VUKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgUHJvbWlzZS5yYWNlKFsgcmVzdGFydFByb21pc2UsIHRpbWVvdXRQcm9taXNlIF0pXG4gICAgICAgICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuXG4gICAgICAgICAgICAgICAgaWYgKGlzVGltZW91dEV4cGlyZWQpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZW1pdCgnZXJyb3InLCB0aGlzLl9jcmVhdGVCcm93c2VyRGlzY29ubmVjdGVkRXJyb3IoKSk7XG4gICAgICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlVGltZW91dCgpO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgX3Jlc3RhcnRCcm93c2VyT25EaXNjb25uZWN0IChlcnIpIHtcbiAgICAgICAgbGV0IHJlc29sdmVGbiA9IG51bGw7XG4gICAgICAgIGxldCByZWplY3RGbiAgPSBudWxsO1xuXG4gICAgICAgIHRoaXMuZGlzY29ubmVjdGlvblByb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICByZXNvbHZlRm4gPSByZXNvbHZlO1xuXG4gICAgICAgICAgICByZWplY3RGbiA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdEZuKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSlcbiAgICAgICAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5fcmVzdGFydEJyb3dzZXIoKTtcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAuY2F0Y2goZSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5lbWl0KCdlcnJvcicsIGUpO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5kaXNjb25uZWN0aW9uUHJvbWlzZS5yZXNvbHZlID0gcmVzb2x2ZUZuO1xuICAgICAgICB0aGlzLmRpc2Nvbm5lY3Rpb25Qcm9taXNlLnJlamVjdCAgPSByZWplY3RGbjtcbiAgICB9XG5cbiAgICBhc3luYyBwcm9jZXNzRGlzY29ubmVjdGlvbiAoZGlzY29ubmVjdGlvblRocmVzaG9sZEV4Y2VlZGVlZCkge1xuICAgICAgICBjb25zdCB7IHJlc29sdmUsIHJlamVjdCB9ID0gdGhpcy5kaXNjb25uZWN0aW9uUHJvbWlzZTtcblxuICAgICAgICBpZiAoZGlzY29ubmVjdGlvblRocmVzaG9sZEV4Y2VlZGVlZClcbiAgICAgICAgICAgIHJlamVjdCgpO1xuICAgICAgICBlbHNlXG4gICAgICAgICAgICByZXNvbHZlKCk7XG4gICAgfVxuXG4gICAgYWRkV2FybmluZyAoLi4uYXJncykge1xuICAgICAgICBpZiAodGhpcy5jdXJyZW50Sm9iKVxuICAgICAgICAgICAgdGhpcy5jdXJyZW50Sm9iLndhcm5pbmdMb2cuYWRkV2FybmluZyguLi5hcmdzKTtcbiAgICB9XG5cbiAgICBzZXRQcm92aWRlck1ldGFJbmZvIChzdHIpIHtcbiAgICAgICAgdGhpcy5icm93c2VySW5mby51c2VyQWdlbnRQcm92aWRlck1ldGFJbmZvID0gc3RyO1xuICAgIH1cblxuICAgIGdldCB1c2VyQWdlbnQgKCkge1xuICAgICAgICBsZXQgdXNlckFnZW50ID0gdGhpcy5icm93c2VySW5mby51c2VyQWdlbnQ7XG5cbiAgICAgICAgaWYgKHRoaXMuYnJvd3NlckluZm8udXNlckFnZW50UHJvdmlkZXJNZXRhSW5mbylcbiAgICAgICAgICAgIHVzZXJBZ2VudCArPSBgICgke3RoaXMuYnJvd3NlckluZm8udXNlckFnZW50UHJvdmlkZXJNZXRhSW5mb30pYDtcblxuICAgICAgICByZXR1cm4gdXNlckFnZW50O1xuICAgIH1cblxuICAgIGdldCBoYXNRdWV1ZWRKb2JzICgpIHtcbiAgICAgICAgcmV0dXJuICEhdGhpcy5qb2JRdWV1ZS5sZW5ndGg7XG4gICAgfVxuXG4gICAgZ2V0IGN1cnJlbnRKb2IgKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5qb2JRdWV1ZVswXTtcbiAgICB9XG5cbiAgICAvLyBBUElcbiAgICBydW5Jbml0U2NyaXB0IChjb2RlKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHRoaXMuaW5pdFNjcmlwdHNRdWV1ZS5wdXNoKHsgY29kZSwgcmVzb2x2ZSB9KSk7XG4gICAgfVxuXG4gICAgYWRkSm9iIChqb2IpIHtcbiAgICAgICAgdGhpcy5qb2JRdWV1ZS5wdXNoKGpvYik7XG4gICAgfVxuXG4gICAgcmVtb3ZlSm9iIChqb2IpIHtcbiAgICAgICAgcmVtb3ZlKHRoaXMuam9iUXVldWUsIGpvYik7XG4gICAgfVxuXG4gICAgY2xvc2UgKCkge1xuICAgICAgICBpZiAodGhpcy5jbG9zZWQgfHwgdGhpcy5jbG9zaW5nKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHRoaXMuY2xvc2luZyA9IHRydWU7XG5cbiAgICAgICAgdGhpcy5fY2xvc2VCcm93c2VyKClcbiAgICAgICAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmJyb3dzZXJDb25uZWN0aW9uR2F0ZXdheS5zdG9wU2VydmluZ0Nvbm5lY3Rpb24odGhpcyk7XG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuaGVhcnRiZWF0VGltZW91dCk7XG5cbiAgICAgICAgICAgICAgICBkZWxldGUgY29ubmVjdGlvbnNbdGhpcy5pZF07XG5cbiAgICAgICAgICAgICAgICB0aGlzLnJlYWR5ICA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIHRoaXMuY2xvc2VkID0gdHJ1ZTtcblxuICAgICAgICAgICAgICAgIHRoaXMuZW1pdCgnY2xvc2VkJyk7XG4gICAgICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBlc3RhYmxpc2ggKHVzZXJBZ2VudCkge1xuICAgICAgICB0aGlzLnJlYWR5ID0gdHJ1ZTtcblxuICAgICAgICBjb25zdCBwYXJzZWRVc2VyQWdlbnQgPSBwYXJzZVVzZXJBZ2VudCh1c2VyQWdlbnQpO1xuXG4gICAgICAgIHRoaXMuYnJvd3NlckluZm8udXNlckFnZW50ICAgICAgID0gcGFyc2VkVXNlckFnZW50LnRvU3RyaW5nKCk7XG4gICAgICAgIHRoaXMuYnJvd3NlckluZm8uZnVsbFVzZXJBZ2VudCAgID0gdXNlckFnZW50O1xuICAgICAgICB0aGlzLmJyb3dzZXJJbmZvLnBhcnNlZFVzZXJBZ2VudCA9IHBhcnNlZFVzZXJBZ2VudDtcblxuICAgICAgICB0aGlzLl93YWl0Rm9ySGVhcnRiZWF0KCk7XG4gICAgICAgIHRoaXMuZW1pdCgncmVhZHknKTtcbiAgICB9XG5cbiAgICBoZWFydGJlYXQgKCkge1xuICAgICAgICBjbGVhclRpbWVvdXQodGhpcy5oZWFydGJlYXRUaW1lb3V0KTtcbiAgICAgICAgdGhpcy5fd2FpdEZvckhlYXJ0YmVhdCgpO1xuXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBjb2RlOiB0aGlzLmNsb3NpbmcgPyBTVEFUVVMuY2xvc2luZyA6IFNUQVRVUy5vayxcbiAgICAgICAgICAgIHVybDogIHRoaXMuY2xvc2luZyA/IHRoaXMuaWRsZVVybCA6ICcnXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgcmVuZGVySWRsZVBhZ2UgKCkge1xuICAgICAgICByZXR1cm4gTXVzdGFjaGUucmVuZGVyKElETEVfUEFHRV9URU1QTEFURSwge1xuICAgICAgICAgICAgdXNlckFnZW50OiAgICAgIHRoaXMudXNlckFnZW50LFxuICAgICAgICAgICAgc3RhdHVzVXJsOiAgICAgIHRoaXMuc3RhdHVzVXJsLFxuICAgICAgICAgICAgaGVhcnRiZWF0VXJsOiAgIHRoaXMuaGVhcnRiZWF0VXJsLFxuICAgICAgICAgICAgaW5pdFNjcmlwdFVybDogIHRoaXMuaW5pdFNjcmlwdFVybCxcbiAgICAgICAgICAgIHJldHJ5VGVzdFBhZ2VzOiAhIXRoaXMuYnJvd3NlckNvbm5lY3Rpb25HYXRld2F5LnJldHJ5VGVzdFBhZ2VzXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIGdldEluaXRTY3JpcHQgKCkge1xuICAgICAgICBjb25zdCBpbml0U2NyaXB0UHJvbWlzZSA9IHRoaXMuaW5pdFNjcmlwdHNRdWV1ZVswXTtcblxuICAgICAgICByZXR1cm4geyBjb2RlOiBpbml0U2NyaXB0UHJvbWlzZSA/IGluaXRTY3JpcHRQcm9taXNlLmNvZGUgOiBudWxsIH07XG4gICAgfVxuXG4gICAgaGFuZGxlSW5pdFNjcmlwdFJlc3VsdCAoZGF0YSkge1xuICAgICAgICBjb25zdCBpbml0U2NyaXB0UHJvbWlzZSA9IHRoaXMuaW5pdFNjcmlwdHNRdWV1ZS5zaGlmdCgpO1xuXG4gICAgICAgIGlmIChpbml0U2NyaXB0UHJvbWlzZSlcbiAgICAgICAgICAgIGluaXRTY3JpcHRQcm9taXNlLnJlc29sdmUoSlNPTi5wYXJzZShkYXRhKSk7XG4gICAgfVxuXG4gICAgaXNIZWFkbGVzc0Jyb3dzZXIgKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5wcm92aWRlci5pc0hlYWRsZXNzQnJvd3Nlcih0aGlzLmlkKTtcbiAgICB9XG5cbiAgICBhc3luYyByZXBvcnRKb2JSZXN1bHQgKHN0YXR1cywgZGF0YSkge1xuICAgICAgICBhd2FpdCB0aGlzLnByb3ZpZGVyLnJlcG9ydEpvYlJlc3VsdCh0aGlzLmlkLCBzdGF0dXMsIGRhdGEpO1xuICAgIH1cblxuICAgIGFzeW5jIGdldFN0YXR1cyAoaXNUZXN0RG9uZSkge1xuICAgICAgICBpZiAoIXRoaXMuaWRsZSAmJiAhaXNUZXN0RG9uZSkge1xuICAgICAgICAgICAgdGhpcy5pZGxlID0gdHJ1ZTtcbiAgICAgICAgICAgIHRoaXMuZW1pdCgnaWRsZScpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3BlbmVkKSB7XG4gICAgICAgICAgICBjb25zdCB0ZXN0UnVuVXJsID0gYXdhaXQgdGhpcy5fZ2V0VGVzdFJ1blVybChpc1Rlc3REb25lIHx8IHRoaXMudGVzdFJ1bkFib3J0ZWQpO1xuXG4gICAgICAgICAgICB0aGlzLnRlc3RSdW5BYm9ydGVkID0gZmFsc2U7XG5cbiAgICAgICAgICAgIGlmICh0ZXN0UnVuVXJsKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5pZGxlID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgY21kOiBDT01NQU5ELnJ1biwgdXJsOiB0ZXN0UnVuVXJsIH07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4geyBjbWQ6IENPTU1BTkQuaWRsZSwgdXJsOiB0aGlzLmlkbGVVcmwgfTtcbiAgICB9XG59XG4iXX0=