testcafe
Version:
Automated browser testing for the modern web development stack.
191 lines • 29.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const pinkie_1 = __importDefault(require("pinkie"));
const compiler_1 = __importDefault(require("../compiler"));
const connection_1 = __importDefault(require("../browser/connection"));
const runtime_1 = require("../errors/runtime");
const pool_1 = __importDefault(require("../browser/provider/pool"));
const types_1 = require("../errors/types");
const browser_set_1 = __importDefault(require("./browser-set"));
const tested_app_1 = __importDefault(require("./tested-app"));
const parse_file_list_1 = __importDefault(require("../utils/parse-file-list"));
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const make_dir_1 = __importDefault(require("make-dir"));
const resolve_path_relatively_cwd_1 = __importDefault(require("../utils/resolve-path-relatively-cwd"));
class Bootstrapper {
constructor(browserConnectionGateway) {
this.browserConnectionGateway = browserConnectionGateway;
this.concurrency = null;
this.sources = [];
this.browsers = [];
this.reporters = [];
this.filter = null;
this.appCommand = null;
this.appInitDelay = null;
this.tsConfigPath = null;
}
static _splitBrowserInfo(browserInfo) {
const remotes = [];
const automated = [];
browserInfo.forEach(browser => {
if (browser instanceof connection_1.default)
remotes.push(browser);
else
automated.push(browser);
});
return { remotes, automated };
}
async _getBrowserInfo() {
if (!this.browsers.length)
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.browserNotSet);
const browserInfo = await pinkie_1.default.all(this.browsers.map(browser => pool_1.default.getBrowserInfo(browser)));
return lodash_1.flatten(browserInfo);
}
_createAutomatedConnections(browserInfo) {
if (!browserInfo)
return [];
return browserInfo
.map(browser => lodash_1.times(this.concurrency, () => new connection_1.default(this.browserConnectionGateway, browser)));
}
async _getBrowserConnections(browserInfo) {
const { automated, remotes } = Bootstrapper._splitBrowserInfo(browserInfo);
if (remotes && remotes.length % this.concurrency)
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotDivideRemotesCountByConcurrency);
let browserConnections = this._createAutomatedConnections(automated);
browserConnections = browserConnections.concat(lodash_1.chunk(remotes, this.concurrency));
return await browser_set_1.default.from(browserConnections);
}
async _getTests() {
if (!this.sources.length)
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.testSourcesNotSet);
const { parsedFileList, compilerOptions } = await this._getCompilerArguments();
const compiler = new compiler_1.default(parsedFileList, compilerOptions);
let tests = await compiler.getTests();
const testsWithOnlyFlag = tests.filter(test => test.only);
if (testsWithOnlyFlag.length)
tests = testsWithOnlyFlag;
if (this.filter)
tests = tests.filter(test => this.filter(test.name, test.fixture.name, test.fixture.path, test.meta, test.fixture.meta));
if (!tests.length)
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.noTestsToRun);
return tests;
}
async _getCompilerArguments() {
const parsedFileList = await parse_file_list_1.default(this.sources, process.cwd());
const compilerOptions = {
typeScriptOptions: {
tsConfigPath: this.tsConfigPath
}
};
return { parsedFileList, compilerOptions };
}
async _ensureOutStream(outStream) {
if (typeof outStream !== 'string')
return outStream;
const fullReporterOutputPath = resolve_path_relatively_cwd_1.default(outStream);
await make_dir_1.default(path_1.default.dirname(fullReporterOutputPath));
return fs_1.default.createWriteStream(fullReporterOutputPath);
}
static _addDefaultReporter(reporters) {
reporters.push({
name: 'spec',
file: process.stdout
});
}
async _getReporterPlugins() {
const stdoutReporters = lodash_1.filter(this.reporters, r => lodash_1.isUndefined(r.output) || r.output === process.stdout);
if (stdoutReporters.length > 1)
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.multipleStdoutReporters, stdoutReporters.map(r => r.name).join(', '));
if (!this.reporters.length)
Bootstrapper._addDefaultReporter(this.reporters);
return pinkie_1.default.all(this.reporters.map(async ({ name, output }) => {
let pluginFactory = name;
let pluginName = null;
const outStream = await this._ensureOutStream(output);
if (typeof pluginFactory !== 'function') {
try {
pluginFactory = require('testcafe-reporter-' + name);
pluginName = name;
}
catch (err) {
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotFindReporterForAlias, name);
}
}
const plugin = pluginFactory();
plugin.name = pluginName;
return {
plugin,
outStream
};
}));
}
async _startTestedApp() {
if (this.appCommand) {
const testedApp = new tested_app_1.default();
await testedApp.start(this.appCommand, this.appInitDelay);
return testedApp;
}
return null;
}
async _canUseParallelBootstrapping(browserInfo) {
const isLocalPromises = browserInfo.map(browser => browser.provider.isLocalBrowser(null, browserInfo.browserName));
const isLocalBrowsers = await pinkie_1.default.all(isLocalPromises);
return isLocalBrowsers.every(result => result);
}
async _bootstrapSequence(browserInfo) {
const tests = await this._getTests();
const testedApp = await this._startTestedApp();
const browserSet = await this._getBrowserConnections(browserInfo);
return { tests, testedApp, browserSet };
}
_wrapBootstrappingPromise(promise) {
return promise
.then(result => ({ error: null, result }))
.catch(error => ({ result: null, error }));
}
async _handleBootstrappingError([browserSetStatus, testsStatus, testedAppStatus]) {
if (!browserSetStatus.error)
await browserSetStatus.result.dispose();
if (!testedAppStatus.error && testedAppStatus.result)
await testedAppStatus.result.kill();
if (testsStatus.error)
throw testsStatus.error;
else if (testedAppStatus.error)
throw testedAppStatus.error;
else
throw browserSetStatus.error;
}
async _bootstrapParallel(browserInfo) {
let bootstrappingPromises = [
this._getBrowserConnections(browserInfo),
this._getTests(),
this._startTestedApp()
];
bootstrappingPromises = bootstrappingPromises.map(promise => this._wrapBootstrappingPromise(promise));
const bootstrappingStatuses = await pinkie_1.default.all(bootstrappingPromises);
if (bootstrappingStatuses.some(status => status.error))
await this._handleBootstrappingError(bootstrappingStatuses);
const [browserSet, tests, testedApp] = bootstrappingStatuses.map(status => status.result);
return { browserSet, tests, testedApp };
}
// API
async createRunnableConfiguration() {
const reporterPlugins = await this._getReporterPlugins();
// NOTE: If a user forgot to specify a browser, but has specified a path to tests, the specified path will be
// considered as the browser argument, and the tests path argument will have the predefined default value.
// It's very ambiguous for the user, who might be confused by compilation errors from an unexpected test.
// So, we need to retrieve the browser aliases and paths before tests compilation.
const browserInfo = await this._getBrowserInfo();
if (await this._canUseParallelBootstrapping(browserInfo))
return Object.assign({ reporterPlugins }, await this._bootstrapParallel(browserInfo));
return Object.assign({ reporterPlugins }, await this._bootstrapSequence(browserInfo));
}
}
exports.default = Bootstrapper;
module.exports = exports.default;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYm9vdHN0cmFwcGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3J1bm5lci9ib290c3RyYXBwZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxtQ0FBb0U7QUFDcEUsb0RBQTZCO0FBQzdCLDJEQUFtQztBQUNuQyx1RUFBc0Q7QUFDdEQsK0NBQWlEO0FBQ2pELG9FQUEyRDtBQUMzRCwyQ0FBaUQ7QUFDakQsZ0VBQXVDO0FBQ3ZDLDhEQUFxQztBQUNyQywrRUFBcUQ7QUFDckQsZ0RBQXdCO0FBQ3hCLDRDQUFvQjtBQUNwQix3REFBK0I7QUFDL0IsdUdBQTRFO0FBRTVFLE1BQXFCLFlBQVk7SUFDN0IsWUFBYSx3QkFBd0I7UUFDakMsSUFBSSxDQUFDLHdCQUF3QixHQUFHLHdCQUF3QixDQUFDO1FBRXpELElBQUksQ0FBQyxXQUFXLEdBQUksSUFBSSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxPQUFPLEdBQVEsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxRQUFRLEdBQU8sRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxTQUFTLEdBQU0sRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLEdBQVMsSUFBSSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxVQUFVLEdBQUssSUFBSSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDO0lBQzdCLENBQUM7SUFFRCxNQUFNLENBQUMsaUJBQWlCLENBQUUsV0FBVztRQUNqQyxNQUFNLE9BQU8sR0FBSyxFQUFFLENBQUM7UUFDckIsTUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFDO1FBRXJCLFdBQVcsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUU7WUFDMUIsSUFBSSxPQUFPLFlBQVksb0JBQWlCO2dCQUNwQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDOztnQkFFdEIsU0FB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TSxJQUFJLENBQUMseUJBQXlCLENBQUMscUJBQXFCLENBQUMsQ0FBQztRQUVoRSxNQUFNLENBQUMsVUFBVSxFQUFFLEtBQUssRUFBRSxTQUFTLENBQUMsR0FBRyxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFMUYsT0FBTyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFDNUMsQ0FBQztJQUVELE1BQU07SUFDTixLQUFLLENBQUMsMkJBQTJCO1FBQzdCLE1BQU0sZUFBZSxHQUFHLE1BQU0sSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUM7UUFFekQsNkdBQTZHO1FBQzdHLDBHQUEwRztRQUMxRyx5R0FBeUc7UUFDekcsa0ZBQWtGO1FBQ2xGLE1BQU0sV0FBVyxHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBRWpELElBQUksTUFBTSxJQUFJLENBQUMsNEJBQTRCLENBQUMsV0FBVyxDQUFDO1lBQ3BELHVCQUFTLGVBQWUsSUFBSyxNQUFNLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxXQUFXLENBQUMsRUFBRztRQUU5RSx1QkFBUyxlQUFlLElBQUssTUFBTSxJQUFJLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDLEVBQUc7SUFDOUUsQ0FBQztDQUNKO0FBck9ELCtCQXFPQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGlzVW5kZWZpbmVkLCBmaWx0ZXIsIGZsYXR0ZW4sIGNodW5rLCB0aW1lcyB9IGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgUHJvbWlzZSBmcm9tICdwaW5raWUnO1xuaW1wb3J0IENvbXBpbGVyIGZyb20gJy4uL2NvbXBpbGVyJztcbmltcG9ydCBCcm93c2VyQ29ubmVjdGlvbiBmcm9tICcuLi9icm93c2VyL2Nvbm5lY3Rpb24nO1xuaW1wb3J0IHsgR2VuZXJhbEVycm9yIH0gZnJvbSAnLi4vZXJyb3JzL3J1bnRpbWUnO1xuaW1wb3J0IGJyb3dzZXJQcm92aWRlclBvb2wgZnJvbSAnLi4vYnJvd3Nlci9wcm92aWRlci9wb29sJztcbmltcG9ydCB7IFJVTlRJTUVfRVJST1JTIH0gZnJvbSAnLi4vZXJyb3JzL3R5cGVzJztcbmltcG9ydCBCcm93c2VyU2V0IGZyb20gJy4vYnJvd3Nlci1zZXQnO1xuaW1wb3J0IFRlc3RlZEFwcCBmcm9tICcuL3Rlc3RlZC1hcHAnO1xuaW1wb3J0IHBhcnNlRmlsZUxpc3QgZnJvbSAnLi4vdXRpbHMvcGFyc2UtZmlsZS1saXN0JztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IGZzIGZyb20gJ2ZzJztcbmltcG9ydCBtYWtlRGlyIGZyb20gJ21ha2UtZGlyJztcbmltcG9ydCByZXNvbHZlUGF0aFJlbGF0aXZlbHlDd2QgZnJvbSAnLi4vdXRpbHMvcmVzb2x2ZS1wYXRoLXJlbGF0aXZlbHktY3dkJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQm9vdHN0cmFwcGVyIHtcbiAgICBjb25zdHJ1Y3RvciAoYnJvd3NlckNvbm5lY3Rpb25HYXRld2F5KSB7XG4gICAgICAgIHRoaXMuYnJvd3NlckNvbm5lY3Rpb25HYXRld2F5ID0gYnJvd3NlckNvbm5lY3Rpb25HYXRld2F5O1xuXG4gICAgICAgIHRoaXMuY29uY3VycmVuY3kgID0gbnVsbDtcbiAgICAgICAgdGhpcy5zb3VyY2VzICAgICAgPSBbXTtcbiAgICAgICAgdGhpcy5icm93c2VycyAgICAgPSBbXTtcbiAgICAgICAgdGhpcy5yZXBvcnRlcnMgICAgPSBbXTtcbiAgICAgICAgdGhpcy5maWx0ZXIgICAgICAgPSBudWxsO1xuICAgICAgICB0aGlzLmFwcENvbW1hbmQgICA9IG51bGw7XG4gICAgICAgIHRoaXMuYXBwSW5pdERlbGF5ID0gbnVsbDtcbiAgICAgICAgdGhpcy50c0NvbmZpZ1BhdGggPSBudWxsO1xuICAgIH1cblxuICAgIHN0YXRpYyBfc3BsaXRCcm93c2VySW5mbyAoYnJvd3NlckluZm8pIHtcbiAgICAgICAgY29uc3QgcmVtb3RlcyAgID0gW107XG4gICAgICAgIGNvbnN0IGF1dG9tYXRlZCA9IFtdO1xuXG4gICAgICAgIGJyb3dzZXJJbmZvLmZvckVhY2goYnJvd3NlciA9PiB7XG4gICAgICAgICAgICBpZiAoYnJvd3NlciBpbnN0YW5jZW9mIEJyb3dzZXJDb25uZWN0aW9uKVxuICAgICAgICAgICAgICAgIHJlbW90ZXMucHVzaChicm93c2VyKTtcbiAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICBhdXRvbWF0ZWQucHVzaChicm93c2VyKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIHsgcmVtb3RlcywgYXV0b21hdGVkIH07XG4gICAgfVxuXG4gICAgYXN5bmMgX2dldEJyb3dzZXJJbmZvICgpIHtcbiAgICAgICAgaWYgKCF0aGlzLmJyb3dzZXJzLmxlbmd0aClcbiAgICAgICAgICAgIHRocm93IG5ldyBHZW5lcmFsRXJyb3IoUlVOVElNRV9FUlJPUlMuYnJvd3Nlck5vdFNldCk7XG5cbiAgICAgICAgY29uc3QgYnJvd3NlckluZm8gPSBhd2FpdCBQcm9taXNlLmFsbCh0aGlzLmJyb3dzZXJzLm1hcChicm93c2VyID0+IGJyb3dzZXJQcm92aWRlclBvb2wuZ2V0QnJvd3NlckluZm8oYnJvd3NlcikpKTtcblxuICAgICAgICByZXR1cm4gZmxhdHRlbihicm93c2VySW5mbyk7XG4gICAgfVxuXG4gICAgX2NyZWF0ZUF1dG9tYXRlZENvbm5lY3Rpb25zIChicm93c2VySW5mbykge1xuICAgICAgICBpZiAoIWJyb3dzZXJJbmZvKVxuICAgICAgICAgICAgcmV0dXJuIFtdO1xuXG4gICAgICAgIHJldHVybiBicm93c2VySW5mb1xuICAgICAgICAgICAgLm1hcChicm93c2VyID0+IHRpbWVzKHRoaXMuY29uY3VycmVuY3ksICgpID0+IG5ldyBCcm93c2VyQ29ubmVjdGlvbih0aGlzLmJyb3dzZXJDb25uZWN0aW9uR2F0ZXdheSwgYnJvd3NlcikpKTtcbiAgICB9XG5cbiAgICBhc3luYyBfZ2V0QnJvd3NlckNvbm5lY3Rpb25zIChicm93c2VySW5mbykge1xuICAgICAgICBjb25zdCB7IGF1dG9tYXRlZCwgcmVtb3RlcyB9ID0gQm9vdHN0cmFwcGVyLl9zcGxpdEJyb3dzZXJJbmZvKGJyb3dzZXJJbmZvKTtcblxuICAgICAgICBpZiAocmVtb3RlcyAmJiByZW1vdGVzLmxlbmd0aCAlIHRoaXMuY29uY3VycmVuY3kpXG4gICAgICAgICAgICB0aHJvdyBuZXcgR2VuZXJhbEVycm9yKFJVTlRJTUVfRVJST1JTLmNhbm5vdERpdmlkZVJlbW90ZXNDb3VudEJ5Q29uY3VycmVuY3kpO1xuXG4gICAgICAgIGxldCBicm93c2VyQ29ubmVjdGlvbnMgPSB0aGlzLl9jcmVhdGVBdXRvbWF0ZWRDb25uZWN0aW9ucyhhdXRvbWF0ZWQpO1xuXG4gICAgICAgIGJyb3dzZXJDb25uZWN0aW9ucyA9IGJyb3dzZXJDb25uZWN0aW9ucy5jb25jYXQoY2h1bmsocmVtb3RlcywgdGhpcy5jb25jdXJyZW5jeSkpO1xuXG4gICAgICAgIHJldHVybiBhd2FpdCBCcm93c2VyU2V0LmZyb20oYnJvd3NlckNvbm5lY3Rpb25zKTtcbiAgICB9XG5cbiAgICBhc3luYyBfZ2V0VGVzdHMgKCkge1xuICAgICAgICBpZiAoIXRoaXMuc291cmNlcy5sZW5ndGgpXG4gICAgICAgICAgICB0aHJvdyBuZXcgR2VuZXJhbEVycm9yKFJVTlRJTUVfRVJST1JTLnRlc3RTb3VyY2VzTm90U2V0KTtcblxuICAgICAgICBjb25zdCB7IHBhcnNlZEZpbGVMaXN0LCBjb21waWxlck9wdGlvbnMgfSA9IGF3YWl0IHRoaXMuX2dldENvbXBpbGVyQXJndW1lbnRzKCk7XG5cbiAgICAgICAgY29uc3QgY29tcGlsZXIgPSBuZXcgQ29tcGlsZXIocGFyc2VkRmlsZUxpc3QsIGNvbXBpbGVyT3B0aW9ucyk7XG4gICAgICAgIGxldCB0ZXN0cyAgICAgID0gYXdhaXQgY29tcGlsZXIuZ2V0VGVzdHMoKTtcblxuICAgICAgICBjb25zdCB0ZXN0c1dpdGhPbmx5RmxhZyA9IHRlc3RzLmZpbHRlcih0ZXN0ID0+IHRlc3Qub25seSk7XG5cbiAgICAgICAgaWYgKHRlc3RzV2l0aE9ubHlGbGFnLmxlbmd0aClcbiAgICAgICAgICAgIHRlc3RzID0gdGVzdHNXaXRoT25seUZsYWc7XG5cbiAgICAgICAgaWYgKHRoaXMuZmlsdGVyKVxuICAgICAgICAgICAgdGVzdHMgPSB0ZXN0cy5maWx0ZXIodGVzdCA9PiB0aGlzLmZpbHRlcih0ZXN0Lm5hbWUsIHRlc3QuZml4dHVyZS5uYW1lLCB0ZXN0LmZpeHR1cmUucGF0aCwgdGVzdC5tZXRhLCB0ZXN0LmZpeHR1cmUubWV0YSkpO1xuXG4gICAgICAgIGlmICghdGVzdHMubGVuZ3RoKVxuICAgICAgICAgICAgdGhyb3cgbmV3IEdlbmVyYWxFcnJvcihSVU5USU1FX0VSUk9SUy5ub1Rlc3RzVG9SdW4pO1xuXG4gICAgICAgIHJldHVybiB0ZXN0cztcbiAgICB9XG5cbiAgICBhc3luYyBfZ2V0Q29tcGlsZXJBcmd1bWVudHMgKCkge1xuICAgICAgICBjb25zdCBwYXJzZWRGaWxlTGlzdCA9IGF3YWl0IHBhcnNlRmlsZUxpc3QodGhpcy5zb3VyY2VzLCBwcm9jZXNzLmN3ZCgpKTtcblxuICAgICAgICBjb25zdCBjb21waWxlck9wdGlvbnMgPSB7XG4gICAgICAgICAgICB0eXBlU2NyaXB0T3B0aW9uczoge1xuICAgICAgICAgICAgICAgIHRzQ29uZmlnUGF0aDogdGhpcy50c0NvbmZpZ1BhdGhcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcblxuICAgICAgICByZXR1cm4geyBwYXJzZWRGaWxlTGlzdCwgY29tcGlsZXJPcHRpb25zIH07XG4gICAgfVxuXG4gICAgYXN5bmMgX2Vuc3VyZU91dFN0cmVhbSAob3V0U3RyZWFtKSB7XG4gICAgICAgIGlmICh0eXBlb2Ygb3V0U3RyZWFtICE9PSAnc3RyaW5nJylcbiAgICAgICAgICAgIHJldHVybiBvdXRTdHJlYW07XG5cbiAgICAgICAgY29uc3QgZnVsbFJlcG9ydGVyT3V0cHV0UGF0aCA9IHJlc29sdmVQYXRoUmVsYXRpdmVseUN3ZChvdXRTdHJlYW0pO1xuXG4gICAgICAgIGF3YWl0IG1ha2VEaXIocGF0aC5kaXJuYW1lKGZ1bGxSZXBvcnRlck91dHB1dFBhdGgpKTtcblxuICAgICAgICByZXR1cm4gZnMuY3JlYXRlV3JpdGVTdHJlYW0oZnVsbFJlcG9ydGVyT3V0cHV0UGF0aCk7XG4gICAgfVxuXG4gICAgc3RhdGljIF9hZGREZWZhdWx0UmVwb3J0ZXIgKHJlcG9ydGVycykge1xuICAgICAgICByZXBvcnRlcnMucHVzaCh7XG4gICAgICAgICAgICBuYW1lOiAnc3BlYycsXG4gICAgICAgICAgICBmaWxlOiBwcm9jZXNzLnN0ZG91dFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBhc3luYyBfZ2V0UmVwb3J0ZXJQbHVnaW5zICgpIHtcbiAgICAgICAgY29uc3Qgc3Rkb3V0UmVwb3J0ZXJzID0gZmlsdGVyKHRoaXMucmVwb3J0ZXJzLCByID0+IGlzVW5kZWZpbmVkKHIub3V0cHV0KSB8fCByLm91dHB1dCA9PT0gcHJvY2Vzcy5zdGRvdXQpO1xuXG4gICAgICAgIGlmIChzdGRvdXRSZXBvcnRlcnMubGVuZ3RoID4gMSlcbiAgICAgICAgICAgIHRocm93IG5ldyBHZW5lcmFsRXJyb3IoUlVOVElNRV9FUlJPUlMubXVsdGlwbGVTdGRvdXRSZXBvcnRlcnMsIHN0ZG91dFJlcG9ydGVycy5tYXAociA9PiByLm5hbWUpLmpvaW4oJywgJykpO1xuXG4gICAgICAgIGlmICghdGhpcy5yZXBvcnRlcnMubGVuZ3RoKVxuICAgICAgICAgICAgQm9vdHN0cmFwcGVyLl9hZGREZWZhdWx0UmVwb3J0ZXIodGhpcy5yZXBvcnRlcnMpO1xuXG4gICAgICAgIHJldHVybiBQcm9taXNlLmFsbCh0aGlzLnJlcG9ydGVycy5tYXAoYXN5bmMgKHsgbmFtZSwgb3V0cHV0IH0pID0+IHtcbiAgICAgICAgICAgIGxldCBwbHVnaW5GYWN0b3J5ID0gbmFtZTtcbiAgICAgICAgICAgIGxldCBwbHVnaW5OYW1lICAgID0gbnVsbDtcblxuICAgICAgICAgICAgY29uc3Qgb3V0U3RyZWFtID0gYXdhaXQgdGhpcy5fZW5zdXJlT3V0U3RyZWFtKG91dHB1dCk7XG5cbiAgICAgICAgICAgIGlmICh0eXBlb2YgcGx1Z2luRmFjdG9yeSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIHBsdWdpbkZhY3RvcnkgPSByZXF1aXJlKCd0ZXN0Y2FmZS1yZXBvcnRlci0nICsgbmFtZSk7XG4gICAgICAgICAgICAgICAgICAgIHBsdWdpbk5hbWUgICAgPSBuYW1lO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBHZW5lcmFsRXJyb3IoUlVOVElNRV9FUlJPUlMuY2Fubm90RmluZFJlcG9ydGVyRm9yQWxpYXMsIG5hbWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgcGx1Z2luID0gcGx1Z2luRmFjdG9yeSgpO1xuXG4gICAgICAgICAgICBwbHVnaW4ubmFtZSA9IHBsdWdpbk5hbWU7XG5cbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgcGx1Z2luLFxuICAgICAgICAgICAgICAgIG91dFN0cmVhbVxuICAgICAgICAgICAgfTtcbiAgICAgICAgfSkpO1xuICAgIH1cblxuICAgIGFzeW5jIF9zdGFydFRlc3RlZEFwcCAoKSB7XG4gICAgICAgIGlmICh0aGlzLmFwcENvbW1hbmQpIHtcbiAgICAgICAgICAgIGNvbnN0IHRlc3RlZEFwcCA9IG5ldyBUZXN0ZWRBcHAoKTtcblxuICAgICAgICAgICAgYXdhaXQgdGVzdGVkQXBwLnN0YXJ0KHRoaXMuYXBwQ29tbWFuZCwgdGhpcy5hcHBJbml0RGVsYXkpO1xuXG4gICAgICAgICAgICByZXR1cm4gdGVzdGVkQXBwO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgYXN5bmMgX2NhblVzZVBhcmFsbGVsQm9vdHN0cmFwcGluZyAoYnJvd3NlckluZm8pIHtcbiAgICAgICAgY29uc3QgaXNMb2NhbFByb21pc2VzID0gYnJvd3NlckluZm8ubWFwKGJyb3dzZXIgPT4gYnJvd3Nlci5wcm92aWRlci5pc0xvY2FsQnJvd3NlcihudWxsLCBicm93c2VySW5mby5icm93c2VyTmFtZSkpO1xuICAgICAgICBjb25zdCBpc0xvY2FsQnJvd3NlcnMgPSBhd2FpdCBQcm9taXNlLmFsbChpc0xvY2FsUHJvbWlzZXMpO1xuXG4gICAgICAgIHJldHVybiBpc0xvY2FsQnJvd3NlcnMuZXZlcnkocmVzdWx0ID0+IHJlc3VsdCk7XG4gICAgfVxuXG4gICAgYXN5bmMgX2Jvb3RzdHJhcFNlcXVlbmNlIChicm93c2VySW5mbykge1xuICAgICAgICBjb25zdCB0ZXN0cyAgICAgICA9IGF3YWl0IHRoaXMuX2dldFRlc3RzKCk7XG4gICAgICAgIGNvbnN0IHRlc3RlZEFwcCAgID0gYXdhaXQgdGhpcy5fc3RhcnRUZXN0ZWRBcHAoKTtcbiAgICAgICAgY29uc3QgYnJvd3NlclNldCAgPSBhd2FpdCB0aGlzLl9nZXRCcm93c2VyQ29ubmVjdGlvbnMoYnJvd3NlckluZm8pO1xuXG4gICAgICAgIHJldHVybiB7IHRlc3RzLCB0ZXN0ZWRBcHAsIGJyb3dzZXJTZXQgfTtcbiAgICB9XG5cbiAgICBfd3JhcEJvb3RzdHJhcHBpbmdQcm9taXNlIChwcm9taXNlKSB7XG4gICAgICAgIHJldHVybiBwcm9taXNlXG4gICAgICAgICAgICAudGhlbihyZXN1bHQgPT4gKHsgZXJyb3I6IG51bGwsIHJlc3VsdCB9KSlcbiAgICAgICAgICAgIC5jYXRjaChlcnJvciA9PiAoeyByZXN1bHQ6IG51bGwsIGVycm9yIH0pKTtcbiAgICB9XG5cbiAgICBhc3luYyBfaGFuZGxlQm9vdHN0cmFwcGluZ0Vycm9yIChbYnJvd3NlclNldFN0YXR1cywgdGVzdHNTdGF0dXMsIHRlc3RlZEFwcFN0YXR1c10pIHtcbiAgICAgICAgaWYgKCFicm93c2VyU2V0U3RhdHVzLmVycm9yKVxuICAgICAgICAgICAgYXdhaXQgYnJvd3NlclNldFN0YXR1cy5yZXN1bHQuZGlzcG9zZSgpO1xuXG4gICAgICAgIGlmICghdGVzdGVkQXBwU3RhdHVzLmVycm9yICYmIHRlc3RlZEFwcFN0YXR1cy5yZXN1bHQpXG4gICAgICAgICAgICBhd2FpdCB0ZXN0ZWRBcHBTdGF0dXMucmVzdWx0LmtpbGwoKTtcblxuICAgICAgICBpZiAodGVzdHNTdGF0dXMuZXJyb3IpXG4gICAgICAgICAgICB0aHJvdyB0ZXN0c1N0YXR1cy5lcnJvcjtcbiAgICAgICAgZWxzZSBpZiAodGVzdGVkQXBwU3RhdHVzLmVycm9yKVxuICAgICAgICAgICAgdGhyb3cgdGVzdGVkQXBwU3RhdHVzLmVycm9yO1xuICAgICAgICBlbHNlXG4gICAgICAgICAgICB0aHJvdyBicm93c2VyU2V0U3RhdHVzLmVycm9yO1xuICAgIH1cblxuICAgIGFzeW5jIF9ib290c3RyYXBQYXJhbGxlbCAoYnJvd3NlckluZm8pIHtcbiAgICAgICAgbGV0IGJvb3RzdHJhcHBpbmdQcm9taXNlcyA9IFtcbiAgICAgICAgICAgIHRoaXMuX2dldEJyb3dzZXJDb25uZWN0aW9ucyhicm93c2VySW5mbyksXG4gICAgICAgICAgICB0aGlzLl9nZXRUZXN0cygpLFxuICAgICAgICAgICAgdGhpcy5fc3RhcnRUZXN0ZWRBcHAoKVxuICAgICAgICBdO1xuXG4gICAgICAgIGJvb3RzdHJhcHBpbmdQcm9taXNlcyA9IGJvb3RzdHJhcHBpbmdQcm9taXNlcy5tYXAocHJvbWlzZSA9PiB0aGlzLl93cmFwQm9vdHN0cmFwcGluZ1Byb21pc2UocHJvbWlzZSkpO1xuXG4gICAgICAgIGNvbnN0IGJvb3RzdHJhcHBpbmdTdGF0dXNlcyA9IGF3YWl0IFByb21pc2UuYWxsKGJvb3RzdHJhcHBpbmdQcm9taXNlcyk7XG5cbiAgICAgICAgaWYgKGJvb3RzdHJhcHBpbmdTdGF0dXNlcy5zb21lKHN0YXR1cyA9PiBzdGF0dXMuZXJyb3IpKVxuICAgICAgICAgICAgYXdhaXQgdGhpcy5faGFuZGxlQm9vdHN0cmFwcGluZ0Vycm9yKGJvb3RzdHJhcHBpbmdTdGF0dXNlcyk7XG5cbiAgICAgICAgY29uc3QgW2Jyb3dzZXJTZXQsIHRlc3RzLCB0ZXN0ZWRBcHBdID0gYm9vdHN0cmFwcGluZ1N0YXR1c2VzLm1hcChzdGF0dXMgPT4gc3RhdHVzLnJlc3VsdCk7XG5cbiAgICAgICAgcmV0dXJuIHsgYnJvd3NlclNldCwgdGVzdHMsIHRlc3RlZEFwcCB9O1xuICAgIH1cblxuICAgIC8vIEFQSVxuICAgIGFzeW5jIGNyZWF0ZVJ1bm5hYmxlQ29uZmlndXJhdGlvbiAoKSB7XG4gICAgICAgIGNvbnN0IHJlcG9ydGVyUGx1Z2lucyA9IGF3YWl0IHRoaXMuX2dldFJlcG9ydGVyUGx1Z2lucygpO1xuXG4gICAgICAgIC8vIE5PVEU6IElmIGEgdXNlciBmb3Jnb3QgdG8gc3BlY2lmeSBhIGJyb3dzZXIsIGJ1dCBoYXMgc3BlY2lmaWVkIGEgcGF0aCB0byB0ZXN0cywgdGhlIHNwZWNpZmllZCBwYXRoIHdpbGwgYmVcbiAgICAgICAgLy8gY29uc2lkZXJlZCBhcyB0aGUgYnJvd3NlciBhcmd1bWVudCwgYW5kIHRoZSB0ZXN0cyBwYXRoIGFyZ3VtZW50IHdpbGwgaGF2ZSB0aGUgcHJlZGVmaW5lZCBkZWZhdWx0IHZhbHVlLlxuICAgICAgICAvLyBJdCdzIHZlcnkgYW1iaWd1b3VzIGZvciB0aGUgdXNlciwgd2hvIG1pZ2h0IGJlIGNvbmZ1c2VkIGJ5IGNvbXBpbGF0aW9uIGVycm9ycyBmcm9tIGFuIHVuZXhwZWN0ZWQgdGVzdC5cbiAgICAgICAgLy8gU28sIHdlIG5lZWQgdG8gcmV0cmlldmUgdGhlIGJyb3dzZXIgYWxpYXNlcyBhbmQgcGF0aHMgYmVmb3JlIHRlc3RzIGNvbXBpbGF0aW9uLlxuICAgICAgICBjb25zdCBicm93c2VySW5mbyA9IGF3YWl0IHRoaXMuX2dldEJyb3dzZXJJbmZvKCk7XG5cbiAgICAgICAgaWYgKGF3YWl0IHRoaXMuX2NhblVzZVBhcmFsbGVsQm9vdHN0cmFwcGluZyhicm93c2VySW5mbykpXG4gICAgICAgICAgICByZXR1cm4geyByZXBvcnRlclBsdWdpbnMsIC4uLmF3YWl0IHRoaXMuX2Jvb3RzdHJhcFBhcmFsbGVsKGJyb3dzZXJJbmZvKSB9O1xuXG4gICAgICAgIHJldHVybiB7IHJlcG9ydGVyUGx1Z2lucywgLi4uYXdhaXQgdGhpcy5fYm9vdHN0cmFwU2VxdWVuY2UoYnJvd3NlckluZm8pIH07XG4gICAgfVxufVxuIl19