UNPKG

protractor-screenshot-extension

Version:

An extension for Protractor that allows testing visual screenshots

249 lines 27.4 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ import * as tslib_1 from "tslib"; import { createWriteStream, existsSync, readFileSync } from 'fs'; import { PNG } from 'pngjs'; import { browser } from 'protractor'; /** @type {?} */ var pixelmatch = require('pixelmatch'); /** @type {?} */ var shell = require('shelljs'); /** * @record */ export function IRectangle() { } if (false) { /** @type {?} */ IRectangle.prototype.x; /** @type {?} */ IRectangle.prototype.y; /** @type {?} */ IRectangle.prototype.w; /** @type {?} */ IRectangle.prototype.h; } /** * @record */ export function IScreenshotOptions() { } if (false) { /** @type {?|undefined} */ IScreenshotOptions.prototype.ignoreRectangles; /** @type {?|undefined} */ IScreenshotOptions.prototype.ignoreElements; /** @type {?|undefined} */ IScreenshotOptions.prototype.threshold; /** @type {?|undefined} */ IScreenshotOptions.prototype.includeAA; } /** @type {?} */ var actualDirectory = 'actual'; /** @type {?} */ var diffDirectory = 'diff'; /** @type {?} */ var baselineDirectory = 'baseline'; var ProtractorScreenshotExtension = /** @class */ (function () { function ProtractorScreenshotExtension(screenshotDirectory) { this._screenshotDirectory = screenshotDirectory; if (this._screenshotDirectory.slice(-1) === '/') { this._screenshotDirectory = this._screenshotDirectory.slice(0, -1); } // Create all 3 subfolders. shell.mkdir('-p', screenshotDirectory + "/" + actualDirectory); shell.mkdir('-p', screenshotDirectory + "/" + diffDirectory); shell.mkdir('-p', screenshotDirectory + "/" + baselineDirectory); } /** * @param {?} element * @param {?} tag * @param {?=} options * @return {?} */ ProtractorScreenshotExtension.prototype.checkElementScreenshot = /** * @param {?} element * @param {?} tag * @param {?=} options * @return {?} */ function (element, tag, options) { return tslib_1.__awaiter(this, void 0, void 0, function () { var baselineImagePath, screenshot, _a, _b, stream, testScreenshotBuffer, _c, _d, testScreenshotPng, baselineScreenshotPng, diffPng, numberOfPixelsDifferent, diffImagePath, actualImagePath; return tslib_1.__generator(this, function (_e) { switch (_e.label) { case 0: baselineImagePath = this._screenshotDirectory + "/" + baselineDirectory + "/" + tag + ".png"; if (!!existsSync(baselineImagePath)) return [3 /*break*/, 3]; console.log("Saving baseline image: " + baselineImagePath); _b = (_a = Buffer).from; return [4 /*yield*/, element.takeScreenshot()]; case 1: screenshot = _b.apply(_a, [_e.sent(), 'base64']); return [4 /*yield*/, this._addBlackoutRectangles(screenshot, options, element)]; case 2: screenshot = _e.sent(); stream = createWriteStream(baselineImagePath); stream.write(screenshot); stream.end(); return [2 /*return*/, 0]; case 3: _d = (_c = Buffer).from; return [4 /*yield*/, element.takeScreenshot()]; case 4: testScreenshotBuffer = _d.apply(_c, [_e.sent(), 'base64']); return [4 /*yield*/, this._addBlackoutRectangles(testScreenshotBuffer, options, element)]; case 5: testScreenshotBuffer = _e.sent(); testScreenshotPng = PNG.sync.read(testScreenshotBuffer); baselineScreenshotPng = PNG.sync.read(readFileSync(baselineImagePath)); diffPng = new PNG({ width: baselineScreenshotPng.width, height: baselineScreenshotPng.height }); numberOfPixelsDifferent = pixelmatch(testScreenshotPng.data, baselineScreenshotPng.data, diffPng.data, baselineScreenshotPng.width, baselineScreenshotPng.height, { threshold: options ? options.threshold : undefined, includeAA: options ? options.includeAA : undefined }); if (!!numberOfPixelsDifferent) { diffImagePath = this._screenshotDirectory + "/" + diffDirectory + "/" + tag + ".png"; actualImagePath = this._screenshotDirectory + "/" + actualDirectory + "/" + tag + ".png"; createWriteStream(actualImagePath).write(testScreenshotBuffer); diffPng.pack().pipe(createWriteStream(diffImagePath)); } return [2 /*return*/, numberOfPixelsDifferent]; } }); }); }; /** * @param {?} tag * @param {?=} options * @return {?} */ ProtractorScreenshotExtension.prototype.checkPageScreenshot = /** * @param {?} tag * @param {?=} options * @return {?} */ function (tag, options) { return this.checkElementScreenshot(browser, tag, options); }; /** * @private * @param {?} image * @param {?} x * @param {?} y * @param {?} w * @param {?} h * @return {?} */ ProtractorScreenshotExtension.prototype._blackoutRectangle = /** * @private * @param {?} image * @param {?} x * @param {?} y * @param {?} w * @param {?} h * @return {?} */ function (image, x, y, w, h) { // Create a black image that will be used for blacking out regions to be ignored. /** @type {?} */ var blackPng = new PNG({ width: w, height: h }); for (var i = 0; i < blackPng.height; i++) { for (var j = 0; j < blackPng.width; j++) { /** @type {?} */ var idx = (blackPng.width * i + j) * 4; blackPng.data[idx] = 0; // red blackPng.data[idx + 1] = 0; // blue blackPng.data[idx + 2] = 0; // green blackPng.data[idx + 3] = 255; // alpha } } blackPng.bitblt(image, 0, 0, w, h, x, y); }; /** * @private * @param {?} imageBuffer * @param {?=} options * @param {?=} parentElement * @return {?} */ ProtractorScreenshotExtension.prototype._addBlackoutRectangles = /** * @private * @param {?} imageBuffer * @param {?=} options * @param {?=} parentElement * @return {?} */ function (imageBuffer, options, parentElement) { return tslib_1.__awaiter(this, void 0, void 0, function () { var imagePng_1; var _this = this; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: if (!(options && (options.ignoreRectangles || options.ignoreElements))) return [3 /*break*/, 3]; imagePng_1 = PNG.sync.read(imageBuffer); if (options.ignoreRectangles && options.ignoreRectangles.length) { options.ignoreRectangles.forEach(function (rect) { _this._blackoutRectangle(imagePng_1, rect.x, rect.y, rect.w, rect.h); }); } if (!(options.ignoreElements && options.ignoreElements.length)) return [3 /*break*/, 2]; // Using `map` instead of `forEach` in order to await all async calls. return [4 /*yield*/, Promise.all(options.ignoreElements.map(function (ignoreElement) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var parentLocation, ignoreLocation, ignoreSize, parentSize, _a, conversionFactor; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: parentLocation = { x: 0, y: 0 }; if (!!!parentElement.getLocation) return [3 /*break*/, 2]; return [4 /*yield*/, parentElement.getLocation()]; case 1: // If an ElementFinder was passed in, call `getLocation()`, otherwise assume the whole browser was passed in. parentLocation = _b.sent(); _b.label = 2; case 2: return [4 /*yield*/, ignoreElement.getLocation()]; case 3: ignoreLocation = _b.sent(); return [4 /*yield*/, ignoreElement.getSize()]; case 4: ignoreSize = _b.sent(); // Adjust coordinates, as the screenshot can be twice the viewport size for high resolution displays. if (!parentElement.driver) return [3 /*break*/, 6]; return [4 /*yield*/, parentElement.driver.manage().window().getSize()]; case 5: _a = _b.sent(); return [3 /*break*/, 8]; case 6: return [4 /*yield*/, parentElement.getSize()]; case 7: _a = _b.sent(); _b.label = 8; case 8: parentSize = _a; conversionFactor = imagePng_1.width / parentSize.width; this._blackoutRectangle(imagePng_1, (ignoreLocation.x - parentLocation.x) * conversionFactor, (ignoreLocation.y - parentLocation.y) * conversionFactor, ignoreSize.width * conversionFactor, ignoreSize.height * conversionFactor); return [2 /*return*/]; } }); }); }))]; case 1: // Using `map` instead of `forEach` in order to await all async calls. _a.sent(); _a.label = 2; case 2: return [2 /*return*/, PNG.sync.write(imagePng_1)]; case 3: return [2 /*return*/, imageBuffer]; } }); }); }; return ProtractorScreenshotExtension; }()); export { ProtractorScreenshotExtension }; if (false) { /** * @type {?} * @private */ ProtractorScreenshotExtension.prototype._screenshotDirectory; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdHJhY3Rvci1zY3JlZW5zaG90LWV4dGVuc2lvbi51dGlsaXR5LmpzIiwic291cmNlUm9vdCI6Im5nOi8vcHJvdHJhY3Rvci1zY3JlZW5zaG90LWV4dGVuc2lvbi8iLCJzb3VyY2VzIjpbImxpYi9wcm90cmFjdG9yLXNjcmVlbnNob3QtZXh0ZW5zaW9uLnV0aWxpdHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxNQUFNLElBQUksQ0FBQztBQUNqRSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sT0FBTyxDQUFDO0FBQzVCLE9BQU8sRUFBRSxPQUFPLEVBQW9DLE1BQU0sWUFBWSxDQUFDOztJQUNqRSxVQUFVLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQzs7SUFDbEMsS0FBSyxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUM7Ozs7QUFFaEMsZ0NBS0M7OztJQUpHLHVCQUFVOztJQUNWLHVCQUFVOztJQUNWLHVCQUFVOztJQUNWLHVCQUFVOzs7OztBQUdkLHdDQUtDOzs7SUFKRyw4Q0FBZ0M7O0lBQ2hDLDRDQUFpQzs7SUFDakMsdUNBQW1COztJQUNuQix1Q0FBb0I7OztJQUdsQixlQUFlLEdBQUcsUUFBUTs7SUFDMUIsYUFBYSxHQUFHLE1BQU07O0lBQ3RCLGlCQUFpQixHQUFHLFVBQVU7QUFFcEM7SUFHSSx1Q0FBWSxtQkFBMkI7UUFDbkMsSUFBSSxDQUFDLG9CQUFvQixHQUFHLG1CQUFtQixDQUFDO1FBQ2hELElBQUksSUFBSSxDQUFDLG9CQUFvQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLEdBQUcsRUFBRTtZQUM3QyxJQUFJLENBQUMsb0JBQW9CLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUN0RTtRQUNELDJCQUEyQjtRQUMzQixLQUFLLENBQUMsS0FBSyxDQUFD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ZVdyaXRlU3RyZWFtLCBleGlzdHNTeW5jLCByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XG5pbXBvcnQgeyBQTkcgfSBmcm9tICdwbmdqcyc7XG5pbXBvcnQgeyBicm93c2VyLCBFbGVtZW50RmluZGVyLCBQcm90cmFjdG9yQnJvd3NlciB9IGZyb20gJ3Byb3RyYWN0b3InO1xuY29uc3QgcGl4ZWxtYXRjaCA9IHJlcXVpcmUoJ3BpeGVsbWF0Y2gnKTtcbmNvbnN0IHNoZWxsID0gcmVxdWlyZSgnc2hlbGxqcycpO1xuXG5leHBvcnQgaW50ZXJmYWNlIElSZWN0YW5nbGUge1xuICAgIHg6IG51bWJlcjtcbiAgICB5OiBudW1iZXI7XG4gICAgdzogbnVtYmVyO1xuICAgIGg6IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJU2NyZWVuc2hvdE9wdGlvbnMge1xuICAgIGlnbm9yZVJlY3RhbmdsZXM/OiBJUmVjdGFuZ2xlW107XG4gICAgaWdub3JlRWxlbWVudHM/OiBFbGVtZW50RmluZGVyW107XG4gICAgdGhyZXNob2xkPzogbnVtYmVyOyAvLyBiZXR3ZWVuIDAgYW5kIDFcbiAgICBpbmNsdWRlQUE/OiBib29sZWFuO1xufVxuXG5jb25zdCBhY3R1YWxEaXJlY3RvcnkgPSAnYWN0dWFsJztcbmNvbnN0IGRpZmZEaXJlY3RvcnkgPSAnZGlmZic7XG5jb25zdCBiYXNlbGluZURpcmVjdG9yeSA9ICdiYXNlbGluZSc7XG5cbmV4cG9ydCBjbGFzcyBQcm90cmFjdG9yU2NyZWVuc2hvdEV4dGVuc2lvbiB7XG4gICAgcHJpdmF0ZSBfc2NyZWVuc2hvdERpcmVjdG9yeTogc3RyaW5nO1xuXG4gICAgY29uc3RydWN0b3Ioc2NyZWVuc2hvdERpcmVjdG9yeTogc3RyaW5nKSB7XG4gICAgICAgIHRoaXMuX3NjcmVlbnNob3REaXJlY3RvcnkgPSBzY3JlZW5zaG90RGlyZWN0b3J5O1xuICAgICAgICBpZiAodGhpcy5fc2NyZWVuc2hvdERpcmVjdG9yeS5zbGljZSgtMSkgPT09ICcvJykge1xuICAgICAgICAgICAgdGhpcy5fc2NyZWVuc2hvdERpcmVjdG9yeSA9IHRoaXMuX3NjcmVlbnNob3REaXJlY3Rvcnkuc2xpY2UoMCwgLTEpO1xuICAgICAgICB9XG4gICAgICAgIC8vIENyZWF0ZSBhbGwgMyBzdWJmb2xkZXJzLlxuICAgICAgICBzaGVsbC5ta2RpcignLXAnLCBgJHtzY3JlZW5zaG90RGlyZWN0b3J5fS8ke2FjdHVhbERpcmVjdG9yeX1gKTtcbiAgICAgICAgc2hlbGwubWtkaXIoJy1wJywgYCR7c2NyZWVuc2hvdERpcmVjdG9yeX0vJHtkaWZmRGlyZWN0b3J5fWApO1xuICAgICAgICBzaGVsbC5ta2RpcignLXAnLCBgJHtzY3JlZW5zaG90RGlyZWN0b3J5fS8ke2Jhc2VsaW5lRGlyZWN0b3J5fWApO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBjaGVja0VsZW1lbnRTY3JlZW5zaG90KFxuICAgICAgICBlbGVtZW50OiBFbGVtZW50RmluZGVyIHwgUHJvdHJhY3RvckJyb3dzZXIsXG4gICAgICAgIHRhZzogc3RyaW5nLFxuICAgICAgICBvcHRpb25zPzogSVNjcmVlbnNob3RPcHRpb25zXG4gICAgKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICAgICAgY29uc3QgYmFzZWxpbmVJbWFnZVBhdGggPSBgJHt0aGlzLl9zY3JlZW5zaG90RGlyZWN0b3J5fS8ke2Jhc2VsaW5lRGlyZWN0b3J5fS8ke3RhZ30ucG5nYDtcbiAgICAgICAgaWYgKCFleGlzdHNTeW5jKGJhc2VsaW5lSW1hZ2VQYXRoKSkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYFNhdmluZyBiYXNlbGluZSBpbWFnZTogJHtiYXNlbGluZUltYWdlUGF0aH1gKTtcbiAgICAgICAgICAgIGxldCBzY3JlZW5zaG90ID0gQnVmZmVyLmZyb20oYXdhaXQgZWxlbWVudC50YWtlU2NyZWVuc2hvdCgpLCAnYmFzZTY0Jyk7XG4gICAgICAgICAgICBzY3JlZW5zaG90ID0gYXdhaXQgdGhpcy5fYWRkQmxhY2tvdXRSZWN0YW5nbGVzKHNjcmVlbnNob3QsIG9wdGlvbnMsIGVsZW1lbnQpO1xuICAgICAgICAgICAgY29uc3Qgc3RyZWFtID0gY3JlYXRlV3JpdGVTdHJlYW0oYmFzZWxpbmVJbWFnZVBhdGgpO1xuICAgICAgICAgICAgc3RyZWFtLndyaXRlKHNjcmVlbnNob3QpO1xuICAgICAgICAgICAgc3RyZWFtLmVuZCgpO1xuICAgICAgICAgICAgcmV0dXJuIDA7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IHRlc3RTY3JlZW5zaG90QnVmZmVyID0gQnVmZmVyLmZyb20oYXdhaXQgZWxlbWVudC50YWtlU2NyZWVuc2hvdCgpLCAnYmFzZTY0Jyk7XG4gICAgICAgIHRlc3RTY3JlZW5zaG90QnVmZmVyID0gYXdhaXQgdGhpcy5fYWRkQmxhY2tvdXRSZWN0YW5nbGVzKHRlc3RTY3JlZW5zaG90QnVmZmVyLCBvcHRpb25zLCBlbGVtZW50KTtcbiAgICAgICAgY29uc3QgdGVzdFNjcmVlbnNob3RQbmcgPSBQTkcuc3luYy5yZWFkKHRlc3RTY3JlZW5zaG90QnVmZmVyKTtcbiAgICAgICAgY29uc3QgYmFzZWxpbmVTY3JlZW5zaG90UG5nID0gUE5HLnN5bmMucmVhZChyZWFkRmlsZVN5bmMoYmFzZWxpbmVJbWFnZVBhdGgpKTtcbiAgICAgICAgY29uc3QgZGlmZlBuZyA9IG5ldyBQTkcoeyB3aWR0aDogYmFzZWxpbmVTY3JlZW5zaG90UG5nLndpZHRoLCBoZWlnaHQ6IGJhc2VsaW5lU2NyZWVuc2hvdFBuZy5oZWlnaHQgfSk7XG4gICAgICAgIGNvbnN0IG51bWJlck9mUGl4ZWxzRGlmZmVyZW50ID0gcGl4ZWxtYXRjaChcbiAgICAgICAgICAgIHRlc3RTY3JlZW5zaG90UG5nLmRhdGEsXG4gICAgICAgICAgICBiYXNlbGluZVNjcmVlbnNob3RQbmcuZGF0YSxcbiAgICAgICAgICAgIGRpZmZQbmcuZGF0YSxcbiAgICAgICAgICAgIGJhc2VsaW5lU2NyZWVuc2hvdFBuZy53aWR0aCxcbiAgICAgICAgICAgIGJhc2VsaW5lU2NyZWVuc2hvdFBuZy5oZWlnaHQsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdGhyZXNob2xkOiBvcHRpb25zID8gb3B0aW9ucy50aHJlc2hvbGQgOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgaW5jbHVkZUFBOiBvcHRpb25zID8gb3B0aW9ucy5pbmNsdWRlQUEgOiB1bmRlZmluZWRcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICAgICAgaWYgKCEhbnVtYmVyT2ZQaXhlbHNEaWZmZXJlbnQpIHtcbiAgICAgICAgICAgIGNvbnN0IGRpZmZJbWFnZVBhdGggPSBgJHt0aGlzLl9zY3JlZW5zaG90RGlyZWN0b3J5fS8ke2RpZmZEaXJlY3Rvcnl9LyR7dGFnfS5wbmdgO1xuICAgICAgICAgICAgY29uc3QgYWN0dWFsSW1hZ2VQYXRoID0gYCR7dGhpcy5fc2NyZWVuc2hvdERpcmVjdG9yeX0vJHthY3R1YWxEaXJlY3Rvcnl9LyR7dGFnfS5wbmdgO1xuICAgICAgICAgICAgY3JlYXRlV3JpdGVTdHJlYW0oYWN0dWFsSW1hZ2VQYXRoKS53cml0ZSh0ZXN0U2NyZWVuc2hvdEJ1ZmZlcik7XG4gICAgICAgICAgICBkaWZmUG5nLnBhY2soKS5waXBlKGNyZWF0ZVdyaXRlU3RyZWFtKGRpZmZJbWFnZVBhdGgpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVtYmVyT2ZQaXhlbHNEaWZmZXJlbnQ7XG4gICAgfVxuXG4gICAgcHVibGljIGNoZWNrUGFnZVNjcmVlbnNob3QodGFnOiBzdHJpbmcsIG9wdGlvbnM/OiBJU2NyZWVuc2hvdE9wdGlvbnMpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgICAgICByZXR1cm4gdGhpcy5jaGVja0VsZW1lbnRTY3JlZW5zaG90KGJyb3dzZXIsIHRhZywgb3B0aW9ucyk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfYmxhY2tvdXRSZWN0YW5nbGUoaW1hZ2U6IFBORywgeDogbnVtYmVyLCB5OiBudW1iZXIsIHc6IG51bWJlciwgaDogbnVtYmVyKTogdm9pZCB7XG4gICAgICAgIC8vIENyZWF0ZSBhIGJsYWNrIGltYWdlIHRoYXQgd2lsbCBiZSB1c2VkIGZvciBibGFja2luZyBvdXQgcmVnaW9ucyB0byBiZSBpZ25vcmVkLlxuICAgICAgICBjb25zdCBibGFja1BuZyA9IG5ldyBQTkcoeyB3aWR0aDogdywgaGVpZ2h0OiBoIH0pO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJsYWNrUG5nLmhlaWdodDsgaSsrKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IGJsYWNrUG5nLndpZHRoOyBqKyspIHtcbiAgICAgICAgICAgICAgICBjb25zdCBpZHggPSAoYmxhY2tQbmcud2lkdGggKiBpICsgaikgKiA0O1xuICAgICAgICAgICAgICAgIGJsYWNrUG5nLmRhdGFbaWR4XSA9IDA7ICAgICAgICAgLy8gcmVkXG4gICAgICAgICAgICAgICAgYmxhY2tQbmcuZGF0YVtpZHggKyAxXSA9IDA7ICAgICAvLyBibHVlXG4gICAgICAgICAgICAgICAgYmxhY2tQbmcuZGF0YVtpZHggKyAyXSA9IDA7ICAgICAvLyBncmVlblxuICAgICAgICAgICAgICAgIGJsYWNrUG5nLmRhdGFbaWR4ICsgM10gPSAyNTU7ICAgLy8gYWxwaGFcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGJsYWNrUG5nLmJpdGJsdChpbWFnZSwgMCwgMCwgdywgaCwgeCwgeSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBfYWRkQmxhY2tvdXRSZWN0YW5nbGVzIChcbiAgICAgICAgaW1hZ2VCdWZmZXI6IEJ1ZmZlcixcbiAgICAgICAgb3B0aW9ucz86IElTY3JlZW5zaG90T3B0aW9ucyxcbiAgICAgICAgcGFyZW50RWxlbWVudD86IEVsZW1lbnRGaW5kZXIgfCBQcm90cmFjdG9yQnJvd3NlclxuICAgICk6IFByb21pc2U8QnVmZmVyPiB7XG4gICAgICAgIGlmIChvcHRpb25zICYmIChvcHRpb25zLmlnbm9yZVJlY3RhbmdsZXMgfHwgb3B0aW9ucy5pZ25vcmVFbGVtZW50cykpIHtcbiAgICAgICAgICAgIGNvbnN0IGltYWdlUG5nID0gUE5HLnN5bmMucmVhZChpbWFnZUJ1ZmZlcik7XG5cbiAgICAgICAgICAgIGlmIChvcHRpb25zLmlnbm9yZVJlY3RhbmdsZXMgJiYgb3B0aW9ucy5pZ25vcmVSZWN0YW5nbGVzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMuaWdub3JlUmVjdGFuZ2xlcy5mb3JFYWNoKChyZWN0KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2JsYWNrb3V0UmVjdGFuZ2xlKGltYWdlUG5nLCByZWN0LngsIHJlY3QueSwgcmVjdC53LCByZWN0LmgpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKG9wdGlvbnMuaWdub3JlRWxlbWVudHMgJiYgb3B0aW9ucy5pZ25vcmVFbGVtZW50cy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAvLyBVc2luZyBgbWFwYCBpbnN0ZWFkIG9mIGBmb3JFYWNoYCBpbiBvcmRlciB0byBhd2FpdCBhbGwgYXN5bmMgY2FsbHMuXG4gICAgICAgICAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwob3B0aW9ucy5pZ25vcmVFbGVtZW50cy5tYXAoYXN5bmMgKGlnbm9yZUVsZW1lbnQpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IHBhcmVudExvY2F0aW9uID0geyB4OiAwLCB5OiAwIH07XG4gICAgICAgICAgICAgICAgICAgIGlmICghIXBhcmVudEVsZW1lbnQuZ2V0TG9jYXRpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIGFuIEVsZW1lbnRGaW5kZXIgd2FzIHBhc3NlZCBpbiwgY2FsbCBgZ2V0TG9jYXRpb24oKWAsIG90aGVyd2lzZSBhc3N1bWUgdGhlIHdob2xlIGJyb3dzZXIgd2FzIHBhc3NlZCBpbi5cbiAgICAgICAgICAgICAgICAgICAgICAgIHBhcmVudExvY2F0aW9uID0gYXdhaXQgcGFyZW50RWxlbWVudC5nZXRMb2NhdGlvbigpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGlnbm9yZUxvY2F0aW9uID0gYXdhaXQgaWdub3JlRWxlbWVudC5nZXRMb2NhdGlvbigpO1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBpZ25vcmVTaXplID0gYXdhaXQgaWdub3JlRWxlbWVudC5nZXRTaXplKCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIEFkanVzdCBjb29yZGluYXRlcywgYXMgdGhlIHNjcmVlbnNob3QgY2FuIGJlIHR3aWNlIHRoZSB2aWV3cG9ydCBzaXplIGZvciBoaWdoIHJlc29sdXRpb24gZGlzcGxheXMuXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBhcmVudFNpemUgPSBwYXJlbnRFbGVtZW50LmRyaXZlciBcbiAgICAgICAgICAgICAgICAgICAgICAgID8gYXdhaXQgcGFyZW50RWxlbWVudC5kcml2ZXIubWFuYWdlKCkud2luZG93KCkuZ2V0U2l6ZSgpIFxuICAgICAgICAgICAgICAgICAgICAgICAgOiBhd2FpdCBwYXJlbnRFbGVtZW50LmdldFNpemUoKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgY29udmVyc2lvbkZhY3RvciA9IGltYWdlUG5nLndpZHRoIC8gcGFyZW50U2l6ZS53aWR0aDtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fYmxhY2tvdXRSZWN0YW5nbGUoXG4gICAgICAgICAgICAgICAgICAgICAgICBpbWFnZVBuZyxcbiAgICAgICAgICAgICAgICAgICAgICAgIChpZ25vcmVMb2NhdGlvbi54IC0gcGFyZW50TG9jYXRpb24ueCkgKiBjb252ZXJzaW9uRmFjdG9yLFxuICAgICAgICAgICAgICAgICAgICAgICAgKGlnbm9yZUxvY2F0aW9uLnkgLSBwYXJlbnRMb2NhdGlvbi55KSAqIGNvbnZlcnNpb25GYWN0b3IsXG4gICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmVTaXplLndpZHRoICogY29udmVyc2lvbkZhY3RvciwgaWdub3JlU2l6ZS5oZWlnaHQgKiBjb252ZXJzaW9uRmFjdG9yXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gUE5HLnN5bmMud3JpdGUoaW1hZ2VQbmcpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGltYWdlQnVmZmVyO1xuICAgIH1cbn1cbiJdfQ==