creevey
Version:
creevey is a tool for automated visual testing, that tightly integrated with storybook
300 lines (260 loc) • 43.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getBrowser = getBrowser;
exports.switchStory = switchStory;
var _https = _interopRequireDefault(require("https"));
var _pngjs = require("pngjs");
var _seleniumWebdriver = require("selenium-webdriver");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const LOCALHOST_REGEXP = /(localhost|127\.0\.0\.1)/gi;
const TESTKONTUR_REGEXP = /testkontur/gi;
function getRealIp() {
return new Promise((resolve, reject) => _https.default.get('https://fake.testkontur.ru/ip', res => {
if (res.statusCode !== 200) {
return reject(new Error(`Couldn't resolve real ip for \`localhost\`. Status code: ${res.statusCode}`));
}
let data = '';
res.setEncoding('utf8');
res.on('data', chunk => data += chunk);
res.on('end', () => resolve(data));
}));
}
async function resetMousePosition(browser) {
const isChrome = (await browser.getCapabilities()).get('browserName') == 'chrome';
const {
top,
left,
width,
height
} = await browser.executeScript(function () {
/* eslint-disable no-var */
// NOTE On storybook >= 4.x already reset scroll
window.scrollTo(0, 0);
var bodyRect = document.body.getBoundingClientRect();
return {
top: bodyRect.top,
left: bodyRect.left,
width: bodyRect.width,
height: bodyRect.height
};
/* eslint-enable no-var */
});
if (isChrome) {
// NOTE Bridge mode not support move mouse relative viewport
await browser.actions({
bridge: true
}).move({
origin: browser.findElement(_seleniumWebdriver.By.css('body')),
x: Math.ceil(-1 * width / 2) - left,
y: Math.ceil(-1 * height / 2) - top
}).perform();
} else {
// NOTE Firefox for some reason moving by 0 x 0 move cursor in bottom left corner :sad:
// NOTE IE don't emit move events until force window focus or connect by RDP on virtual machine
await browser.actions().move({
origin: _seleniumWebdriver.Origin.VIEWPORT,
x: 0,
y: 1
}).perform();
}
}
async function resizeViewport(browser, viewport) {
const windowRect = await browser.manage().window().getRect();
const {
innerWidth,
innerHeight
} = await browser.executeScript(function () {
return {
innerWidth: window.innerWidth,
innerHeight: window.innerHeight
};
});
const dWidth = windowRect.width - innerWidth;
const dHeight = windowRect.height - innerHeight;
await browser.manage().window().setRect({
width: viewport.width + dWidth,
height: viewport.height + dHeight
});
}
function disableAnimations(browser) {
const disableAnimationsStyles = `
*,
*:hover,
*::before,
*::after {
animation-delay: -0.0001ms !important;
animation-duration: 0s !important;
animation-play-state: paused !important;
cursor: none !important;
caret-color: transparent !important;
transition: 0s !important;
}
`;
return browser.executeScript(function (stylesheet) {
/* eslint-disable no-var */
var style = document.createElement('style');
var textNode = document.createTextNode(stylesheet);
style.setAttribute('type', 'text/css');
style.appendChild(textNode);
document.head.appendChild(style);
/* eslint-enable no-var */
}, disableAnimationsStyles);
}
const getScrollBarWidth = (() => {
let scrollBarWidth = null;
return async browser => {
if (scrollBarWidth != null) return Promise.resolve(scrollBarWidth);
scrollBarWidth = await browser.executeScript(function () {
// eslint-disable-next-line no-var
var div = document.createElement('div');
div.innerHTML = 'a'; // NOTE: In IE clientWidth is 0 if this div is empty.
div.style.overflowY = 'scroll';
document.body.appendChild(div); // eslint-disable-next-line no-var
var widthDiff = div.offsetWidth - div.clientWidth;
document.body.removeChild(div);
return widthDiff;
});
return scrollBarWidth;
};
})();
async function takeCompositeScreenshot(browser, windowRect, elementRect) {
const screens = [];
const browserName = (await browser.getCapabilities()).get('browserName'); // NOTE Firefox and Safari take viewport screenshot without scrollbars
const isScreenshotWithoutScrollBar = browserName == 'firefox' || browserName == 'Safari';
const scrollBarWidth = await getScrollBarWidth(browser); // NOTE Sometimes viewport has been scrolled somewhere
const normalizedElementRect = {
left: elementRect.left - windowRect.x,
right: elementRect.right - windowRect.x,
top: elementRect.top - windowRect.y,
bottom: elementRect.bottom - windowRect.y
};
const isFitHorizontally = windowRect.width >= elementRect.width + normalizedElementRect.left;
const isFitVertically = windowRect.height >= elementRect.height + normalizedElementRect.top;
const viewportWidth = windowRect.width - (isFitVertically ? 0 : scrollBarWidth);
const viewportHeight = windowRect.height - (isFitHorizontally ? 0 : scrollBarWidth);
const cols = Math.ceil(elementRect.width / viewportWidth);
const rows = Math.ceil(elementRect.height / viewportHeight);
const xOffset = Math.round(isFitHorizontally ? normalizedElementRect.left : Math.max(0, cols * viewportWidth - elementRect.width));
const yOffset = Math.round(isFitVertically ? normalizedElementRect.top : Math.max(0, rows * viewportHeight - elementRect.height));
for (let row = 0; row < rows; row += 1) {
for (let col = 0; col < cols; col += 1) {
const dx = Math.min(viewportWidth * col + normalizedElementRect.left, Math.max(0, normalizedElementRect.right - viewportWidth));
const dy = Math.min(viewportHeight * row + normalizedElementRect.top, Math.max(0, normalizedElementRect.bottom - viewportHeight));
await browser.executeScript(function (x, y) {
window.scrollTo(x, y);
}, dx, dy);
screens.push((await browser.takeScreenshot()));
}
}
const images = screens.map(s => Buffer.from(s, 'base64')).map(b => _pngjs.PNG.sync.read(b));
const compositeImage = new _pngjs.PNG({
width: Math.round(elementRect.width),
height: Math.round(elementRect.height)
});
for (let y = 0; y < compositeImage.height; y += 1) {
for (let x = 0; x < compositeImage.width; x += 1) {
const col = Math.floor(x / viewportWidth);
const row = Math.floor(y / viewportHeight);
const isLastCol = cols - col == 1;
const isLastRow = rows - row == 1;
const scrollOffset = isFitVertically || isScreenshotWithoutScrollBar ? 0 : scrollBarWidth;
const i = (y * compositeImage.width + x) * 4;
const j = // NOTE compositeImage(x, y) => image(x, y)
(y % viewportHeight * (viewportWidth + scrollOffset) + x % viewportWidth) * 4 + ( // NOTE Offset for last row/col image
isLastRow ? yOffset * (viewportWidth + scrollOffset) * 4 : 0) + (isLastCol ? xOffset * 4 : 0);
const image = images[row * cols + col];
compositeImage.data[i + 0] = image.data[j + 0];
compositeImage.data[i + 1] = image.data[j + 1];
compositeImage.data[i + 2] = image.data[j + 2];
compositeImage.data[i + 3] = image.data[j + 3];
}
}
return _pngjs.PNG.sync.write(compositeImage).toString('base64');
}
async function takeScreenshot(browser, captureElement) {
if (!captureElement) return browser.takeScreenshot();
const {
elementRect,
windowRect
} = await browser.executeScript(function (selector) {
var _document$querySelect;
window.scrollTo(0, 0);
return {
elementRect: (_document$querySelect = document.querySelector(selector)) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.getBoundingClientRect(),
windowRect: {
width: window.innerWidth,
height: window.innerHeight,
x: Math.round(window.scrollX),
y: Math.round(window.scrollY)
}
};
}, captureElement);
const isFitIntoViewport = elementRect.width + elementRect.left <= windowRect.width && elementRect.height + elementRect.top <= windowRect.height;
if (isFitIntoViewport) return browser.findElement(_seleniumWebdriver.By.css(captureElement)).takeScreenshot();
return takeCompositeScreenshot(browser, windowRect, elementRect);
}
async function selectStory(browser, storyId, kind, story) {
const errorMessage = await browser.executeAsyncScript(function (storyId, kind, name, callback) {
window.__CREEVEY_SELECT_STORY__(storyId, kind, name, callback);
}, storyId, kind, story);
if (errorMessage) throw new Error(errorMessage);
}
async function getBrowser(config, browserConfig) {
const {
gridUrl = config.gridUrl,
storybookUrl: address = config.storybookUrl,
limit,
testRegex,
viewport,
...capabilities
} = browserConfig;
void limit;
void testRegex;
let realAddress = address;
if (LOCALHOST_REGEXP.test(address) && TESTKONTUR_REGEXP.test(gridUrl)) {
realAddress = address.replace(LOCALHOST_REGEXP, (await getRealIp()));
}
const browser = await new _seleniumWebdriver.Builder().usingServer(gridUrl).withCapabilities(capabilities).build();
if (viewport) {
await resizeViewport(browser, viewport);
}
try {
await browser.get(`${realAddress}/iframe.html`);
await browser.wait(_seleniumWebdriver.until.elementLocated(_seleniumWebdriver.By.css('#root')), 10000);
} catch (_) {
throw new Error(`Can't load storybook root page by URL ${realAddress}/iframe.html`);
}
await disableAnimations(browser);
return browser;
}
async function switchStory() {
var _this$currentTest, _this$currentTest$ctx, _story$parameters$cre;
let testOrSuite = this.currentTest;
if (!testOrSuite) throw new Error("Can't switch story, because test context doesn't have 'currentTest' field");
this.testScope.length = 0;
this.testScope.push(this.browserName);
while ((_testOrSuite = testOrSuite) === null || _testOrSuite === void 0 ? void 0 : _testOrSuite.title) {
var _testOrSuite;
this.testScope.push(testOrSuite.title);
testOrSuite = testOrSuite.parent;
}
const story = (_this$currentTest = this.currentTest) === null || _this$currentTest === void 0 ? void 0 : (_this$currentTest$ctx = _this$currentTest.ctx) === null || _this$currentTest$ctx === void 0 ? void 0 : _this$currentTest$ctx.story;
if (!story) throw new Error(`Current test '${this.testScope.join('/')}' context doesn't have 'story' field`);
await resetMousePosition(this.browser);
await selectStory(this.browser, story.id, story.kind, story.name);
const {
captureElement
} = (_story$parameters$cre = story.parameters.creevey) !== null && _story$parameters$cre !== void 0 ? _story$parameters$cre : {};
if (captureElement) Object.defineProperty(this, 'captureElement', {
enumerable: true,
configurable: true,
get() {
return this.browser.findElement(_seleniumWebdriver.By.css(captureElement));
}
});else Reflect.deleteProperty(this, 'captureElement');
this.takeScreenshot = () => takeScreenshot(this.browser, captureElement);
this.testScope.reverse();
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9zZWxlbml1bS50cyJdLCJuYW1lcyI6WyJMT0NBTEhPU1RfUkVHRVhQIiwiVEVTVEtPTlRVUl9SRUdFWFAiLCJnZXRSZWFsSXAiLCJQcm9taXNlIiwicmVzb2x2ZSIsInJlamVjdCIsImh0dHBzIiwiZ2V0IiwicmVzIiwic3RhdHVzQ29kZSIsIkVycm9yIiwiZGF0YSIsInNldEVuY29kaW5nIiwib24iLCJjaHVuayIsInJlc2V0TW91c2VQb3NpdGlvbiIsImJyb3dzZXIiLCJpc0Nocm9tZSIsImdldENhcGFiaWxpdGllcyIsInRvcCIsImxlZnQiLCJ3aWR0aCIsImhlaWdodCIsImV4ZWN1dGVTY3JpcHQiLCJ3aW5kb3ciLCJzY3JvbGxUbyIsImJvZHlSZWN0IiwiZG9jdW1lbnQiLCJib2R5IiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwiYWN0aW9ucyIsImJyaWRnZSIsIm1vdmUiLCJvcmlnaW4iLCJmaW5kRWxlbWVudCIsIkJ5IiwiY3NzIiwieCIsIk1hdGgiLCJjZWlsIiwieSIsInBlcmZvcm0iLCJPcmlnaW4iLCJWSUVXUE9SVCIsInJlc2l6ZVZpZXdwb3J0Iiwidmlld3BvcnQiLCJ3aW5kb3dSZWN0IiwibWFuYWdlIiwiZ2V0UmVjdCIsImlubmVyV2lkdGgiLCJpbm5lckhlaWdodCIsImRXaWR0aCIsImRIZWlnaHQiLCJzZXRSZWN0IiwiZGlzYWJsZUFuaW1hdGlvbnMiLCJkaXNhYmxlQW5pbWF0aW9uc1N0eWxlcyIsInN0eWxlc2hlZXQiLCJzdHlsZSIsImNyZWF0ZUVsZW1lbnQiLCJ0ZXh0Tm9kZSIsImNyZWF0ZVRleHROb2RlIiwic2V0QXR0cmlidXRlIiwiYXBwZW5kQ2hpbGQiLCJoZWFkIiwiZ2V0U2Nyb2xsQmFyV2lkdGgiLCJzY3JvbGxCYXJXaWR0aCIsImRpdiIsImlubmVySFRNTCIsIm92ZXJmbG93WSIsIndpZHRoRGlmZiIsIm9mZnNldFdpZHRoIiwiY2xpZW50V2lkdGgiLCJyZW1vdmVDaGlsZCIsInRha2VDb21wb3NpdGVTY3JlZW5zaG90IiwiZWxlbWVudFJlY3QiLCJzY3JlZW5zIiwiYnJvd3Nlck5hbWUiLCJpc1NjcmVlbnNob3RXaXRob3V0U2Nyb2xsQmFyIiwibm9ybWFsaXplZEVsZW1lbnRSZWN0IiwicmlnaHQiLCJib3R0b20iLCJpc0ZpdEhvcml6b250YWxseSIsImlzRml0VmVydGljYWxseSIsInZpZXdwb3J0V2lkdGgiLCJ2aWV3cG9ydEhlaWdodCIsImNvbHMiLCJyb3dzIiwieE9mZnNldCIsInJvdW5kIiwibWF4IiwieU9mZnNldCIsInJvdyIsImNvbCIsImR4IiwibWluIiwiZHkiLCJwdXNoIiwidGFrZVNjcmVlbnNob3QiLCJpbWFnZXMiLCJtYXAiLCJzIiwiQnVmZmVyIiwiZnJvbSIsImIiLCJQTkciLCJzeW5jIiwicmVhZCIsImNvbXBvc2l0ZUltYWdlIiwiZmxvb3IiLCJpc0xhc3RDb2wiLCJpc0xhc3RSb3ciLCJzY3JvbGxPZmZzZXQiLCJpIiwiaiIsImltYWdlIiwid3JpdGUiLCJ0b1N0cmluZyIsImNhcHR1cmVFbGVtZW50Iiwic2VsZWN0b3IiLCJxdWVyeVNlbGVjdG9yIiwic2Nyb2xsWCIsInNjcm9sbFkiLCJpc0ZpdEludG9WaWV3cG9ydCIsInNlbGVjdFN0b3J5Iiwic3RvcnlJZCIsImtpbmQiLCJzdG9yeSIsImVycm9yTWVzc2FnZSIsImV4ZWN1dGVBc3luY1NjcmlwdCIsIm5hbWUiLCJjYWxsYmFjayIsIl9fQ1JFRVZFWV9TRUxFQ1RfU1RPUllfXyIsImdldEJyb3dzZXIiLCJjb25maWciLCJicm93c2VyQ29uZmlnIiwiZ3JpZFVybCIsInN0b3J5Ym9va1VybCIsImFkZHJlc3MiLCJsaW1pdCIsInRlc3RSZWdleCIsImNhcGFiaWxpdGllcyIsInJlYWxBZGRyZXNzIiwidGVzdCIsInJlcGxhY2UiLCJCdWlsZGVyIiwidXNpbmdTZXJ2ZXIiLCJ3aXRoQ2FwYWJpbGl0aWVzIiwiYnVpbGQiLCJ3YWl0IiwidW50aWwiLCJlbGVtZW50TG9jYXRlZCIsIl8iLCJzd2l0Y2hTdG9yeSIsInRlc3RPclN1aXRlIiwiY3VycmVudFRlc3QiLCJ0ZXN0U2NvcGUiLCJsZW5ndGgiLCJ0aXRsZSIsInBhcmVudCIsImN0eCIsImpvaW4iLCJpZCIsInBhcmFtZXRlcnMiLCJjcmVldmV5IiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJlbnVtZXJhYmxlIiwiY29uZmlndXJhYmxlIiwiUmVmbGVjdCIsImRlbGV0ZVByb3BlcnR5IiwicmV2ZXJzZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFBQTs7QUFDQTs7QUFFQTs7OztBQVNBLE1BQU1BLGdCQUFnQixHQUFHLDRCQUF6QjtBQUNBLE1BQU1DLGlCQUFpQixHQUFHLGNBQTFCOztBQUVBLFNBQVNDLFNBQVQsR0FBc0M7QUFDcEMsU0FBTyxJQUFJQyxPQUFKLENBQVksQ0FBQ0MsT0FBRCxFQUFVQyxNQUFWLEtBQ2pCQyxlQUFNQyxHQUFOLENBQVUsK0JBQVYsRUFBNENDLEdBQUQsSUFBUztBQUNsRCxRQUFJQSxHQUFHLENBQUNDLFVBQUosS0FBbUIsR0FBdkIsRUFBNEI7QUFDMUIsYUFBT0osTUFBTSxDQUFDLElBQUlLLEtBQUosQ0FBVyw0REFBMkRGLEdBQUcsQ0FBQ0MsVUFBVyxFQUFyRixDQUFELENBQWI7QUFDRDs7QUFFRCxRQUFJRSxJQUFJLEdBQUcsRUFBWDtBQUVBSCxJQUFBQSxHQUFHLENBQUNJLFdBQUosQ0FBZ0IsTUFBaEI7QUFDQUosSUFBQUEsR0FBRyxDQUFDSyxFQUFKLENBQU8sTUFBUCxFQUFnQkMsS0FBRCxJQUFZSCxJQUFJLElBQUlHLEtBQW5DO0FBQ0FOLElBQUFBLEdBQUcsQ0FBQ0ssRUFBSixDQUFPLEtBQVAsRUFBYyxNQUFNVCxPQUFPLENBQUNPLElBQUQsQ0FBM0I7QUFDRCxHQVZELENBREssQ0FBUDtBQWFEOztBQUVELGVBQWVJLGtCQUFmLENBQWtDQyxPQUFsQyxFQUFxRTtBQUNuRSxRQUFNQyxRQUFRLEdBQUcsQ0FBQyxNQUFNRCxPQUFPLENBQUNFLGVBQVIsRUFBUCxFQUFrQ1gsR0FBbEMsQ0FBc0MsYUFBdEMsS0FBd0QsUUFB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O0FBRUEsTUFBSTlDLGNBQUosRUFDRStDLE1BQU0sQ0FBQ0MsY0FBUCxDQUFzQixJQUF0QixFQUE0QixnQkFBNUIsRUFBOEM7QUFDNUNDLElBQUFBLFVBQVUsRUFBRSxJQURnQztBQUU1Q0MsSUFBQUEsWUFBWSxFQUFFLElBRjhCOztBQUc1Q2hLLElBQUFBLEdBQUcsR0FBRztBQUNKLGFBQU8sS0FBS1MsT0FBTCxDQUFha0IsV0FBYixDQUF5QkMsc0JBQUdDLEdBQUgsQ0FBT2lGLGNBQVAsQ0FBekIsQ0FBUDtBQUNEOztBQUwyQyxHQUE5QyxFQURGLEtBUUttRCxPQUFPLENBQUNDLGNBQVIsQ0FBdUIsSUFBdkIsRUFBNkIsZ0JBQTdCOztBQUVMLE9BQUt4RSxjQUFMLEdBQXNCLE1BQU1BLGNBQWMsQ0FBQyxLQUFLakYsT0FBTixFQUFlcUcsY0FBZixDQUExQzs7QUFFQSxPQUFLc0MsU0FBTCxDQUFlZSxPQUFmO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xuaW1wb3J0IHsgUE5HIH0gZnJvbSAncG5nanMnO1xuaW1wb3J0IHsgQ29udGV4dCwgVGVzdCwgU3VpdGUgfSBmcm9tICdtb2NoYSc7XG5pbXBvcnQgeyBCdWlsZGVyLCBCeSwgdW50aWwsIFdlYkRyaXZlciwgT3JpZ2luIH0gZnJvbSAnc2VsZW5pdW0td2ViZHJpdmVyJztcbmltcG9ydCB7IENvbmZpZywgQnJvd3NlckNvbmZpZywgU3RvcnlJbnB1dCB9IGZyb20gJy4vdHlwZXMnO1xuXG5kZWNsYXJlIGdsb2JhbCB7XG4gIGludGVyZmFjZSBXaW5kb3cge1xuICAgIF9fQ1JFRVZFWV9SRVNUT1JFX1NDUk9MTF9fPzogKCkgPT4gdm9pZDtcbiAgfVxufVxuXG5jb25zdCBMT0NBTEhPU1RfUkVHRVhQID0gLyhsb2NhbGhvc3R8MTI3XFwuMFxcLjBcXC4xKS9naTtcbmNvbnN0IFRFU1RLT05UVVJfUkVHRVhQID0gL3Rlc3Rrb250dXIvZ2k7XG5cbmZ1bmN0aW9uIGdldFJlYWxJcCgpOiBQcm9taXNlPHN0cmluZz4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT5cbiAgICBodHRwcy5nZXQoJ2h0dHBzOi8vZmFrZS50ZXN0a29udHVyLnJ1L2lwJywgKHJlcykgPT4ge1xuICAgICAgaWYgKHJlcy5zdGF0dXNDb2RlICE9PSAyMDApIHtcbiAgICAgICAgcmV0dXJuIHJlamVjdChuZXcgRXJyb3IoYENvdWxkbid0IHJlc29sdmUgcmVhbCBpcCBmb3IgXFxgbG9jYWxob3N0XFxgLiBTdGF0dXMgY29kZTogJHtyZXMuc3RhdHVzQ29kZX1gKSk7XG4gICAgICB9XG5cbiAgICAgIGxldCBkYXRhID0gJyc7XG5cbiAgICAgIHJlcy5zZXRFbmNvZGluZygndXRmOCcpO1xuICAgICAgcmVzLm9uKCdkYXRhJywgKGNodW5rKSA9PiAoZGF0YSArPSBjaHVuaykpO1xuICAgICAgcmVzLm9uKCdlbmQnLCAoKSA9PiByZXNvbHZlKGRhdGEpKTtcbiAgICB9KSxcbiAgKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gcmVzZXRNb3VzZVBvc2l0aW9uKGJyb3dzZXI6IFdlYkRyaXZlcik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBpc0Nocm9tZSA9IChhd2FpdCBicm93c2VyLmdldENhcGFiaWxpdGllcygpKS5nZXQoJ2Jyb3dzZXJOYW1lJykgPT0gJ2Nocm9tZSc7XG4gIGNvbnN0IHsgdG9wLCBsZWZ0LCB3aWR0aCwgaGVpZ2h0IH0gPSBhd2FpdCBicm93c2VyLmV4ZWN1dGVTY3JpcHQoZnVuY3Rpb24gKCkge1xuICAgIC8qIGVzbGludC1kaXNhYmxlIG5vLXZhciAqL1xuICAgIC8vIE5PVEUgT24gc3Rvcnlib29rID49IDQueCBhbHJlYWR5IHJlc2V0IHNjcm9sbFxuICAgIHdpbmRvdy5zY3JvbGxUbygwLCAwKTtcblxuICAgIHZhciBib2R5UmVjdCA9IGRvY3VtZW50LmJvZHkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHRvcDogYm9keVJlY3QudG9wLFxuICAgICAgbGVmdDogYm9keVJlY3QubGVmdCxcbiAgICAgIHdpZHRoOiBib2R5UmVjdC53aWR0aCxcbiAgICAgIGhlaWdodDogYm9keVJlY3QuaGVpZ2h0LFxuICAgIH07XG4gICAgLyogZXNsaW50LWVuYWJsZSBuby12YXIgKi9cbiAgfSk7XG5cbiAgaWYgKGlzQ2hyb21lKSB7XG4gICAgLy8gTk9URSBCcmlkZ2UgbW9kZSBub3Qgc3VwcG9ydCBtb3ZlIG1vdXNlIHJlbGF0aXZlIHZpZXdwb3J0XG4gICAgYXdhaXQgYnJvd3NlclxuICAgICAgLmFjdGlvbnMoeyBicmlkZ2U6IHRydWUgfSlcbiAgICAgIC5tb3ZlKHtcbiAgICAgICAgb3JpZ2luOiBicm93c2VyLmZpbmRFbGVtZW50KEJ5LmNzcygnYm9keScpKSxcbiAgICAgICAgeDogTWF0aC5jZWlsKCgtMSAqIHdpZHRoKSAvIDIpIC0gbGVmdCxcbiAgICAgICAgeTogTWF0aC5jZWlsKCgtMSAqIGhlaWdodCkgLyAyKSAtIHRvcCxcbiAgICAgIH0pXG4gICAgICAucGVyZm9ybSgpO1xuICB9IGVsc2Uge1xuICAgIC8vIE5PVEUgRmlyZWZveCBmb3Igc29tZSByZWFzb24gbW92aW5nIGJ5IDAgeCAwIG1vdmUgY3Vyc29yIGluIGJvdHRvbSBsZWZ0IGNvcm5lciA6c2FkOlxuICAgIC8vIE5PVEUgSUUgZG9uJ3QgZW1pdCBtb3ZlIGV2ZW50cyB1bnRpbCBmb3JjZSB3aW5kb3cgZm9jdXMgb3IgY29ubmVjdCBieSBSRFAgb24gdmlydHVhbCBtYWNoaW5lXG4gICAgYXdhaXQgYnJvd3Nlci5hY3Rpb25zKCkubW92ZSh7IG9yaWdpbjogT3JpZ2luLlZJRVdQT1JULCB4OiAwLCB5OiAxIH0pLnBlcmZvcm0oKTtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiByZXNpemVWaWV3cG9ydChicm93c2VyOiBXZWJEcml2ZXIsIHZpZXdwb3J0OiB7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3Qgd2luZG93UmVjdCA9IGF3YWl0IGJyb3dzZXIubWFuYWdlKCkud2luZG93KCkuZ2V0UmVjdCgpO1xuICBjb25zdCB7IGlubmVyV2lkdGgsIGlubmVySGVpZ2h0IH0gPSBhd2FpdCBicm93c2VyLmV4ZWN1dGVTY3JpcHQoZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiB7XG4gICAgICBpbm5lcldpZHRoOiB3aW5kb3cuaW5uZXJXaWR0aCxcbiAgICAgIGlubmVySGVpZ2h0OiB3aW5kb3cuaW5uZXJIZWlnaHQsXG4gICAgfTtcbiAgfSk7XG4gIGNvbnN0IGRXaWR0aCA9IHdpbmRvd1JlY3Qud2lkdGggLSBpbm5lcldpZHRoO1xuICBjb25zdCBkSGVpZ2h0ID0gd2luZG93UmVjdC5oZWlnaHQgLSBpbm5lckhlaWdodDtcbiAgYXdhaXQgYnJvd3NlclxuICAgIC5tYW5hZ2UoKVxuICAgIC53aW5kb3coKVxuICAgIC5zZXRSZWN0KHtcbiAgICAgIHdpZHRoOiB2aWV3cG9ydC53aWR0aCArIGRXaWR0aCxcbiAgICAgIGhlaWdodDogdmlld3BvcnQuaGVpZ2h0ICsgZEhlaWdodCxcbiAgICB9KTtcbn1cblxuZnVuY3Rpb24gZGlzYWJsZUFuaW1hdGlvbnMoYnJvd3NlcjogV2ViRHJpdmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGRpc2FibGVBbmltYXRpb25zU3R5bGVzID0gYFxuKixcbio6aG92ZXIsXG4qOjpiZWZvcmUsXG4qOjphZnRlciB7XG4gIGFuaW1hdGlvbi1kZWxheTogLTAuMDAwMW1zICFpbXBvcnRhbnQ7XG4gIGFuaW1hdGlvbi1kdXJhdGlvbjogMHMgIWltcG9ydGFudDtcbiAgYW5pbWF0aW9uLXBsYXktc3RhdGU6IHBhdXNlZCAhaW1wb3J0YW50O1xuICBjdXJzb3I6IG5vbmUgIWltcG9ydGFudDtcbiAgY2FyZXQtY29sb3I6IHRyYW5zcGFyZW50ICFpbXBvcnRhbnQ7XG4gIHRyYW5zaXRpb246IDBzICFpbXBvcnRhbnQ7XG59XG5gO1xuICByZXR1cm4gYnJvd3Nlci5leGVjdXRlU2NyaXB0KGZ1bmN0aW9uIChzdHlsZXNoZWV0OiBzdHJpbmcpIHtcbiAgICAvKiBlc2xpbnQtZGlzYWJsZSBuby12YXIgKi9cbiAgICB2YXIgc3R5bGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzdHlsZScpO1xuICAgIHZhciB0ZXh0Tm9kZSA9IGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKHN0eWxlc2hlZXQpO1xuICAgIHN0eWxlLnNldEF0dHJpYnV0ZSgndHlwZScsICd0ZXh0L2NzcycpO1xuICAgIHN0eWxlLmFwcGVuZENoaWxkKHRleHROb2RlKTtcbiAgICBkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHN0eWxlKTtcbiAgICAvKiBlc2xpbnQtZW5hYmxlIG5vLXZhciAqL1xuICB9LCBkaXNhYmxlQW5pbWF0aW9uc1N0eWxlcyk7XG59XG5cbmNvbnN0IGdldFNjcm9sbEJhcldpZHRoOiAoYnJvd3NlcjogV2ViRHJpdmVyKSA9PiBQcm9taXNlPG51bWJlcj4gPSAoKCkgPT4ge1xuICBsZXQgc2Nyb2xsQmFyV2lkdGg6IG51bWJlciB8IG51bGwgPSBudWxsO1xuXG4gIHJldHVybiBhc3luYyAoYnJvd3NlcjogV2ViRHJpdmVyKTogUHJvbWlzZTxudW1iZXI+ID0+IHtcbiAgICBpZiAoc2Nyb2xsQmFyV2lkdGggIT0gbnVsbCkgcmV0dXJuIFByb21pc2UucmVzb2x2ZShzY3JvbGxCYXJXaWR0aCk7XG4gICAgc2Nyb2xsQmFyV2lkdGggPSBhd2FpdCBicm93c2VyLmV4ZWN1dGVTY3JpcHQ8bnVtYmVyPihmdW5jdGlvbiAoKSB7XG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdmFyXG4gICAgICB2YXIgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICBkaXYuaW5uZXJIVE1MID0gJ2EnOyAvLyBOT1RFOiBJbiBJRSBjbGllbnRXaWR0aCBpcyAwIGlmIHRoaXMgZGl2IGlzIGVtcHR5LlxuICAgICAgZGl2LnN0eWxlLm92ZXJmbG93WSA9ICdzY3JvbGwnO1xuICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChkaXYpO1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXZhclxuICAgICAgdmFyIHdpZHRoRGlmZiA9IGRpdi5vZmZzZXRXaWR0aCAtIGRpdi5jbGllbnRXaWR0aDtcbiAgICAgIGRvY3VtZW50LmJvZHkucmVtb3ZlQ2hpbGQoZGl2KTtcblxuICAgICAgcmV0dXJuIHdpZHRoRGlmZjtcbiAgICB9KTtcbiAgICByZXR1cm4gc2Nyb2xsQmFyV2lkdGg7XG4gIH07XG59KSgpO1xuXG5hc3luYyBmdW5jdGlvbiB0YWtlQ29tcG9zaXRlU2NyZWVuc2hvdChcbiAgYnJvd3NlcjogV2ViRHJpdmVyLFxuICB3aW5kb3dSZWN0OiB7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyOyB4OiBudW1iZXI7IHk6IG51bWJlciB9LFxuICBlbGVtZW50UmVjdDogRE9NUmVjdCxcbik6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHNjcmVlbnMgPSBbXTtcbiAgY29uc3QgYnJvd3Nlck5hbWUgPSAoYXdhaXQgYnJvd3Nlci5nZXRDYXBhYmlsaXRpZXMoKSkuZ2V0KCdicm93c2VyTmFtZScpO1xuICAvLyBOT1RFIEZpcmVmb3ggYW5kIFNhZmFyaSB0YWtlIHZpZXdwb3J0IHNjcmVlbnNob3Qgd2l0aG91dCBzY3JvbGxiYXJzXG4gIGNvbnN0IGlzU2NyZWVuc2hvdFdpdGhvdXRTY3JvbGxCYXIgPSBicm93c2VyTmFtZSA9PSAnZmlyZWZveCcgfHwgYnJvd3Nlck5hbWUgPT0gJ1NhZmFyaSc7XG4gIGNvbnN0IHNjcm9sbEJhcldpZHRoID0gYXdhaXQgZ2V0U2Nyb2xsQmFyV2lkdGgoYnJvd3Nlcik7XG4gIC8vIE5PVEUgU29tZXRpbWVzIHZpZXdwb3J0IGhhcyBiZWVuIHNjcm9sbGVkIHNvbWV3aGVyZVxuICBjb25zdCBub3JtYWxpemVkRWxlbWVudFJlY3QgPSB7XG4gICAgbGVmdDogZWxlbWVudFJlY3QubGVmdCAtIHdpbmRvd1JlY3QueCxcbiAgICByaWdodDogZWxlbWVudFJlY3QucmlnaHQgLSB3aW5kb3dSZWN0LngsXG4gICAgdG9wOiBlbGVtZW50UmVjdC50b3AgLSB3aW5kb3dSZWN0LnksXG4gICAgYm90dG9tOiBlbGVtZW50UmVjdC5ib3R0b20gLSB3aW5kb3dSZWN0LnksXG4gIH07XG4gIGNvbnN0IGlzRml0SG9yaXpvbnRhbGx5ID0gd2luZG93UmVjdC53aWR0aCA+PSBlbGVtZW50UmVjdC53aWR0aCArIG5vcm1hbGl6ZWRFbGVtZW50UmVjdC5sZWZ0O1xuICBjb25zdCBpc0ZpdFZlcnRpY2FsbHkgPSB3aW5kb3dSZWN0LmhlaWdodCA+PSBlbGVtZW50UmVjdC5oZWlnaHQgKyBub3JtYWxpemVkRWxlbWVudFJlY3QudG9wO1xuICBjb25zdCB2aWV3cG9ydFdpZHRoID0gd2luZG93UmVjdC53aWR0aCAtIChpc0ZpdFZlcnRpY2FsbHkgPyAwIDogc2Nyb2xsQmFyV2lkdGgpO1xuICBjb25zdCB2aWV3cG9ydEhlaWdodCA9IHdpbmRvd1JlY3QuaGVpZ2h0IC0gKGlzRml0SG9yaXpvbnRhbGx5ID8gMCA6IHNjcm9sbEJhcldpZHRoKTtcbiAgY29uc3QgY29scyA9IE1hdGguY2VpbChlbGVtZW50UmVjdC53aWR0aCAvIHZpZXdwb3J0V2lkdGgpO1xuICBjb25zdCByb3dzID0gTWF0aC5jZWlsKGVsZW1lbnRSZWN0LmhlaWdodCAvIHZpZXdwb3J0SGVpZ2h0KTtcbiAgY29uc3QgeE9mZnNldCA9IE1hdGgucm91bmQoXG4gICAgaXNGaXRIb3Jpem9udGFsbHkgPyBub3JtYWxpemVkRWxlbWVudFJlY3QubGVmdCA6IE1hdGgubWF4KDAsIGNvbHMgKiB2aWV3cG9ydFdpZHRoIC0gZWxlbWVudFJlY3Qud2lkdGgpLFxuICApO1xuICBjb25zdCB5T2Zmc2V0ID0gTWF0aC5yb3VuZChcbiAgICBpc0ZpdFZlcnRpY2FsbHkgPyBub3JtYWxpemVkRWxlbWVudFJlY3QudG9wIDogTWF0aC5tYXgoMCwgcm93cyAqIHZpZXdwb3J0SGVpZ2h0IC0gZWxlbWVudFJlY3QuaGVpZ2h0KSxcbiAgKTtcblxuICBmb3IgKGxldCByb3cgPSAwOyByb3cgPCByb3dzOyByb3cgKz0gMSkge1xuICAgIGZvciAobGV0IGNvbCA9IDA7IGNvbCA8IGNvbHM7IGNvbCArPSAxKSB7XG4gICAgICBjb25zdCBkeCA9IE1hdGgubWluKFxuICAgICAgICB2aWV3cG9ydFdpZHRoICogY29sICsgbm9ybWFsaXplZEVsZW1lbnRSZWN0LmxlZnQsXG4gICAgICAgIE1hdGgubWF4KDAsIG5vcm1hbGl6ZWRFbGVtZW50UmVjdC5yaWdodCAtIHZpZXdwb3J0V2lkdGgpLFxuICAgICAgKTtcbiAgICAgIGNvbnN0IGR5ID0gTWF0aC5taW4oXG4gICAgICAgIHZpZXdwb3J0SGVpZ2h0ICogcm93ICsgbm9ybWFsaXplZEVsZW1lbnRSZWN0LnRvcCxcbiAgICAgICAgTWF0aC5tYXgoMCwgbm9ybWFsaXplZEVsZW1lbnRSZWN0LmJvdHRvbSAtIHZpZXdwb3J0SGVpZ2h0KSxcbiAgICAgICk7XG4gICAgICBhd2FpdCBicm93c2VyLmV4ZWN1dGVTY3JpcHQoXG4gICAgICAgIGZ1bmN0aW9uICh4OiBudW1iZXIsIHk6IG51bWJlcikge1xuICAgICAgICAgIHdpbmRvdy5zY3JvbGxUbyh4LCB5KTtcbiAgICAgICAgfSxcbiAgICAgICAgZHgsXG4gICAgICAgIGR5LFxuICAgICAgKTtcbiAgICAgIHNjcmVlbnMucHVzaChhd2FpdCBicm93c2VyLnRha2VTY3JlZW5zaG90KCkpO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IGltYWdlcyA9IHNjcmVlbnMubWFwKChzKSA9PiBCdWZmZXIuZnJvbShzLCAnYmFzZTY0JykpLm1hcCgoYikgPT4gUE5HLnN5bmMucmVhZChiKSk7XG4gIGNvbnN0IGNvbXBvc2l0ZUltYWdlID0gbmV3IFBORyh7IHdpZHRoOiBNYXRoLnJvdW5kKGVsZW1lbnRSZWN0LndpZHRoKSwgaGVpZ2h0OiBNYXRoLnJvdW5kKGVsZW1lbnRSZWN0LmhlaWdodCkgfSk7XG5cbiAgZm9yIChsZXQgeSA9IDA7IHkgPCBjb21wb3NpdGVJbWFnZS5oZWlnaHQ7IHkgKz0gMSkge1xuICAgIGZvciAobGV0IHggPSAwOyB4IDwgY29tcG9zaXRlSW1hZ2Uud2lkdGg7IHggKz0gMSkge1xuICAgICAgY29uc3QgY29sID0gTWF0aC5mbG9vcih4IC8gdmlld3BvcnRXaWR0aCk7XG4gICAgICBjb25zdCByb3cgPSBNYXRoLmZsb29yKHkgLyB2aWV3cG9ydEhlaWdodCk7XG4gICAgICBjb25zdCBpc0xhc3RDb2wgPSBjb2xzIC0gY29sID09IDE7XG4gICAgICBjb25zdCBpc0xhc3RSb3cgPSByb3dzIC0gcm93ID09IDE7XG4gICAgICBjb25zdCBzY3JvbGxPZmZzZXQgPSBpc0ZpdFZlcnRpY2FsbHkgfHwgaXNTY3JlZW5zaG90V2l0aG91dFNjcm9sbEJhciA/IDAgOiBzY3JvbGxCYXJXaWR0aDtcbiAgICAgIGNvbnN0IGkgPSAoeSAqIGNvbXBvc2l0ZUltYWdlLndpZHRoICsgeCkgKiA0O1xuICAgICAgY29uc3QgaiA9XG4gICAgICAgIC8vIE5PVEUgY29tcG9zaXRlSW1hZ2UoeCwgeSkgPT4gaW1hZ2UoeCwgeSlcbiAgICAgICAgKCh5ICUgdmlld3BvcnRIZWlnaHQpICogKHZpZXdwb3J0V2lkdGggKyBzY3JvbGxPZmZzZXQpICsgKHggJSB2aWV3cG9ydFdpZHRoKSkgKiA0ICtcbiAgICAgICAgLy8gTk9URSBPZmZzZXQgZm9yIGxhc3Qgcm93L2NvbCBpbWFnZVxuICAgICAgICAoaXNMYXN0Um93ID8geU9mZnNldCAqICh2aWV3cG9ydFdpZHRoICsgc2Nyb2xsT2Zmc2V0KSAqIDQgOiAwKSArXG4gICAgICAgIChpc0xhc3RDb2wgPyB4T2Zmc2V0ICogNCA6IDApO1xuICAgICAgY29uc3QgaW1hZ2UgPSBpbWFnZXNbcm93ICogY29scyArIGNvbF07XG4gICAgICBjb21wb3NpdGVJbWFnZS5kYXRhW2kgKyAwXSA9IGltYWdlLmRhdGFbaiArIDBdO1xuICAgICAgY29tcG9zaXRlSW1hZ2UuZGF0YVtpICsgMV0gPSBpbWFnZS5kYXRhW2ogKyAxXTtcbiAgICAgIGNvbXBvc2l0ZUltYWdlLmRhdGFbaSArIDJdID0gaW1hZ2UuZGF0YVtqICsgMl07XG4gICAgICBjb21wb3NpdGVJbWFnZS5kYXRhW2kgKyAzXSA9IGltYWdlLmRhdGFbaiArIDNdO1xuICAgIH1cbiAgfVxuICByZXR1cm4gUE5HLnN5bmMud3JpdGUoY29tcG9zaXRlSW1hZ2UpLnRvU3RyaW5nKCdiYXNlNjQnKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gdGFrZVNjcmVlbnNob3QoYnJvd3NlcjogV2ViRHJpdmVyLCBjYXB0dXJlRWxlbWVudD86IHN0cmluZyB8IG51bGwpOiBQcm9taXNlPHN0cmluZz4ge1xuICBpZiAoIWNhcHR1cmVFbGVtZW50KSByZXR1cm4gYnJvd3Nlci50YWtlU2NyZWVuc2hvdCgpO1xuXG4gIGNvbnN0IHsgZWxlbWVudFJlY3QsIHdpbmRvd1JlY3QgfSA9IGF3YWl0IGJyb3dzZXIuZXhlY3V0ZVNjcmlwdChmdW5jdGlvbiAoc2VsZWN0b3I6IHN0cmluZykge1xuICAgIHdpbmRvdy5zY3JvbGxUbygwLCAwKTtcbiAgICByZXR1cm4ge1xuICAgICAgZWxlbWVudFJlY3Q6IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3Ioc2VsZWN0b3IpPy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSxcbiAgICAgIHdpbmRvd1JlY3Q6IHtcbiAgICAgICAgd2lkdGg6IHdpbmRvdy5pbm5lcldpZHRoLFxuICAgICAgICBoZWlnaHQ6IHdpbmRvdy5pbm5lckhlaWdodCxcbiAgICAgICAgeDogTWF0aC5yb3VuZCh3aW5kb3cuc2Nyb2xsWCksXG4gICAgICAgIHk6IE1hdGgucm91bmQod2luZG93LnNjcm9sbFkpLFxuICAgICAgfSxcbiAgICB9O1xuICB9LCBjYXB0dXJlRWxlbWVudCk7XG5cbiAgY29uc3QgaXNGaXRJbnRvVmlld3BvcnQgPVxuICAgIGVsZW1lbnRSZWN0LndpZHRoICsgZWxlbWVudFJlY3QubGVmdCA8PSB3aW5kb3dSZWN0LndpZHRoICYmXG4gICAgZWxlbWVudFJlY3QuaGVpZ2h0ICsgZWxlbWVudFJlY3QudG9wIDw9IHdpbmRvd1JlY3QuaGVpZ2h0O1xuXG4gIGlmIChpc0ZpdEludG9WaWV3cG9ydCkgcmV0dXJuIGJyb3dzZXIuZmluZEVsZW1lbnQoQnkuY3NzKGNhcHR1cmVFbGVtZW50KSkudGFrZVNjcmVlbnNob3QoKTtcblxuICByZXR1cm4gdGFrZUNvbXBvc2l0ZVNjcmVlbnNob3QoYnJvd3Nlciwgd2luZG93UmVjdCwgZWxlbWVudFJlY3QpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBzZWxlY3RTdG9yeShicm93c2VyOiBXZWJEcml2ZXIsIHN0b3J5SWQ6IHN0cmluZywga2luZDogc3RyaW5nLCBzdG9yeTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGVycm9yTWVzc2FnZSA9IGF3YWl0IGJyb3dzZXIuZXhlY3V0ZUFzeW5jU2NyaXB0PHN0cmluZyB8IHVuZGVmaW5lZD4oXG4gICAgZnVuY3Rpb24gKHN0b3J5SWQ6IHN0cmluZywga2luZDogc3RyaW5nLCBuYW1lOiBzdHJpbmcsIGNhbGxiYWNrOiAoZXJyb3I/OiBzdHJpbmcpID0+IHZvaWQpIHtcbiAgICAgIHdpbmRvdy5fX0NSRUVWRVlfU0VMRUNUX1NUT1JZX18oc3RvcnlJZCwga2luZCwgbmFtZSwgY2FsbGJhY2spO1xuICAgIH0sXG4gICAgc3RvcnlJZCxcbiAgICBraW5kLFxuICAgIHN0b3J5LFxuICApO1xuICBpZiAoZXJyb3JNZXNzYWdlKSB0aHJvdyBuZXcgRXJyb3IoZXJyb3JNZXNzYWdlKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldEJyb3dzZXIoY29uZmlnOiBDb25maWcsIGJyb3dzZXJDb25maWc6IEJyb3dzZXJDb25maWcpOiBQcm9taXNlPFdlYkRyaXZlcj4ge1xuICBjb25zdCB7XG4gICAgZ3JpZFVybCA9IGNvbmZpZy5ncmlkVXJsLFxuICAgIHN0b3J5Ym9va1VybDogYWRkcmVzcyA9IGNvbmZpZy5zdG9yeWJvb2tVcmwsXG4gICAgbGltaXQsXG4gICAgdGVzdFJlZ2V4LFxuICAgIHZpZXdwb3J0LFxuICAgIC4uLmNhcGFiaWxpdGllc1xuICB9ID0gYnJvd3NlckNvbmZpZztcbiAgdm9pZCBsaW1pdDtcbiAgdm9pZCB0ZXN0UmVnZXg7XG4gIGxldCByZWFsQWRkcmVzcyA9IGFkZHJlc3M7XG4gIGlmIChMT0NBTEhPU1RfUkVHRVhQLnRlc3QoYWRkcmVzcykgJiYgVEVTVEtPTlRVUl9SRUdFWFAudGVzdChncmlkVXJsKSkge1xuICAgIHJlYWxBZGRyZXNzID0gYWRkcmVzcy5yZXBsYWNlKExPQ0FMSE9TVF9SRUdFWFAsIGF3YWl0IGdldFJlYWxJcCgpKTtcbiAgfVxuICBjb25zdCBicm93c2VyID0gYXdhaXQgbmV3IEJ1aWxkZXIoKS51c2luZ1NlcnZlcihncmlkVXJsKS53aXRoQ2FwYWJpbGl0aWVzKGNhcGFiaWxpdGllcykuYnVpbGQoKTtcblxuICBpZiAodmlld3BvcnQpIHtcbiAgICBhd2FpdCByZXNpemVWaWV3cG9ydChicm93c2VyLCB2aWV3cG9ydCk7XG4gIH1cbiAgdHJ5IHtcbiAgICBhd2FpdCBicm93c2VyLmdldChgJHtyZWFsQWRkcmVzc30vaWZyYW1lLmh0bWxgKTtcbiAgICBhd2FpdCBicm93c2VyLndhaXQodW50aWwuZWxlbWVudExvY2F0ZWQoQnkuY3NzKCcjcm9vdCcpKSwgMTAwMDApO1xuICB9IGNhdGNoIChfKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBDYW4ndCBsb2FkIHN0b3J5Ym9vayByb290IHBhZ2UgYnkgVVJMICR7cmVhbEFkZHJlc3N9L2lmcmFtZS5odG1sYCk7XG4gIH1cbiAgYXdhaXQgZGlzYWJsZUFuaW1hdGlvbnMoYnJvd3Nlcik7XG5cbiAgcmV0dXJuIGJyb3dzZXI7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzd2l0Y2hTdG9yeSh0aGlzOiBDb250ZXh0KTogUHJvbWlzZTx2b2lkPiB7XG4gIGxldCB0ZXN0T3JTdWl0ZTogVGVzdCB8IFN1aXRlIHwgdW5kZWZpbmVkID0gdGhpcy5jdXJyZW50VGVzdDtcblxuICBpZiAoIXRlc3RPclN1aXRlKSB0aHJvdyBuZXcgRXJyb3IoXCJDYW4ndCBzd2l0Y2ggc3RvcnksIGJlY2F1c2UgdGVzdCBjb250ZXh0IGRvZXNuJ3QgaGF2ZSAnY3VycmVudFRlc3QnIGZpZWxkXCIpO1xuXG4gIHRoaXMudGVzdFNjb3BlLmxlbmd0aCA9IDA7XG4gIHRoaXMudGVzdFNjb3BlLnB1c2godGhpcy5icm93c2VyTmFtZSk7XG4gIHdoaWxlICh0ZXN0T3JTdWl0ZT8udGl0bGUpIHtcbiAgICB0aGlzLnRlc3RTY29wZS5wdXNoKHRlc3RPclN1aXRlLnRpdGxlKTtcbiAgICB0ZXN0T3JTdWl0ZSA9IHRlc3RPclN1aXRlLnBhcmVudDtcbiAgfVxuICBjb25zdCBzdG9yeTogU3RvcnlJbnB1dCB8IHVuZGVmaW5lZCA9IHRoaXMuY3VycmVudFRlc3Q/LmN0eD8uc3Rvcnk7XG5cbiAgaWYgKCFzdG9yeSkgdGhyb3cgbmV3IEVycm9yKGBDdXJyZW50IHRlc3QgJyR7dGhpcy50ZXN0U2NvcGUuam9pbignLycpfScgY29udGV4dCBkb2Vzbid0IGhhdmUgJ3N0b3J5JyBmaWVsZGApO1xuXG4gIGF3YWl0IHJlc2V0TW91c2VQb3NpdGlvbih0aGlzLmJyb3dzZXIpO1xuICBhd2FpdCBzZWxlY3RTdG9yeSh0aGlzLmJyb3dzZXIsIHN0b3J5LmlkLCBzdG9yeS5raW5kLCBzdG9yeS5uYW1lKTtcblxuICBjb25zdCB7IGNhcHR1cmVFbGVtZW50IH0gPSBzdG9yeS5wYXJhbWV0ZXJzLmNyZWV2ZXkgPz8ge307XG5cbiAgaWYgKGNhcHR1cmVFbGVtZW50KVxuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0aGlzLCAnY2FwdHVyZUVsZW1lbnQnLCB7XG4gICAgICBlbnVtZXJhYmxlOiB0cnVlLFxuICAgICAgY29uZmlndXJhYmxlOiB0cnVlLFxuICAgICAgZ2V0KCkge1xuICAgICAgICByZXR1cm4gdGhpcy5icm93c2VyLmZpbmRFbGVtZW50KEJ5LmNzcyhjYXB0dXJlRWxlbWVudCkpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgZWxzZSBSZWZsZWN0LmRlbGV0ZVByb3BlcnR5KHRoaXMsICdjYXB0dXJlRWxlbWVudCcpO1xuXG4gIHRoaXMudGFrZVNjcmVlbnNob3QgPSAoKSA9PiB0YWtlU2NyZWVuc2hvdCh0aGlzLmJyb3dzZXIsIGNhcHR1cmVFbGVtZW50KTtcblxuICB0aGlzLnRlc3RTY29wZS5yZXZlcnNlKCk7XG59XG4iXX0=