protractor-screenshot-extension
Version:
An extension for Protractor that allows testing visual screenshots
193 lines • 23.6 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 {?} */
const pixelmatch = require('pixelmatch');
/** @type {?} */
const 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 {?} */
const actualDirectory = 'actual';
/** @type {?} */
const diffDirectory = 'diff';
/** @type {?} */
const baselineDirectory = 'baseline';
export class ProtractorScreenshotExtension {
/**
* @param {?} screenshotDirectory
*/
constructor(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 {?}
*/
checkElementScreenshot(element, tag, options) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
/** @type {?} */
const baselineImagePath = `${this._screenshotDirectory}/${baselineDirectory}/${tag}.png`;
if (!existsSync(baselineImagePath)) {
console.log(`Saving baseline image: ${baselineImagePath}`);
/** @type {?} */
let screenshot = Buffer.from(yield element.takeScreenshot(), 'base64');
screenshot = yield this._addBlackoutRectangles(screenshot, options, element);
/** @type {?} */
const stream = createWriteStream(baselineImagePath);
stream.write(screenshot);
stream.end();
return 0;
}
/** @type {?} */
let testScreenshotBuffer = Buffer.from(yield element.takeScreenshot(), 'base64');
testScreenshotBuffer = yield this._addBlackoutRectangles(testScreenshotBuffer, options, element);
/** @type {?} */
const testScreenshotPng = PNG.sync.read(testScreenshotBuffer);
/** @type {?} */
const baselineScreenshotPng = PNG.sync.read(readFileSync(baselineImagePath));
/** @type {?} */
const diffPng = new PNG({ width: baselineScreenshotPng.width, height: baselineScreenshotPng.height });
/** @type {?} */
const numberOfPixelsDifferent = pixelmatch(testScreenshotPng.data, baselineScreenshotPng.data, diffPng.data, baselineScreenshotPng.width, baselineScreenshotPng.height, {
threshold: options ? options.threshold : undefined,
includeAA: options ? options.includeAA : undefined
});
if (!!numberOfPixelsDifferent) {
/** @type {?} */
const diffImagePath = `${this._screenshotDirectory}/${diffDirectory}/${tag}.png`;
/** @type {?} */
const actualImagePath = `${this._screenshotDirectory}/${actualDirectory}/${tag}.png`;
createWriteStream(actualImagePath).write(testScreenshotBuffer);
diffPng.pack().pipe(createWriteStream(diffImagePath));
}
return numberOfPixelsDifferent;
});
}
/**
* @param {?} tag
* @param {?=} options
* @return {?}
*/
checkPageScreenshot(tag, options) {
return this.checkElementScreenshot(browser, tag, options);
}
/**
* @private
* @param {?} image
* @param {?} x
* @param {?} y
* @param {?} w
* @param {?} h
* @return {?}
*/
_blackoutRectangle(image, x, y, w, h) {
// Create a black image that will be used for blacking out regions to be ignored.
/** @type {?} */
const blackPng = new PNG({ width: w, height: h });
for (let i = 0; i < blackPng.height; i++) {
for (let j = 0; j < blackPng.width; j++) {
/** @type {?} */
const 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 {?}
*/
_addBlackoutRectangles(imageBuffer, options, parentElement) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (options && (options.ignoreRectangles || options.ignoreElements)) {
/** @type {?} */
const imagePng = PNG.sync.read(imageBuffer);
if (options.ignoreRectangles && options.ignoreRectangles.length) {
options.ignoreRectangles.forEach((rect) => {
this._blackoutRectangle(imagePng, rect.x, rect.y, rect.w, rect.h);
});
}
if (options.ignoreElements && options.ignoreElements.length) {
// Using `map` instead of `forEach` in order to await all async calls.
yield Promise.all(options.ignoreElements.map((ignoreElement) => tslib_1.__awaiter(this, void 0, void 0, function* () {
/** @type {?} */
let parentLocation = { x: 0, y: 0 };
if (!!parentElement.getLocation) {
// If an ElementFinder was passed in, call `getLocation()`, otherwise assume the whole browser was passed in.
parentLocation = yield parentElement.getLocation();
}
/** @type {?} */
const ignoreLocation = yield ignoreElement.getLocation();
/** @type {?} */
const ignoreSize = yield ignoreElement.getSize();
// Adjust coordinates, as the screenshot can be twice the viewport size for high resolution displays.
/** @type {?} */
const parentSize = parentElement.driver
? yield parentElement.driver.manage().window().getSize()
: yield parentElement.getSize();
/** @type {?} */
const conversionFactor = imagePng.width / parentSize.width;
this._blackoutRectangle(imagePng, (ignoreLocation.x - parentLocation.x) * conversionFactor, (ignoreLocation.y - parentLocation.y) * conversionFactor, ignoreSize.width * conversionFactor, ignoreSize.height * conversionFactor);
})));
}
return PNG.sync.write(imagePng);
}
return imageBuffer;
});
}
}
if (false) {
/**
* @type {?}
* @private
*/
ProtractorScreenshotExtension.prototype._screenshotDirectory;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdHJhY3Rvci1zY3JlZW5zaG90LWV4dGVuc2lvbi51dGlsaXR5LmpzIiwic291cmNlUm9vdCI6Im5nOi8vcHJvdHJhY3Rvci1zY3JlZW5zaG90LWV4dGVuc2lvbi8iLCJzb3VyY2VzIjpbImxpYi9wcm90cmFjdG9yLXNjcmVlbnNob3QtZXh0ZW5zaW9uLnV0aWxpdHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxNQUFNLElBQUksQ0FBQztBQUNqRSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sT0FBTyxDQUFDO0FBQzVCLE9BQU8sRUFBRSxPQUFPLEVBQW9DLE1BQU0sWUFBWSxDQUFDOztNQUNqRSxVQUFVLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQzs7TUFDbEMsS0FBSyxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUM7Ozs7QUFFaEMsZ0NBS0M7OztJQUpHLHVCQUFVOztJQUNWLHVCQUFVOztJQUNWLHVCQUFVOztJQUNWLHVCQUFVOzs7OztBQUdkLHdDQUtDOzs7SUFKRyw4Q0FBZ0M7O0lBQ2hDLDRDQUFpQzs7SUFDakMsdUNBQW1COztJQUNuQix1Q0FBb0I7OztNQUdsQixlQUFlLEdBQUcsUUFBUTs7TUFDMUIsYUFBYSxHQUFHLE1BQU07O01BQ3RCLGlCQUFpQixHQUFHLFVBQVU7QUFFcEMsTUFBTSxPQUFPLDZCQUE2Qjs7OztJQUd0QyxZQUFZLG1CQUEyQjtRQUNuQyxJQUFJLENBQUMsb0JBQW9CLEdBQUcsbUJBQW1CLENBQUM7UUFDaEQsSUFBSSxJQUFJLENBQUMsb0JBQW9CLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssR0FBRyxFQUFFO1lBQzdDLElBQUksQ0FBQyxvQkFBb0IsR0FBRyxJQUFJLENBQUMsb0JBQW9CLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ3RFO1FBQ0QsMkJBQTJCO1FBQzNC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LENBQUMsTUFBTTs0QkFDbkMsQ0FBQyxDQUFDLE1BQU0sYUFBYSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLEVBQUU7NEJBQ3hELENBQUMsQ0FBQyxNQUFNLGFBQWEsQ0FBQyxPQUFPLEVBQUU7OzhCQUM3QixnQkFBZ0IsR0FBRyxRQUFRLENBQUMsS0FBSyxHQUFHLFVBQVUsQ0FBQyxLQUFLO3dCQUMxRCxJQUFJLENBQUMsa0JBQWtCLENBQ25CLFFBQVEsRUFDUixDQUFDLGNBQWMsQ0FBQyxDQUFDLEdBQUcsY0FBYyxDQUFDLENBQUMsQ0FBQyxHQUFHLGdCQUFnQixFQUN4RCxDQUFDLGNBQWMsQ0FBQyxDQUFDLEdBQUcsY0FBYyxDQUFDLENBQUMsQ0FBQyxHQUFHLGdCQUFnQixFQUN4RCxVQUFVLENBQUMsS0FBSyxHQUFHLGdCQUFnQixFQUFFLFVBQVUsQ0FBQyxNQUFNLEdBQUcsZ0JBQWdCLENBQzVFLENBQUM7b0JBQ04sQ0FBQyxDQUFBLENBQUMsQ0FBQyxDQUFDO2lCQUNQO2dCQUVELE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUM7YUFDbkM7WUFFRCxPQUFPLFdBQVcsQ0FBQztRQUN2QixDQUFDO0tBQUE7Q0FDSjs7Ozs7O0lBbkhHLDZEQUFxQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNyZWF0ZVdyaXRlU3RyZWFtLCBleGlzdHNTeW5jLCByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XG5pbXBvcnQgeyBQTkcgfSBmcm9tICdwbmdqcyc7XG5pbXBvcnQgeyBicm93c2VyLCBFbGVtZW50RmluZGVyLCBQcm90cmFjdG9yQnJvd3NlciB9IGZyb20gJ3Byb3RyYWN0b3InO1xuY29uc3QgcGl4ZWxtYXRjaCA9IHJlcXVpcmUoJ3BpeGVsbWF0Y2gnKTtcbmNvbnN0IHNoZWxsID0gcmVxdWlyZSgnc2hlbGxqcycpO1xuXG5leHBvcnQgaW50ZXJmYWNlIElSZWN0YW5nbGUge1xuICAgIHg6IG51bWJlcjtcbiAgICB5OiBudW1iZXI7XG4gICAgdzogbnVtYmVyO1xuICAgIGg6IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJU2NyZWVuc2hvdE9wdGlvbnMge1xuICAgIGlnbm9yZVJlY3RhbmdsZXM/OiBJUmVjdGFuZ2xlW107XG4gICAgaWdub3JlRWxlbWVudHM/OiBFbGVtZW50RmluZGVyW107XG4gICAgdGhyZXNob2xkPzogbnVtYmVyOyAvLyBiZXR3ZWVuIDAgYW5kIDFcbiAgICBpbmNsdWRlQUE/OiBib29sZWFuO1xufVxuXG5jb25zdCBhY3R1YWxEaXJlY3RvcnkgPSAnYWN0dWFsJztcbmNvbnN0IGRpZmZEaXJlY3RvcnkgPSAnZGlmZic7XG5jb25zdCBiYXNlbGluZURpcmVjdG9yeSA9ICdiYXNlbGluZSc7XG5cbmV4cG9ydCBjbGFzcyBQcm90cmFjdG9yU2NyZWVuc2hvdEV4dGVuc2lvbiB7XG4gICAgcHJpdmF0ZSBfc2NyZWVuc2hvdERpcmVjdG9yeTogc3RyaW5nO1xuXG4gICAgY29uc3RydWN0b3Ioc2NyZWVuc2hvdERpcmVjdG9yeTogc3RyaW5nKSB7XG4gICAgICAgIHRoaXMuX3NjcmVlbnNob3REaXJlY3RvcnkgPSBzY3JlZW5zaG90RGlyZWN0b3J5O1xuICAgICAgICBpZiAodGhpcy5fc2NyZWVuc2hvdERpcmVjdG9yeS5zbGljZSgtMSkgPT09ICcvJykge1xuICAgICAgICAgICAgdGhpcy5fc2NyZWVuc2hvdERpcmVjdG9yeSA9IHRoaXMuX3NjcmVlbnNob3REaXJlY3Rvcnkuc2xpY2UoMCwgLTEpO1xuICAgICAgICB9XG4gICAgICAgIC8vIENyZWF0ZSBhbGwgMyBzdWJmb2xkZXJzLlxuICAgICAgICBzaGVsbC5ta2RpcignLXAnLCBgJHtzY3JlZW5zaG90RGlyZWN0b3J5fS8ke2FjdHVhbERpcmVjdG9yeX1gKTtcbiAgICAgICAgc2hlbGwubWtkaXIoJy1wJywgYCR7c2NyZWVuc2hvdERpcmVjdG9yeX0vJHtkaWZmRGlyZWN0b3J5fWApO1xuICAgICAgICBzaGVsbC5ta2RpcignLXAnLCBgJHtzY3JlZW5zaG90RGlyZWN0b3J5fS8ke2Jhc2VsaW5lRGlyZWN0b3J5fWApO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBjaGVja0VsZW1lbnRTY3JlZW5zaG90KFxuICAgICAgICBlbGVtZW50OiBFbGVtZW50RmluZGVyIHwgUHJvdHJhY3RvckJyb3dzZXIsXG4gICAgICAgIHRhZzogc3RyaW5nLFxuICAgICAgICBvcHRpb25zPzogSVNjcmVlbnNob3RPcHRpb25zXG4gICAgKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICAgICAgY29uc3QgYmFzZWxpbmVJbWFnZVBhdGggPSBgJHt0aGlzLl9zY3JlZW5zaG90RGlyZWN0b3J5fS8ke2Jhc2VsaW5lRGlyZWN0b3J5fS8ke3RhZ30ucG5nYDtcbiAgICAgICAgaWYgKCFleGlzdHNTeW5jKGJhc2VsaW5lSW1hZ2VQYXRoKSkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYFNhdmluZyBiYXNlbGluZSBpbWFnZTogJHtiYXNlbGluZUltYWdlUGF0aH1gKTtcbiAgICAgICAgICAgIGxldCBzY3JlZW5zaG90ID0gQnVmZmVyLmZyb20oYXdhaXQgZWxlbWVudC50YWtlU2NyZWVuc2hvdCgpLCAnYmFzZTY0Jyk7XG4gICAgICAgICAgICBzY3JlZW5zaG90ID0gYXdhaXQgdGhpcy5fYWRkQmxhY2tvdXRSZWN0YW5nbGVzKHNjcmVlbnNob3QsIG9wdGlvbnMsIGVsZW1lbnQpO1xuICAgICAgICAgICAgY29uc3Qgc3RyZWFtID0gY3JlYXRlV3JpdGVTdHJlYW0oYmFzZWxpbmVJbWFnZVBhdGgpO1xuICAgICAgICAgICAgc3RyZWFtLndyaXRlKHNjcmVlbnNob3QpO1xuICAgICAgICAgICAgc3RyZWFtLmVuZCgpO1xuICAgICAgICAgICAgcmV0dXJuIDA7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IHRlc3RTY3JlZW5zaG90QnVmZmVyID0gQnVmZmVyLmZyb20oYXdhaXQgZWxlbWVudC50YWtlU2NyZWVuc2hvdCgpLCAnYmFzZTY0Jyk7XG4gICAgICAgIHRlc3RTY3JlZW5zaG90QnVmZmVyID0gYXdhaXQgdGhpcy5fYWRkQmxhY2tvdXRSZWN0YW5nbGVzKHRlc3RTY3JlZW5zaG90QnVmZmVyLCBvcHRpb25zLCBlbGVtZW50KTtcbiAgICAgICAgY29uc3QgdGVzdFNjcmVlbnNob3RQbmcgPSBQTkcuc3luYy5yZWFkKHRlc3RTY3JlZW5zaG90QnVmZmVyKTtcbiAgICAgICAgY29uc3QgYmFzZWxpbmVTY3JlZW5zaG90UG5nID0gUE5HLnN5bmMucmVhZChyZWFkRmlsZVN5bmMoYmFzZWxpbmVJbWFnZVBhdGgpKTtcbiAgICAgICAgY29uc3QgZGlmZlBuZyA9IG5ldyBQTkcoeyB3aWR0aDogYmFzZWxpbmVTY3JlZW5zaG90UG5nLndpZHRoLCBoZWlnaHQ6IGJhc2VsaW5lU2NyZWVuc2hvdFBuZy5oZWlnaHQgfSk7XG4gICAgICAgIGNvbnN0IG51bWJlck9mUGl4ZWxzRGlmZmVyZW50ID0gcGl4ZWxtYXRjaChcbiAgICAgICAgICAgIHRlc3RTY3JlZW5zaG90UG5nLmRhdGEsXG4gICAgICAgICAgICBiYXNlbGluZVNjcmVlbnNob3RQbmcuZGF0YSxcbiAgICAgICAgICAgIGRpZmZQbmcuZGF0YSxcbiAgICAgICAgICAgIGJhc2VsaW5lU2NyZWVuc2hvdFBuZy53aWR0aCxcbiAgICAgICAgICAgIGJhc2VsaW5lU2NyZWVuc2hvdFBuZy5oZWlnaHQsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdGhyZXNob2xkOiBvcHRpb25zID8gb3B0aW9ucy50aHJlc2hvbGQgOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgaW5jbHVkZUFBOiBvcHRpb25zID8gb3B0aW9ucy5pbmNsdWRlQUEgOiB1bmRlZmluZWRcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICAgICAgaWYgKCEhbnVtYmVyT2ZQaXhlbHNEaWZmZXJlbnQpIHtcbiAgICAgICAgICAgIGNvbnN0IGRpZmZJbWFnZVBhdGggPSBgJHt0aGlzLl9zY3JlZW5zaG90RGlyZWN0b3J5fS8ke2RpZmZEaXJlY3Rvcnl9LyR7dGFnfS5wbmdgO1xuICAgICAgICAgICAgY29uc3QgYWN0dWFsSW1hZ2VQYXRoID0gYCR7dGhpcy5fc2NyZWVuc2hvdERpcmVjdG9yeX0vJHthY3R1YWxEaXJlY3Rvcnl9LyR7dGFnfS5wbmdgO1xuICAgICAgICAgICAgY3JlYXRlV3JpdGVTdHJlYW0oYWN0dWFsSW1hZ2VQYXRoKS53cml0ZSh0ZXN0U2NyZWVuc2hvdEJ1ZmZlcik7XG4gICAgICAgICAgICBkaWZmUG5nLnBhY2soKS5waXBlKGNyZWF0ZVdyaXRlU3RyZWFtKGRpZmZJbWFnZVBhdGgpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVtYmVyT2ZQaXhlbHNEaWZmZXJlbnQ7XG4gICAgfVxuXG4gICAgcHVibGljIGNoZWNrUGFnZVNjcmVlbnNob3QodGFnOiBzdHJpbmcsIG9wdGlvbnM/OiBJU2NyZWVuc2hvdE9wdGlvbnMpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgICAgICByZXR1cm4gdGhpcy5jaGVja0VsZW1lbnRTY3JlZW5zaG90KGJyb3dzZXIsIHRhZywgb3B0aW9ucyk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfYmxhY2tvdXRSZWN0YW5nbGUoaW1hZ2U6IFBORywgeDogbnVtYmVyLCB5OiBudW1iZXIsIHc6IG51bWJlciwgaDogbnVtYmVyKTogdm9pZCB7XG4gICAgICAgIC8vIENyZWF0ZSBhIGJsYWNrIGltYWdlIHRoYXQgd2lsbCBiZSB1c2VkIGZvciBibGFja2luZyBvdXQgcmVnaW9ucyB0byBiZSBpZ25vcmVkLlxuICAgICAgICBjb25zdCBibGFja1BuZyA9IG5ldyBQTkcoeyB3aWR0aDogdywgaGVpZ2h0OiBoIH0pO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJsYWNrUG5nLmhlaWdodDsgaSsrKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IGJsYWNrUG5nLndpZHRoOyBqKyspIHtcbiAgICAgICAgICAgICAgICBjb25zdCBpZHggPSAoYmxhY2tQbmcud2lkdGggKiBpICsgaikgKiA0O1xuICAgICAgICAgICAgICAgIGJsYWNrUG5nLmRhdGFbaWR4XSA9IDA7ICAgICAgICAgLy8gcmVkXG4gICAgICAgICAgICAgICAgYmxhY2tQbmcuZGF0YVtpZHggKyAxXSA9IDA7ICAgICAvLyBibHVlXG4gICAgICAgICAgICAgICAgYmxhY2tQbmcuZGF0YVtpZHggKyAyXSA9IDA7ICAgICAvLyBncmVlblxuICAgICAgICAgICAgICAgIGJsYWNrUG5nLmRhdGFbaWR4ICsgM10gPSAyNTU7ICAgLy8gYWxwaGFcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGJsYWNrUG5nLmJpdGJsdChpbWFnZSwgMCwgMCwgdywgaCwgeCwgeSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBfYWRkQmxhY2tvdXRSZWN0YW5nbGVzIChcbiAgICAgICAgaW1hZ2VCdWZmZXI6IEJ1ZmZlcixcbiAgICAgICAgb3B0aW9ucz86IElTY3JlZW5zaG90T3B0aW9ucyxcbiAgICAgICAgcGFyZW50RWxlbWVudD86IEVsZW1lbnRGaW5kZXIgfCBQcm90cmFjdG9yQnJvd3NlclxuICAgICk6IFByb21pc2U8QnVmZmVyPiB7XG4gICAgICAgIGlmIChvcHRpb25zICYmIChvcHRpb25zLmlnbm9yZVJlY3RhbmdsZXMgfHwgb3B0aW9ucy5pZ25vcmVFbGVtZW50cykpIHtcbiAgICAgICAgICAgIGNvbnN0IGltYWdlUG5nID0gUE5HLnN5bmMucmVhZChpbWFnZUJ1ZmZlcik7XG5cbiAgICAgICAgICAgIGlmIChvcHRpb25zLmlnbm9yZVJlY3RhbmdsZXMgJiYgb3B0aW9ucy5pZ25vcmVSZWN0YW5nbGVzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIG9wdGlvbnMuaWdub3JlUmVjdGFuZ2xlcy5mb3JFYWNoKChyZWN0KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2JsYWNrb3V0UmVjdGFuZ2xlKGltYWdlUG5nLCByZWN0LngsIHJlY3QueSwgcmVjdC53LCByZWN0LmgpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKG9wdGlvbnMuaWdub3JlRWxlbWVudHMgJiYgb3B0aW9ucy5pZ25vcmVFbGVtZW50cy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAvLyBVc2luZyBgbWFwYCBpbnN0ZWFkIG9mIGBmb3JFYWNoYCBpbiBvcmRlciB0byBhd2FpdCBhbGwgYXN5bmMgY2FsbHMuXG4gICAgICAgICAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwob3B0aW9ucy5pZ25vcmVFbGVtZW50cy5tYXAoYXN5bmMgKGlnbm9yZUVsZW1lbnQpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IHBhcmVudExvY2F0aW9uID0geyB4OiAwLCB5OiAwIH07XG4gICAgICAgICAgICAgICAgICAgIGlmICghIXBhcmVudEVsZW1lbnQuZ2V0TG9jYXRpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIElmIGFuIEVsZW1lbnRGaW5kZXIgd2FzIHBhc3NlZCBpbiwgY2FsbCBgZ2V0TG9jYXRpb24oKWAsIG90aGVyd2lzZSBhc3N1bWUgdGhlIHdob2xlIGJyb3dzZXIgd2FzIHBhc3NlZCBpbi5cbiAgICAgICAgICAgICAgICAgICAgICAgIHBhcmVudExvY2F0aW9uID0gYXdhaXQgcGFyZW50RWxlbWVudC5nZXRMb2NhdGlvbigpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGlnbm9yZUxvY2F0aW9uID0gYXdhaXQgaWdub3JlRWxlbWVudC5nZXRMb2NhdGlvbigpO1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBpZ25vcmVTaXplID0gYXdhaXQgaWdub3JlRWxlbWVudC5nZXRTaXplKCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIEFkanVzdCBjb29yZGluYXRlcywgYXMgdGhlIHNjcmVlbnNob3QgY2FuIGJlIHR3aWNlIHRoZSB2aWV3cG9ydCBzaXplIGZvciBoaWdoIHJlc29sdXRpb24gZGlzcGxheXMuXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBhcmVudFNpemUgPSBwYXJlbnRFbGVtZW50LmRyaXZlciBcbiAgICAgICAgICAgICAgICAgICAgICAgID8gYXdhaXQgcGFyZW50RWxlbWVudC5kcml2ZXIubWFuYWdlKCkud2luZG93KCkuZ2V0U2l6ZSgpIFxuICAgICAgICAgICAgICAgICAgICAgICAgOiBhd2FpdCBwYXJlbnRFbGVtZW50LmdldFNpemUoKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgY29udmVyc2lvbkZhY3RvciA9IGltYWdlUG5nLndpZHRoIC8gcGFyZW50U2l6ZS53aWR0aDtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fYmxhY2tvdXRSZWN0YW5nbGUoXG4gICAgICAgICAgICAgICAgICAgICAgICBpbWFnZVBuZyxcbiAgICAgICAgICAgICAgICAgICAgICAgIChpZ25vcmVMb2NhdGlvbi54IC0gcGFyZW50TG9jYXRpb24ueCkgKiBjb252ZXJzaW9uRmFjdG9yLFxuICAgICAgICAgICAgICAgICAgICAgICAgKGlnbm9yZUxvY2F0aW9uLnkgLSBwYXJlbnRMb2NhdGlvbi55KSAqIGNvbnZlcnNpb25GYWN0b3IsXG4gICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmVTaXplLndpZHRoICogY29udmVyc2lvbkZhY3RvciwgaWdub3JlU2l6ZS5oZWlnaHQgKiBjb252ZXJzaW9uRmFjdG9yXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4gUE5HLnN5bmMud3JpdGUoaW1hZ2VQbmcpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGltYWdlQnVmZmVyO1xuICAgIH1cbn1cbiJdfQ==