UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

202 lines 35.2 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { parallel } from './change-detection'; import { ComponentHarness, HarnessPredicate, } from './component-harness'; /** * Base harness environment class that can be extended to allow `ComponentHarness`es to be used in * different test environments (e.g. testbed, protractor, etc.). This class implements the * functionality of both a `HarnessLoader` and `LocatorFactory`. This class is generic on the raw * element type, `E`, used by the particular test environment. */ export class HarnessEnvironment { constructor(rawRootElement) { this.rawRootElement = rawRootElement; this.rootElement = this.createTestElement(rawRootElement); } // Implemented as part of the `LocatorFactory` interface. documentRootLocatorFactory() { return this.createEnvironment(this.getDocumentRoot()); } // Implemented as part of the `LocatorFactory` interface. locatorFor(...queries) { return () => _assertResultFound(this._getAllHarnessesAndTestElements(queries), _getDescriptionForLocatorForQueries(queries)); } // Implemented as part of the `LocatorFactory` interface. locatorForOptional(...queries) { return async () => (await this._getAllHarnessesAndTestElements(queries))[0] || null; } // Implemented as part of the `LocatorFactory` interface. locatorForAll(...queries) { return () => this._getAllHarnessesAndTestElements(queries); } // Implemented as part of the `LocatorFactory` interface. async rootHarnessLoader() { return this; } // Implemented as part of the `LocatorFactory` interface. async harnessLoaderFor(selector) { return this.createEnvironment(await _assertResultFound(this.getAllRawElements(selector), [ _getDescriptionForHarnessLoaderQuery(selector), ])); } // Implemented as part of the `LocatorFactory` interface. async harnessLoaderForOptional(selector) { const elements = await this.getAllRawElements(selector); return elements[0] ? this.createEnvironment(elements[0]) : null; } // Implemented as part of the `LocatorFactory` interface. async harnessLoaderForAll(selector) { const elements = await this.getAllRawElements(selector); return elements.map(element => this.createEnvironment(element)); } // Implemented as part of the `HarnessLoader` interface. getHarness(query) { return this.locatorFor(query)(); } // Implemented as part of the `HarnessLoader` interface. getAllHarnesses(query) { return this.locatorForAll(query)(); } // Implemented as part of the `HarnessLoader` interface. async getChildLoader(selector) { return this.createEnvironment(await _assertResultFound(this.getAllRawElements(selector), [ _getDescriptionForHarnessLoaderQuery(selector), ])); } // Implemented as part of the `HarnessLoader` interface. async getAllChildLoaders(selector) { return (await this.getAllRawElements(selector)).map(e => this.createEnvironment(e)); } /** Creates a `ComponentHarness` for the given harness type with the given raw host element. */ createComponentHarness(harnessType, element) { return new harnessType(this.createEnvironment(element)); } /** * Matches the given raw elements with the given list of element and harness queries to produce a * list of matched harnesses and test elements. */ async _getAllHarnessesAndTestElements(queries) { const { allQueries, harnessQueries, elementQueries, harnessTypes } = _parseQueries(queries); // Combine all of the queries into one large comma-delimited selector and use it to get all raw // elements matching any of the individual queries. const rawElements = await this.getAllRawElements([...elementQueries, ...harnessQueries.map(predicate => predicate.getSelector())].join(',')); // If every query is searching for the same harness subclass, we know every result corresponds // to an instance of that subclass. Likewise, if every query is for a `TestElement`, we know // every result corresponds to a `TestElement`. Otherwise we need to verify which result was // found by which selector so it can be matched to the appropriate instance. const skipSelectorCheck = (elementQueries.length === 0 && harnessTypes.size === 1) || harnessQueries.length === 0; const perElementMatches = await parallel(() => rawElements.map(async (rawElement) => { const testElement = this.createTestElement(rawElement); const allResultsForElement = await parallel( // For each query, get `null` if it doesn't match, or a `TestElement` or // `ComponentHarness` as appropriate if it does match. This gives us everything that // matches the current raw element, but it may contain duplicate entries (e.g. // multiple `TestElement` or multiple `ComponentHarness` of the same type). () => allQueries.map(query => this._getQueryResultForElement(query, rawElement, testElement, skipSelectorCheck))); return _removeDuplicateQueryResults(allResultsForElement); })); return [].concat(...perElementMatches); } /** * Check whether the given query matches the given element, if it does return the matched * `TestElement` or `ComponentHarness`, if it does not, return null. In cases where the caller * knows for sure that the query matches the element's selector, `skipSelectorCheck` can be used * to skip verification and optimize performance. */ async _getQueryResultForElement(query, rawElement, testElement, skipSelectorCheck = false) { if (typeof query === 'string') { return skipSelectorCheck || (await testElement.matchesSelector(query)) ? testElement : null; } if (skipSelectorCheck || (await testElement.matchesSelector(query.getSelector()))) { const harness = this.createComponentHarness(query.harnessType, rawElement); return (await query.evaluate(harness)) ? harness : null; } return null; } } /** * Parses a list of queries in the format accepted by the `locatorFor*` methods into an easier to * work with format. */ function _parseQueries(queries) { const allQueries = []; const harnessQueries = []; const elementQueries = []; const harnessTypes = new Set(); for (const query of queries) { if (typeof query === 'string') { allQueries.push(query); elementQueries.push(query); } else { const predicate = query instanceof HarnessPredicate ? query : new HarnessPredicate(query, {}); allQueries.push(predicate); harnessQueries.push(predicate); harnessTypes.add(predicate.harnessType); } } return { allQueries, harnessQueries, elementQueries, harnessTypes }; } /** * Removes duplicate query results for a particular element. (e.g. multiple `TestElement` * instances or multiple instances of the same `ComponentHarness` class. */ async function _removeDuplicateQueryResults(results) { let testElementMatched = false; let matchedHarnessTypes = new Set(); const dedupedMatches = []; for (const result of results) { if (!result) { continue; } if (result instanceof ComponentHarness) { if (!matchedHarnessTypes.has(result.constructor)) { matchedHarnessTypes.add(result.constructor); dedupedMatches.push(result); } } else if (!testElementMatched) { testElementMatched = true; dedupedMatches.push(result); } } return dedupedMatches; } /** Verifies that there is at least one result in an array. */ async function _assertResultFound(results, queryDescriptions) { const result = (await results)[0]; if (result == undefined) { throw Error(`Failed to find element matching one of the following queries:\n` + queryDescriptions.map(desc => `(${desc})`).join(',\n')); } return result; } /** Gets a list of description strings from a list of queries. */ function _getDescriptionForLocatorForQueries(queries) { return queries.map(query => typeof query === 'string' ? _getDescriptionForTestElementQuery(query) : _getDescriptionForComponentHarnessQuery(query)); } /** Gets a description string for a `ComponentHarness` query. */ function _getDescriptionForComponentHarnessQuery(query) { const harnessPredicate = query instanceof HarnessPredicate ? query : new HarnessPredicate(query, {}); const { name, hostSelector } = harnessPredicate.harnessType; const description = `${name} with host element matching selector: "${hostSelector}"`; const constraints = harnessPredicate.getDescription(); return (description + (constraints ? ` satisfying the constraints: ${harnessPredicate.getDescription()}` : '')); } /** Gets a description string for a `TestElement` query. */ function _getDescriptionForTestElementQuery(selector) { return `TestElement for element matching selector: "${selector}"`; } /** Gets a description string for a `HarnessLoader` query. */ function _getDescriptionForHarnessLoaderQuery(selector) { return `HarnessLoader for element matching selector: "${selector}"`; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFybmVzcy1lbnZpcm9ubWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9jZGsvdGVzdGluZy9oYXJuZXNzLWVudmlyb25tZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUVILE9BQU8sRUFBQyxRQUFRLEVBQUMsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QyxPQUFPLEVBRUwsZ0JBQWdCLEVBR2hCLGdCQUFnQixHQUlqQixNQUFNLHFCQUFxQixDQUFDO0FBcUI3Qjs7Ozs7R0FLRztBQUNILE1BQU0sT0FBZ0Isa0JBQWtCO0lBSXRDLFlBQWdDLGNBQWlCO1FBQWpCLG1CQUFjLEdBQWQsY0FBYyxDQUFHO1FBQy9DLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQzVELENBQUM7SUFFRCx5REFBeUQ7SUFDekQsMEJBQTBCO1FBQ3hCLE9BQU8sSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELENBQUM7SUFFRCx5REFBeUQ7SUFDekQsVUFBVSxDQUNSLEdBQUcsT0FBVTtRQUViLE9BQU8sR0FBRyxFQUFFLENBQ1Ysa0JBQWtCLENBQ2hCLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxPQUFPLENBQUMsRUFDN0MsbUNBQW1DLENBQUMsT0FBTyxDQUFDLENBQzdDLENBQUM7SUFDTixDQUFDO0lBRUQseURBQXlEO0lBQ3pELGtCQUFrQixDQUNoQixHQUFHLE9BQVU7UUFFYixPQUFPLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUN0RixDQUFDO0lBRUQseURBQXlEO0lBQ3pELGFBQWEsQ0FDWCxHQUFHLE9BQVU7UUFFYixPQUFPLEdBQUcsRUFB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QWlELFFBQVEsR0FBRyxDQUFDO0FBQ3RFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgR29vZ2xlIExMQyBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGFuIE1JVC1zdHlsZSBsaWNlbnNlIHRoYXQgY2FuIGJlXG4gKiBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGF0IGh0dHBzOi8vYW5ndWxhci5pby9saWNlbnNlXG4gKi9cblxuaW1wb3J0IHtwYXJhbGxlbH0gZnJvbSAnLi9jaGFuZ2UtZGV0ZWN0aW9uJztcbmltcG9ydCB7XG4gIEFzeW5jRmFjdG9yeUZuLFxuICBDb21wb25lbnRIYXJuZXNzLFxuICBDb21wb25lbnRIYXJuZXNzQ29uc3RydWN0b3IsXG4gIEhhcm5lc3NMb2FkZXIsXG4gIEhhcm5lc3NQcmVkaWNhdGUsXG4gIEhhcm5lc3NRdWVyeSxcbiAgTG9jYXRvckZhY3RvcnksXG4gIExvY2F0b3JGblJlc3VsdCxcbn0gZnJvbSAnLi9jb21wb25lbnQtaGFybmVzcyc7XG5pbXBvcnQge1Rlc3RFbGVtZW50fSBmcm9tICcuL3Rlc3QtZWxlbWVudCc7XG5cbi8qKiBQYXJzZWQgZm9ybSBvZiB0aGUgcXVlcmllcyBwYXNzZWQgdG8gdGhlIGBsb2NhdG9yRm9yKmAgbWV0aG9kcy4gKi9cbnR5cGUgUGFyc2VkUXVlcmllczxUIGV4dGVuZHMgQ29tcG9uZW50SGFybmVzcz4gPSB7XG4gIC8qKiBUaGUgZnVsbCBsaXN0IG9mIHF1ZXJpZXMsIGluIHRoZWlyIG9yaWdpbmFsIG9yZGVyLiAqL1xuICBhbGxRdWVyaWVzOiAoc3RyaW5nIHwgSGFybmVzc1ByZWRpY2F0ZTxUPilbXTtcbiAgLyoqXG4gICAqIEEgZmlsdGVyZWQgdmlldyBvZiBgYWxsUXVlcmllc2AgY29udGFpbmluZyBvbmx5IHRoZSBxdWVyaWVzIHRoYXQgYXJlIGxvb2tpbmcgZm9yIGFcbiAgICogYENvbXBvbmVudEhhcm5lc3NgXG4gICAqL1xuICBoYXJuZXNzUXVlcmllczogSGFybmVzc1ByZWRpY2F0ZTxUPltdO1xuICAvKipcbiAgICogQSBmaWx0ZXJlZCB2aWV3IG9mIGBhbGxRdWVyaWVzYCBjb250YWluaW5nIG9ubHkgdGhlIHF1ZXJpZXMgdGhhdCBhcmUgbG9va2luZyBmb3IgYVxuICAgKiBgVGVzdEVsZW1lbnRgXG4gICAqL1xuICBlbGVtZW50UXVlcmllczogc3RyaW5nW107XG4gIC8qKiBUaGUgc2V0IG9mIGFsbCBgQ29tcG9uZW50SGFybmVzc2Agc3ViY2xhc3NlcyByZXByZXNlbnRlZCBpbiB0aGUgb3JpZ2luYWwgcXVlcnkgbGlzdC4gKi9cbiAgaGFybmVzc1R5cGVzOiBTZXQ8Q29tcG9uZW50SGFybmVzc0NvbnN0cnVjdG9yPFQ+Pjtcbn07XG5cbi8qKlxuICogQmFzZSBoYXJuZXNzIGVudmlyb25tZW50IGNsYXNzIHRoYXQgY2FuIGJlIGV4dGVuZGVkIHRvIGFsbG93IGBDb21wb25lbnRIYXJuZXNzYGVzIHRvIGJlIHVzZWQgaW5cbiAqIGRpZmZlcmVudCB0ZXN0IGVudmlyb25tZW50cyAoZS5nLiB0ZXN0YmVkLCBwcm90cmFjdG9yLCBldGMuKS4gVGhpcyBjbGFzcyBpbXBsZW1lbnRzIHRoZVxuICogZnVuY3Rpb25hbGl0eSBvZiBib3RoIGEgYEhhcm5lc3NMb2FkZXJgIGFuZCBgTG9jYXRvckZhY3RvcnlgLiBUaGlzIGNsYXNzIGlzIGdlbmVyaWMgb24gdGhlIHJhd1xuICogZWxlbWVudCB0eXBlLCBgRWAsIHVzZWQgYnkgdGhlIHBhcnRpY3VsYXIgdGVzdCBlbnZpcm9ubWVudC5cbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEhhcm5lc3NFbnZpcm9ubWVudDxFPiBpbXBsZW1lbnRzIEhhcm5lc3NMb2FkZXIsIExvY2F0b3JGYWN0b3J5IHtcbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIHJvb3RFbGVtZW50OiBUZXN0RWxlbWVudDtcblxuICBwcm90ZWN0ZWQgY29uc3RydWN0b3IocHJvdGVjdGVkIHJhd1Jvb3RFbGVtZW50OiBFKSB7XG4gICAgdGhpcy5yb290RWxlbWVudCA9IHRoaXMuY3JlYXRlVGVzdEVsZW1lbnQocmF3Um9vdEVsZW1lbnQpO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIGRvY3VtZW50Um9vdExvY2F0b3JGYWN0b3J5KCk6IExvY2F0b3JGYWN0b3J5IHtcbiAgICByZXR1cm4gdGhpcy5jcmVhdGVFbnZpcm9ubWVudCh0aGlzLmdldERvY3VtZW50Um9vdCgpKTtcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBMb2NhdG9yRmFjdG9yeWAgaW50ZXJmYWNlLlxuICBsb2NhdG9yRm9yPFQgZXh0ZW5kcyAoSGFybmVzc1F1ZXJ5PGFueT4gfCBzdHJpbmcpW10+KFxuICAgIC4uLnF1ZXJpZXM6IFRcbiAgKTogQXN5bmNGYWN0b3J5Rm48TG9jYXRvckZuUmVzdWx0PFQ+PiB7XG4gICAgcmV0dXJuICgpID0+XG4gICAgICBfYXNzZXJ0UmVzdWx0Rm91bmQoXG4gICAgICAgIHRoaXMuX2dldEFsbEhhcm5lc3Nlc0FuZFRlc3RFbGVtZW50cyhxdWVyaWVzKSxcbiAgICAgICAgX2dldERlc2NyaXB0aW9uRm9yTG9jYXRvckZvclF1ZXJpZXMocXVlcmllcyksXG4gICAgICApO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIGxvY2F0b3JGb3JPcHRpb25hbDxUIGV4dGVuZHMgKEhhcm5lc3NRdWVyeTxhbnk+IHwgc3RyaW5nKVtdPihcbiAgICAuLi5xdWVyaWVzOiBUXG4gICk6IEFzeW5jRmFjdG9yeUZuPExvY2F0b3JGblJlc3VsdDxUPiB8IG51bGw+IHtcbiAgICByZXR1cm4gYXN5bmMgKCkgPT4gKGF3YWl0IHRoaXMuX2dldEFsbEhhcm5lc3Nlc0FuZFRlc3RFbGVtZW50cyhxdWVyaWVzKSlbMF0gfHwgbnVsbDtcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBMb2NhdG9yRmFjdG9yeWAgaW50ZXJmYWNlLlxuICBsb2NhdG9yRm9yQWxsPFQgZXh0ZW5kcyAoSGFybmVzc1F1ZXJ5PGFueT4gfCBzdHJpbmcpW10+KFxuICAgIC4uLnF1ZXJpZXM6IFRcbiAgKTogQXN5bmNGYWN0b3J5Rm48TG9jYXRvckZuUmVzdWx0PFQ+W10+IHtcbiAgICByZXR1cm4gKCkgPT4gdGhpcy5fZ2V0QWxsSGFybmVzc2VzQW5kVGVzdEVsZW1lbnRzKHF1ZXJpZXMpO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIGFzeW5jIHJvb3RIYXJuZXNzTG9hZGVyKCk6IFByb21pc2U8SGFybmVzc0xvYWRlcj4ge1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIGFzeW5jIGhhcm5lc3NMb2FkZXJGb3Ioc2VsZWN0b3I6IHN0cmluZyk6IFByb21pc2U8SGFybmVzc0xvYWRlcj4ge1xuICAgIHJldHVybiB0aGlzLmNyZWF0ZUVudmlyb25tZW50KFxuICAgICAgYXdhaXQgX2Fzc2VydFJlc3VsdEZvdW5kKHRoaXMuZ2V0QWxsUmF3RWxlbWVudHMoc2VsZWN0b3IpLCBbXG4gICAgICAgIF9nZXREZXNjcmlwdGlvbkZvckhhcm5lc3NMb2FkZXJRdWVyeShzZWxlY3RvciksXG4gICAgICBdKSxcbiAgICApO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIGFzeW5jIGhhcm5lc3NMb2FkZXJGb3JPcHRpb25hbChzZWxlY3Rvcjogc3RyaW5nKTogUHJvbWlzZTxIYXJuZXNzTG9hZGVyIHwgbnVsbD4ge1xuICAgIGNvbnN0IGVsZW1lbnRzID0gYXdhaXQgdGhpcy5nZXRBbGxSYXdFbGVtZW50cyhzZWxlY3Rvcik7XG4gICAgcmV0dXJuIGVsZW1lbnRzWzBdID8gdGhpcy5jcmVhdGVFbnZpcm9ubWVudChlbGVtZW50c1swXSkgOiBudWxsO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIGFzeW5jIGhhcm5lc3NMb2FkZXJGb3JBbGwoc2VsZWN0b3I6IHN0cmluZyk6IFByb21pc2U8SGFybmVzc0xvYWRlcltdPiB7XG4gICAgY29uc3QgZWxlbWVudHMgPSBhd2FpdCB0aGlzLmdldEFsbFJhd0VsZW1lbnRzKHNlbGVjdG9yKTtcbiAgICByZXR1cm4gZWxlbWVudHMubWFwKGVsZW1lbnQgPT4gdGhpcy5jcmVhdGVFbnZpcm9ubWVudChlbGVtZW50KSk7XG4gIH1cblxuICAvLyBJbXBsZW1lbnRlZCBhcyBwYXJ0IG9mIHRoZSBgSGFybmVzc0xvYWRlcmAgaW50ZXJmYWNlLlxuICBnZXRIYXJuZXNzPFQgZXh0ZW5kcyBDb21wb25lbnRIYXJuZXNzPihxdWVyeTogSGFybmVzc1F1ZXJ5PFQ+KTogUHJvbWlzZTxUPiB7XG4gICAgcmV0dXJuIHRoaXMubG9jYXRvckZvcihxdWVyeSkoKTtcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBIYXJuZXNzTG9hZGVyYCBpbnRlcmZhY2UuXG4gIGdldEFsbEhhcm5lc3NlczxUIGV4dGVuZHMgQ29tcG9uZW50SGFybmVzcz4ocXVlcnk6IEhhcm5lc3NRdWVyeTxUPik6IFByb21pc2U8VFtdPiB7XG4gICAgcmV0dXJuIHRoaXMubG9jYXRvckZvckFsbChxdWVyeSkoKTtcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBIYXJuZXNzTG9hZGVyYCBpbnRlcmZhY2UuXG4gIGFzeW5jIGdldENoaWxkTG9hZGVyKHNlbGVjdG9yOiBzdHJpbmcpOiBQcm9taXNlPEhhcm5lc3NMb2FkZXI+IHtcbiAgICByZXR1cm4gdGhpcy5jcmVhdGVFbnZpcm9ubWVudChcbiAgICAgIGF3YWl0IF9hc3NlcnRSZXN1bHRGb3VuZCh0aGlzLmdldEFsbFJhd0VsZW1lbnRzKHNlbGVjdG9yKSwgW1xuICAgICAgICBfZ2V0RGVzY3JpcHRpb25Gb3JIYXJuZXNzTG9hZGVyUXVlcnkoc2VsZWN0b3IpLFxuICAgICAgXSksXG4gICAgKTtcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBIYXJuZXNzTG9hZGVyYCBpbnRlcmZhY2UuXG4gIGFzeW5jIGdldEFsbENoaWxkTG9hZGVycyhzZWxlY3Rvcjogc3RyaW5nKTogUHJvbWlzZTxIYXJuZXNzTG9hZGVyW10+IHtcbiAgICByZXR1cm4gKGF3YWl0IHRoaXMuZ2V0QWxsUmF3RWxlbWVudHMoc2VsZWN0b3IpKS5tYXAoZSA9PiB0aGlzLmNyZWF0ZUVudmlyb25tZW50KGUpKTtcbiAgfVxuXG4gIC8qKiBDcmVhdGVzIGEgYENvbXBvbmVudEhhcm5lc3NgIGZvciB0aGUgZ2l2ZW4gaGFybmVzcyB0eXBlIHdpdGggdGhlIGdpdmVuIHJhdyBob3N0IGVsZW1lbnQuICovXG4gIHByb3RlY3RlZCBjcmVhdGVDb21wb25lbnRIYXJuZXNzPFQgZXh0ZW5kcyBDb21wb25lbnRIYXJuZXNzPihcbiAgICBoYXJuZXNzVHlwZTogQ29tcG9uZW50SGFybmVzc0NvbnN0cnVjdG9yPFQ+LFxuICAgIGVsZW1lbnQ6IEUsXG4gICk6IFQge1xuICAgIHJldHVybiBuZXcgaGFybmVzc1R5cGUodGhpcy5jcmVhdGVFbnZpcm9ubWVudChlbGVtZW50KSk7XG4gIH1cblxuICAvLyBQYXJ0IG9mIExvY2F0b3JGYWN0b3J5IGludGVyZmFjZSwgc3ViY2xhc3NlcyB3aWxsIGltcGxlbWVudC5cbiAgYWJzdHJhY3QgZm9yY2VTdGFiaWxpemUoKTogUHJvbWlzZTx2b2lkPjtcblxuICAvLyBQYXJ0IG9mIExvY2F0b3JGYWN0b3J5IGludGVyZmFjZSwgc3ViY2xhc3NlcyB3aWxsIGltcGxlbWVudC5cbiAgYWJzdHJhY3Qgd2FpdEZvclRhc2tzT3V0c2lkZUFuZ3VsYXIoKTogUHJvbWlzZTx2b2lkPjtcblxuICAvKiogR2V0cyB0aGUgcm9vdCBlbGVtZW50IGZvciB0aGUgZG9jdW1lbnQuICovXG4gIHByb3RlY3RlZCBhYnN0cmFjdCBnZXREb2N1bWVudFJvb3QoKTogRTtcblxuICAvKiogQ3JlYXRlcyBhIGBUZXN0RWxlbWVudGAgZnJvbSBhIHJhdyBlbGVtZW50LiAqL1xuICBwcm90ZWN0ZWQgYWJzdHJhY3QgY3JlYXRlVGVzdEVsZW1lbnQoZWxlbWVudDogRSk6IFRlc3RFbGVtZW50O1xuXG4gIC8qKiBDcmVhdGVzIGEgYEhhcm5lc3NMb2FkZXJgIHJvb3RlZCBhdCB0aGUgZ2l2ZW4gcmF3IGVsZW1lbnQuICovXG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjcmVhdGVFbnZpcm9ubWVudChlbGVtZW50OiBFKTogSGFybmVzc0Vudmlyb25tZW50PEU+O1xuXG4gIC8qKlxuICAgKiBHZXRzIGEgbGlzdCBvZiBhbGwgZWxlbWVudHMgbWF0Y2hpbmcgdGhlIGdpdmVuIHNlbGVjdG9yIHVuZGVyIHRoaXMgZW52aXJvbm1lbnQncyByb290IGVsZW1lbnQuXG4gICAqL1xuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZ2V0QWxsUmF3RWxlbWVudHMoc2VsZWN0b3I6IHN0cmluZyk6IFByb21pc2U8RVtdPjtcblxuICAvKipcbiAgICogTWF0Y2hlcyB0aGUgZ2l2ZW4gcmF3IGVsZW1lbnRzIHdpdGggdGhlIGdpdmVuIGxpc3Qgb2YgZWxlbWVudCBhbmQgaGFybmVzcyBxdWVyaWVzIHRvIHByb2R1Y2UgYVxuICAgKiBsaXN0IG9mIG1hdGNoZWQgaGFybmVzc2VzIGFuZCB0ZXN0IGVsZW1lbnRzLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBfZ2V0QWxsSGFybmVzc2VzQW5kVGVzdEVsZW1lbnRzPFQgZXh0ZW5kcyAoSGFybmVzc1F1ZXJ5PGFueT4gfCBzdHJpbmcpW10+KFxuICAgIHF1ZXJpZXM6IFQsXG4gICk6IFByb21pc2U8TG9jYXRvckZuUmVzdWx0PFQ+W10+IHtcbiAgICBjb25zdCB7YWxsUXVlcmllcywgaGFybmVzc1F1ZXJpZXMsIGVsZW1lbnRRdWVyaWVzLCBoYXJuZXNzVHlwZXN9ID0gX3BhcnNlUXVlcmllcyhxdWVyaWVzKTtcblxuICAgIC8vIENvbWJpbmUgYWxsIG9mIHRoZSBxdWVyaWVzIGludG8gb25lIGxhcmdlIGNvbW1hLWRlbGltaXRlZCBzZWxlY3RvciBhbmQgdXNlIGl0IHRvIGdldCBhbGwgcmF3XG4gICAgLy8gZWxlbWVudHMgbWF0Y2hpbmcgYW55IG9mIHRoZSBpbmRpdmlkdWFsIHF1ZXJpZXMuXG4gICAgY29uc3QgcmF3RWxlbWVudHMgPSBhd2FpdCB0aGlzLmdldEFsbFJhd0VsZW1lbnRzKFxuICAgICAgWy4uLmVsZW1lbnRRdWVyaWVzLCAuLi5oYXJuZXNzUXVlcmllcy5tYXAocHJlZGljYXRlID0+IHByZWRpY2F0ZS5nZXRTZWxlY3RvcigpKV0uam9pbignLCcpLFxuICAgICk7XG5cbiAgICAvLyBJZiBldmVyeSBxdWVyeSBpcyBzZWFyY2hpbmcgZm9yIHRoZSBzYW1lIGhhcm5lc3Mgc3ViY2xhc3MsIHdlIGtub3cgZXZlcnkgcmVzdWx0IGNvcnJlc3BvbmRzXG4gICAgLy8gdG8gYW4gaW5zdGFuY2Ugb2YgdGhhdCBzdWJjbGFzcy4gTGlrZXdpc2UsIGlmIGV2ZXJ5IHF1ZXJ5IGlzIGZvciBhIGBUZXN0RWxlbWVudGAsIHdlIGtub3dcbiAgICAvLyBldmVyeSByZXN1bHQgY29ycmVzcG9uZHMgdG8gYSBgVGVzdEVsZW1lbnRgLiBPdGhlcndpc2Ugd2UgbmVlZCB0byB2ZXJpZnkgd2hpY2ggcmVzdWx0IHdhc1xuICAgIC8vIGZvdW5kIGJ5IHdoaWNoIHNlbGVjdG9yIHNvIGl0IGNhbiBiZSBtYXRjaGVkIHRvIHRoZSBhcHByb3ByaWF0ZSBpbnN0YW5jZS5cbiAgICBjb25zdCBza2lwU2VsZWN0b3JDaGVjayA9XG4gICAgICAoZWxlbWVudFF1ZXJpZXMubGVuZ3RoID09PSAwICYmIGhhcm5lc3NUeXBlcy5zaXplID09PSAxKSB8fCBoYXJuZXNzUXVlcmllcy5sZW5ndGggPT09IDA7XG5cbiAgICBjb25zdCBwZXJFbGVtZW50TWF0Y2hlcyA9IGF3YWl0IHBhcmFsbGVsKCgpID0+XG4gICAgICByYXdFbGVtZW50cy5tYXAoYXN5bmMgcmF3RWxlbWVudCA9PiB7XG4gICAgICAgIGNvbnN0IHRlc3RFbGVtZW50ID0gdGhpcy5jcmVhdGVUZXN0RWxlbWVudChyYXdFbGVtZW50KTtcbiAgICAgICAgY29uc3QgYWxsUmVzdWx0c0ZvckVsZW1lbnQgPSBhd2FpdCBwYXJhbGxlbChcbiAgICAgICAgICAvLyBGb3IgZWFjaCBxdWVyeSwgZ2V0IGBudWxsYCBpZiBpdCBkb2Vzbid0IG1hdGNoLCBvciBhIGBUZXN0RWxlbWVudGAgb3JcbiAgICAgICAgICAvLyBgQ29tcG9uZW50SGFybmVzc2AgYXMgYXBwcm9wcmlhdGUgaWYgaXQgZG9lcyBtYXRjaC4gVGhpcyBnaXZlcyB1cyBldmVyeXRoaW5nIHRoYXRcbiAgICAgICAgICAvLyBtYXRjaGVzIHRoZSBjdXJyZW50IHJhdyBlbGVtZW50LCBidXQgaXQgbWF5IGNvbnRhaW4gZHVwbGljYXRlIGVudHJpZXMgKGUuZy5cbiAgICAgICAgICAvLyBtdWx0aXBsZSBgVGVzdEVsZW1lbnRgIG9yIG11bHRpcGxlIGBDb21wb25lbnRIYXJuZXNzYCBvZiB0aGUgc2FtZSB0eXBlKS5cbiAgICAgICAgICAoKSA9PlxuICAgICAgICAgICAgYWxsUXVlcmllcy5tYXAocXVlcnkgPT5cbiAgICAgICAgICAgICAgdGhpcy5fZ2V0UXVlcnlSZXN1bHRGb3JFbGVtZW50KHF1ZXJ5LCByYXdFbGVtZW50LCB0ZXN0RWxlbWVudCwgc2tpcFNlbGVjdG9yQ2hlY2spLFxuICAgICAgICAgICAgKSxcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIF9yZW1vdmVEdXBsaWNhdGVRdWVyeVJlc3VsdHMoYWxsUmVzdWx0c0ZvckVsZW1lbnQpO1xuICAgICAgfSksXG4gICAgKTtcbiAgICByZXR1cm4gKFtdIGFzIGFueSkuY29uY2F0KC4uLnBlckVsZW1lbnRNYXRjaGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayB3aGV0aGVyIHRoZSBnaXZlbiBxdWVyeSBtYXRjaGVzIHRoZSBnaXZlbiBlbGVtZW50LCBpZiBpdCBkb2VzIHJldHVybiB0aGUgbWF0Y2hlZFxuICAgKiBgVGVzdEVsZW1lbnRgIG9yIGBDb21wb25lbnRIYXJuZXNzYCwgaWYgaXQgZG9lcyBub3QsIHJldHVybiBudWxsLiBJbiBjYXNlcyB3aGVyZSB0aGUgY2FsbGVyXG4gICAqIGtub3dzIGZvciBzdXJlIHRoYXQgdGhlIHF1ZXJ5IG1hdGNoZXMgdGhlIGVsZW1lbnQncyBzZWxlY3RvciwgYHNraXBTZWxlY3RvckNoZWNrYCBjYW4gYmUgdXNlZFxuICAgKiB0byBza2lwIHZlcmlmaWNhdGlvbiBhbmQgb3B0aW1pemUgcGVyZm9ybWFuY2UuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIF9nZXRRdWVyeVJlc3VsdEZvckVsZW1lbnQ8VCBleHRlbmRzIENvbXBvbmVudEhhcm5lc3M+KFxuICAgIHF1ZXJ5OiBzdHJpbmcgfCBIYXJuZXNzUHJlZGljYXRlPFQ+LFxuICAgIHJhd0VsZW1lbnQ6IEUsXG4gICAgdGVzdEVsZW1lbnQ6IFRlc3RFbGVtZW50LFxuICAgIHNraXBTZWxlY3RvckNoZWNrOiBib29sZWFuID0gZmFsc2UsXG4gICk6IFByb21pc2U8VCB8IFRlc3RFbGVtZW50IHwgbnVsbD4ge1xuICAgIGlmICh0eXBlb2YgcXVlcnkgPT09ICdzdHJpbmcnKSB7XG4gICAgICByZXR1cm4gc2tpcFNlbGVjdG9yQ2hlY2sgfHwgKGF3YWl0IHRlc3RFbGVtZW50Lm1hdGNoZXNTZWxlY3RvcihxdWVyeSkpID8gdGVzdEVsZW1lbnQgOiBudWxsO1xuICAgIH1cbiAgICBpZiAoc2tpcFNlbGVjdG9yQ2hlY2sgfHwgKGF3YWl0IHRlc3RFbGVtZW50Lm1hdGNoZXNTZWxlY3RvcihxdWVyeS5nZXRTZWxlY3RvcigpKSkpIHtcbiAgICAgIGNvbnN0IGhhcm5lc3MgPSB0aGlzLmNyZWF0ZUNvbXBvbmVudEhhcm5lc3MocXVlcnkuaGFybmVzc1R5cGUsIHJhd0VsZW1lbnQpO1xuICAgICAgcmV0dXJuIChhd2FpdCBxdWVyeS5ldmFsdWF0ZShoYXJuZXNzKSkgPyBoYXJuZXNzIDogbnVsbDtcbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn1cblxuLyoqXG4gKiBQYXJzZXMgYSBsaXN0IG9mIHF1ZXJpZXMgaW4gdGhlIGZvcm1hdCBhY2NlcHRlZCBieSB0aGUgYGxvY2F0b3JGb3IqYCBtZXRob2RzIGludG8gYW4gZWFzaWVyIHRvXG4gKiB3b3JrIHdpdGggZm9ybWF0LlxuICovXG5mdW5jdGlvbiBfcGFyc2VRdWVyaWVzPFQgZXh0ZW5kcyAoSGFybmVzc1F1ZXJ5PGFueT4gfCBzdHJpbmcpW10+KFxuICBxdWVyaWVzOiBULFxuKTogUGFyc2VkUXVlcmllczxMb2NhdG9yRm5SZXN1bHQ8VD4gJiBDb21wb25lbnRIYXJuZXNzPiB7XG4gIGNvbnN0IGFsbFF1ZXJpZXMgPSBbXTtcbiAgY29uc3QgaGFybmVzc1F1ZXJpZXMgPSBbXTtcbiAgY29uc3QgZWxlbWVudFF1ZXJpZXMgPSBbXTtcbiAgY29uc3QgaGFybmVzc1R5cGVzID0gbmV3IFNldDxcbiAgICBDb21wb25lbnRIYXJuZXNzQ29uc3RydWN0b3I8TG9jYXRvckZuUmVzdWx0PFQ+ICYgQ29tcG9uZW50SGFybmVzcz5cbiAgPigpO1xuXG4gIGZvciAoY29uc3QgcXVlcnkgb2YgcXVlcmllcykge1xuICAgIGlmICh0eXBlb2YgcXVlcnkgPT09ICdzdHJpbmcnKSB7XG4gICAgICBhbGxRdWVyaWVzLnB1c2gocXVlcnkpO1xuICAgICAgZWxlbWVudFF1ZXJpZXMucHVzaChxdWVyeSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHByZWRpY2F0ZSA9IHF1ZXJ5IGluc3RhbmNlb2YgSGFybmVzc1ByZWRpY2F0ZSA/IHF1ZXJ5IDogbmV3IEhhcm5lc3NQcmVkaWNhdGUocXVlcnksIHt9KTtcbiAgICAgIGFsbFF1ZXJpZXMucHVzaChwcmVkaWNhdGUpO1xuICAgICAgaGFybmVzc1F1ZXJpZXMucHVzaChwcmVkaWNhdGUpO1xuICAgICAgaGFybmVzc1R5cGVzLmFkZChwcmVkaWNhdGUuaGFybmVzc1R5cGUpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7YWxsUXVlcmllcywgaGFybmVzc1F1ZXJpZXMsIGVsZW1lbnRRdWVyaWVzLCBoYXJuZXNzVHlwZXN9O1xufVxuXG4vKipcbiAqIFJlbW92ZXMgZHVwbGljYXRlIHF1ZXJ5IHJlc3VsdHMgZm9yIGEgcGFydGljdWxhciBlbGVtZW50LiAoZS5nLiBtdWx0aXBsZSBgVGVzdEVsZW1lbnRgXG4gKiBpbnN0YW5jZXMgb3IgbXVsdGlwbGUgaW5zdGFuY2VzIG9mIHRoZSBzYW1lIGBDb21wb25lbnRIYXJuZXNzYCBjbGFzcy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gX3JlbW92ZUR1cGxpY2F0ZVF1ZXJ5UmVzdWx0czxUIGV4dGVuZHMgKENvbXBvbmVudEhhcm5lc3MgfCBUZXN0RWxlbWVudCB8IG51bGwpW10+KFxuICByZXN1bHRzOiBULFxuKTogUHJvbWlzZTxUPiB7XG4gIGxldCB0ZXN0RWxlbWVudE1hdGNoZWQgPSBmYWxzZTtcbiAgbGV0IG1hdGNoZWRIYXJuZXNzVHlwZXMgPSBuZXcgU2V0KCk7XG4gIGNvbnN0IGRlZHVwZWRNYXRjaGVzID0gW107XG4gIGZvciAoY29uc3QgcmVzdWx0IG9mIHJlc3VsdHMpIHtcbiAgICBpZiAoIXJlc3VsdCkge1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBDb21wb25lbnRIYXJuZXNzKSB7XG4gICAgICBpZiAoIW1hdGNoZWRIYXJuZXNzVHlwZXMuaGFzKHJlc3VsdC5jb25zdHJ1Y3RvcikpIHtcbiAgICAgICAgbWF0Y2hlZEhhcm5lc3NUeXBlcy5hZGQocmVzdWx0LmNvbnN0cnVjdG9yKTtcbiAgICAgICAgZGVkdXBlZE1hdGNoZXMucHVzaChyZXN1bHQpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoIXRlc3RFbGVtZW50TWF0Y2hlZCkge1xuICAgICAgdGVzdEVsZW1lbnRNYXRjaGVkID0gdHJ1ZTtcbiAgICAgIGRlZHVwZWRNYXRjaGVzLnB1c2gocmVzdWx0KTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGRlZHVwZWRNYXRjaGVzIGFzIFQ7XG59XG5cbi8qKiBWZXJpZmllcyB0aGF0IHRoZXJlIGlzIGF0IGxlYXN0IG9uZSByZXN1bHQgaW4gYW4gYXJyYXkuICovXG5hc3luYyBmdW5jdGlvbiBfYXNzZXJ0UmVzdWx0Rm91bmQ8VD4oXG4gIHJlc3VsdHM6IFByb21pc2U8VFtdPixcbiAgcXVlcnlEZXNjcmlwdGlvbnM6IHN0cmluZ1tdLFxuKTogUHJvbWlzZTxUPiB7XG4gIGNvbnN0IHJlc3VsdCA9IChhd2FpdCByZXN1bHRzKVswXTtcbiAgaWYgKHJlc3VsdCA9PSB1bmRlZmluZWQpIHtcbiAgICB0aHJvdyBFcnJvcihcbiAgICAgIGBGYWlsZWQgdG8gZmluZCBlbGVtZW50IG1hdGNoaW5nIG9uZSBvZiB0aGUgZm9sbG93aW5nIHF1ZXJpZXM6XFxuYCArXG4gICAgICAgIHF1ZXJ5RGVzY3JpcHRpb25zLm1hcChkZXNjID0+IGAoJHtkZXNjfSlgKS5qb2luKCcsXFxuJyksXG4gICAgKTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG4vKiogR2V0cyBhIGxpc3Qgb2YgZGVzY3JpcHRpb24gc3RyaW5ncyBmcm9tIGEgbGlzdCBvZiBxdWVyaWVzLiAqL1xuZnVuY3Rpb24gX2dldERlc2NyaXB0aW9uRm9yTG9jYXRvckZvclF1ZXJpZXMocXVlcmllczogKHN0cmluZyB8IEhhcm5lc3NRdWVyeTxhbnk+KVtdKSB7XG4gIHJldHVybiBxdWVyaWVzLm1hcChxdWVyeSA9PlxuICAgIHR5cGVvZiBxdWVyeSA9PT0gJ3N0cmluZydcbiAgICAgID8gX2dldERlc2NyaXB0aW9uRm9yVGVzdEVsZW1lbnRRdWVyeShxdWVyeSlcbiAgICAgIDogX2dldERlc2NyaXB0aW9uRm9yQ29tcG9uZW50SGFybmVzc1F1ZXJ5KHF1ZXJ5KSxcbiAgKTtcbn1cblxuLyoqIEdldHMgYSBkZXNjcmlwdGlvbiBzdHJpbmcgZm9yIGEgYENvbXBvbmVudEhhcm5lc3NgIHF1ZXJ5LiAqL1xuZnVuY3Rpb24gX2dldERlc2NyaXB0aW9uRm9yQ29tcG9uZW50SGFybmVzc1F1ZXJ5KHF1ZXJ5OiBIYXJuZXNzUXVlcnk8YW55Pikge1xuICBjb25zdCBoYXJuZXNzUHJlZGljYXRlID1cbiAgICBxdWVyeSBpbnN0YW5jZW9mIEhhcm5lc3NQcmVkaWNhdGUgPyBxdWVyeSA6IG5ldyBIYXJuZXNzUHJlZGljYXRlKHF1ZXJ5LCB7fSk7XG4gIGNvbnN0IHtuYW1lLCBob3N0U2VsZWN0b3J9ID0gaGFybmVzc1ByZWRpY2F0ZS5oYXJuZXNzVHlwZTtcbiAgY29uc3QgZGVzY3JpcHRpb24gPSBgJHtuYW1lfSB3aXRoIGhvc3QgZWxlbWVudCBtYXRjaGluZyBzZWxlY3RvcjogXCIke2hvc3RTZWxlY3Rvcn1cImA7XG4gIGNvbnN0IGNvbnN0cmFpbnRzID0gaGFybmVzc1ByZWRpY2F0ZS5nZXREZXNjcmlwdGlvbigpO1xuICByZXR1cm4gKFxuICAgIGRlc2NyaXB0aW9uICtcbiAgICAoY29uc3RyYWludHMgPyBgIHNhdGlzZnlpbmcgdGhlIGNvbnN0cmFpbnRzOiAke2hhcm5lc3NQcmVkaWNhdGUuZ2V0RGVzY3JpcHRpb24oKX1gIDogJycpXG4gICk7XG59XG5cbi8qKiBHZXRzIGEgZGVzY3JpcHRpb24gc3RyaW5nIGZvciBhIGBUZXN0RWxlbWVudGAgcXVlcnkuICovXG5mdW5jdGlvbiBfZ2V0RGVzY3JpcHRpb25Gb3JUZXN0RWxlbWVudFF1ZXJ5KHNlbGVjdG9yOiBzdHJpbmcpIHtcbiAgcmV0dXJuIGBUZXN0RWxlbWVudCBmb3IgZWxlbWVudCBtYXRjaGluZyBzZWxlY3RvcjogXCIke3NlbGVjdG9yfVwiYDtcbn1cblxuLyoqIEdldHMgYSBkZXNjcmlwdGlvbiBzdHJpbmcgZm9yIGEgYEhhcm5lc3NMb2FkZXJgIHF1ZXJ5LiAqL1xuZnVuY3Rpb24gX2dldERlc2NyaXB0aW9uRm9ySGFybmVzc0xvYWRlclF1ZXJ5KHNlbGVjdG9yOiBzdHJpbmcpIHtcbiAgcmV0dXJuIGBIYXJuZXNzTG9hZGVyIGZvciBlbGVtZW50IG1hdGNoaW5nIHNlbGVjdG9yOiBcIiR7c2VsZWN0b3J9XCJgO1xufVxuIl19