protractor-screenshot-extension
Version:
An extension for Protractor that allows testing visual screenshots
249 lines • 27.4 kB
JavaScript
/**
* @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==