appium-webdriveragent-driver
Version:
Appium driver for WebDriverAgent
678 lines (538 loc) • 41 kB
JavaScript
;
var _get = require('babel-runtime/helpers/get')['default'];
var _inherits = require('babel-runtime/helpers/inherits')['default'];
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var _slicedToArray = require('babel-runtime/helpers/sliced-to-array')['default'];
var _regeneratorRuntime = require('babel-runtime/regenerator')['default'];
var _getIterator = require('babel-runtime/core-js/get-iterator')['default'];
var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default'];
Object.defineProperty(exports, '__esModule', {
value: true
});
var _appiumBaseDriver = require('appium-base-driver');
var _appiumSupport = require('appium-support');
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _nodeSimctl = require('node-simctl');
var _iosAppUtils = require('ios-app-utils');
var _webdriveragent = require('./webdriveragent');
var _webdriveragent2 = _interopRequireDefault(_webdriveragent);
var _logger = require('./logger');
var _logger2 = _interopRequireDefault(_logger);
var _simulatorManagementJs = require('./simulatorManagement.js');
var _appiumIosSimulator = require('appium-ios-simulator');
var _asyncbox = require('asyncbox');
var _mobileJsonWireProtocol = require('mobile-json-wire-protocol');
var _appiumSafariDriver = require('appium-safari-driver');
var _commandsIndexJs = require('./commands/index.js');
var _commandsIndexJs2 = _interopRequireDefault(_commandsIndexJs);
var SAFARI_BUNDLE_ID = 'com.apple.mobilesafari';
var WebDriverAgentDriver = (function (_BaseDriver) {
_inherits(WebDriverAgentDriver, _BaseDriver);
function WebDriverAgentDriver() {
var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var shouldValidateCaps = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
_classCallCheck(this, WebDriverAgentDriver);
_get(Object.getPrototypeOf(WebDriverAgentDriver.prototype), 'constructor', this).call(this, opts, shouldValidateCaps);
// TODO set up desired cap constraints
//this.desiredCapConstraints = desiredCapConstraints;
this.wda = null;
this.sim = null;
this.jwpProxyActive = false;
this.proxyReqRes = null;
this.jwpProxyAvoid = [];
this._safariDriver = null; // lazy-loaded in getter
}
_createClass(WebDriverAgentDriver, [{
key: 'getSafariDriver',
value: function getSafariDriver() {
var safariDriver;
return _regeneratorRuntime.async(function getSafariDriver$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
if (!this._safariDriver) {
context$2$0.next = 2;
break;
}
return context$2$0.abrupt('return', this._safariDriver);
case 2:
_logger2['default'].info('Creating Safari Driver');
safariDriver = new _appiumSafariDriver.SafariDriver();
context$2$0.prev = 4;
context$2$0.next = 7;
return _regeneratorRuntime.awrap(safariDriver.createSession(this.opts));
case 7:
context$2$0.next = 14;
break;
case 9:
context$2$0.prev = 9;
context$2$0.t0 = context$2$0['catch'](4);
_logger2['default'].warn('Safari Driver could not start a session, no webviews found');
_logger2['default'].debug(context$2$0.t0, context$2$0.t0.message);
return context$2$0.abrupt('return', null);
case 14:
_logger2['default'].info('Safari Driver connected');
this._safariDriver = safariDriver;
return context$2$0.abrupt('return', safariDriver);
case 17:
case 'end':
return context$2$0.stop();
}
}, null, this, [[4, 9]]);
}
}, {
key: 'createSession',
value: function createSession(caps) {
var sessionId, _ref, _ref2, _ref3, udid, realDevice;
return _regeneratorRuntime.async(function createSession$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
this.lifecycleData = {}; // this is used for keeping track of the state we start so when we delete the session we can put things back
context$2$0.prev = 1;
sessionId = undefined;
context$2$0.next = 5;
return _regeneratorRuntime.awrap(_get(Object.getPrototypeOf(WebDriverAgentDriver.prototype), 'createSession', this).call(this, caps));
case 5:
_ref = context$2$0.sent;
_ref2 = _slicedToArray(_ref, 1);
sessionId = _ref2[0];
_logger2['default'].info('Determining device to run tests on');
context$2$0.next = 11;
return _regeneratorRuntime.awrap(this.determineDevice());
case 11:
_ref3 = context$2$0.sent;
udid = _ref3.udid;
realDevice = _ref3.realDevice;
this.opts.udid = udid;
this.opts.isRealDevice = realDevice;
if (!this.opts.browserName) {
context$2$0.next = 22;
break;
}
_logger2['default'].info('safari test requested');
this.safari = true;
this.opts.bundleId = SAFARI_BUNDLE_ID;
if (!this.opts.isRealDevice) {
context$2$0.next = 22;
break;
}
throw new Error('real device safari support not yet implemented. Contact an appium dev');
case 22:
if (!this.opts.app) {
context$2$0.next = 25;
break;
}
context$2$0.next = 25;
return _regeneratorRuntime.awrap(this.checkAppPresent());
case 25:
if (this.opts.bundleId) {
context$2$0.next = 29;
break;
}
context$2$0.next = 28;
return _regeneratorRuntime.awrap((0, _iosAppUtils.extractBundleId)(this.opts.app));
case 28:
this.opts.bundleId = context$2$0.sent;
case 29:
if (this.opts.isRealDevice) {
context$2$0.next = 34;
break;
}
_logger2['default'].info('setting up simulator');
context$2$0.next = 33;
return _regeneratorRuntime.awrap(this.startSim());
case 33:
this.sim = context$2$0.sent;
case 34:
if (!(this.isSafari() && this.isSimulator())) {
context$2$0.next = 38;
break;
}
context$2$0.next = 37;
return _regeneratorRuntime.awrap((0, _simulatorManagementJs.launchSafariOnSim)(this.opts.safariInitialUrl, this.sim));
case 37:
return context$2$0.abrupt('return', [sessionId, caps]);
case 38:
this.wda = new _webdriveragent2['default']({
sim: this.sim, //TODO can't be a sim if real device. find a different way to supply logs to webdriveragent
platformVersion: this.opts.platformVersion,
host: this.opts.host,
agentPath: this.opts.agentPath
});
context$2$0.next = 41;
return _regeneratorRuntime.awrap(this.wda.launch(sessionId));
case 41:
this.proxyReqRes = this.wda.proxyReqRes.bind(this.wda);
this.jwpProxyActive = true;
context$2$0.next = 45;
return _regeneratorRuntime.awrap(this.startWdaSession(this.opts.app, this.opts.bundleId));
case 45:
return context$2$0.abrupt('return', [sessionId, caps]);
case 48:
context$2$0.prev = 48;
context$2$0.t0 = context$2$0['catch'](1);
_logger2['default'].error(context$2$0.t0);
context$2$0.next = 53;
return _regeneratorRuntime.awrap(this.deleteSession());
case 53:
throw context$2$0.t0;
case 54:
case 'end':
return context$2$0.stop();
}
}, null, this, [[1, 48]]);
}
}, {
key: 'deleteSession',
value: function deleteSession() {
var noReset;
return _regeneratorRuntime.async(function deleteSession$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
this.jwpProxyActive = false;
this.proxyReqRes = null;
noReset = this.opts.noReset;
if (!this.wda) {
context$2$0.next = 6;
break;
}
context$2$0.next = 6;
return _regeneratorRuntime.awrap(this.wda.quit());
case 6:
if (!(!noReset && this.sim)) {
context$2$0.next = 13;
break;
}
if (!this.lifecycleData.bootSim) {
context$2$0.next = 10;
break;
}
context$2$0.next = 10;
return _regeneratorRuntime.awrap(this.sim.shutdown());
case 10:
if (!this.lifecycleData.createSim) {
context$2$0.next = 13;
break;
}
context$2$0.next = 13;
return _regeneratorRuntime.awrap(this.sim['delete']());
case 13:
context$2$0.next = 15;
return _regeneratorRuntime.awrap(_get(Object.getPrototypeOf(WebDriverAgentDriver.prototype), 'deleteSession', this).call(this));
case 15:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'checkAppPresent',
value: function checkAppPresent() {
return _regeneratorRuntime.async(function checkAppPresent$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
_logger2['default'].debug("Checking whether app is actually present");
context$2$0.next = 3;
return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(this.opts.app));
case 3:
if (context$2$0.sent) {
context$2$0.next = 5;
break;
}
_logger2['default'].errorAndThrow('Could not find app at ' + this.opts.app);
case 5:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'determineDevice',
value: function determineDevice() {
var realDevice, udid, sim;
return _regeneratorRuntime.async(function determineDevice$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
realDevice = undefined, udid = undefined;
if (!this.opts.udid) {
context$2$0.next = 13;
break;
}
context$2$0.next = 4;
return _regeneratorRuntime.awrap((0, _appiumIosSimulator.simExists)(this.opts.udid));
case 4:
if (!context$2$0.sent) {
context$2$0.next = 10;
break;
}
this.lifecycleData.createSim = false;
udid = this.opts.udid;
realDevice = false;
context$2$0.next = 11;
break;
case 10:
throw new _mobileJsonWireProtocol.errors.SessionNotCreatedError('no simulator found with udid ' + this.opts.udid);
case 11:
context$2$0.next = 19;
break;
case 13:
// no udid: create a new sim
_logger2['default'].info('udid not provided, using desired caps to create a new sim');
context$2$0.next = 16;
return _regeneratorRuntime.awrap(this.createSim());
case 16:
sim = context$2$0.sent;
udid = sim.udid;
realDevice = false;
case 19:
return context$2$0.abrupt('return', { udid: udid, realDevice: realDevice });
case 20:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'startSim',
value: function startSim() {
var sim;
return _regeneratorRuntime.async(function startSim$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
sim = undefined;
context$2$0.next = 3;
return _regeneratorRuntime.awrap((0, _appiumIosSimulator.getSimulator)(this.opts.udid));
case 3:
sim = context$2$0.sent;
context$2$0.next = 6;
return _regeneratorRuntime.awrap((0, _simulatorManagementJs.simBooted)(this.opts.udid));
case 6:
if (context$2$0.sent) {
context$2$0.next = 15;
break;
}
_logger2['default'].info('simulator with udid ' + this.opts.udid + ' not booted. Booting up now');
context$2$0.next = 10;
return _regeneratorRuntime.awrap((0, _appiumIosSimulator.killAllSimulators)());
case 10:
context$2$0.next = 12;
return _regeneratorRuntime.awrap(sim.run());
case 12:
this.lifecycleData.bootSim = true;
context$2$0.next = 17;
break;
case 15:
_logger2['default'].info('simulator ' + this.opts.udid + ' already booted');
this.lifecycleData.bootSim = false;
case 17:
return context$2$0.abrupt('return', sim);
case 18:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'createSim',
value: function createSim() {
var sim;
return _regeneratorRuntime.async(function createSim$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
this.lifecycleData.createSim = true;
// create sim for caps
context$2$0.next = 3;
return _regeneratorRuntime.awrap((0, _simulatorManagementJs.createSim)(this.caps, this.sessionId));
case 3:
sim = context$2$0.sent;
_logger2['default'].info('created simulator ' + sim.udid + '. Booting it up');
return context$2$0.abrupt('return', sim);
case 6:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'launchApp',
value: function launchApp() {
var APP_LAUNCH_TIMEOUT, checkStatus;
return _regeneratorRuntime.async(function launchApp$(context$2$0) {
var _this = this;
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
APP_LAUNCH_TIMEOUT = 20 * 1000;
context$2$0.next = 3;
return _regeneratorRuntime.awrap((0, _nodeSimctl.launch)(this.sim.udid, this.opts.bundleId));
case 3:
checkStatus = function checkStatus() {
var response, currentApp;
return _regeneratorRuntime.async(function checkStatus$(context$3$0) {
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
context$3$0.next = 2;
return _regeneratorRuntime.awrap(this.wda.jwproxy.command('/status', 'GET'));
case 2:
response = context$3$0.sent;
currentApp = response.currentApp.bundleID;
if (!(currentApp !== this.opts.bundleId)) {
context$3$0.next = 6;
break;
}
throw new Error(this.opts.bundleId + ' not in foreground. ' + currentApp + ' is in foreground');
case 6:
case 'end':
return context$3$0.stop();
}
}, null, _this);
};
_logger2['default'].info('waiting for ' + this.opts.bundleId + ' to be in foreground');
context$2$0.next = 7;
return _regeneratorRuntime.awrap((0, _asyncbox.retryInterval)(APP_LAUNCH_TIMEOUT / 200, 200, checkStatus));
case 7:
_logger2['default'].info(this.opts.bundleId + ' is in foreground');
case 8:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'startWdaSession',
value: function startWdaSession(appPath, bundleId) {
var desired;
return _regeneratorRuntime.async(function startWdaSession$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
desired = {
desiredCapabilities: {
app: appPath,
bundleId: bundleId
}
};
context$2$0.next = 3;
return _regeneratorRuntime.awrap(this.wda.jwproxy.command('/session', 'POST', desired));
case 3:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'shouldProxyToSafari',
value: function shouldProxyToSafari(command) {
var commandsToNotProxyToSafari = ['getContexts', 'setContext', 'getCurrentContext', 'deleteSession'];
return this.inWebview() && (0, _mobileJsonWireProtocol.isSessionCommand)(command) && !_lodash2['default'].contains(commandsToNotProxyToSafari, command);
}
}, {
key: 'executeCommand',
value: function executeCommand(cmd) {
var safariDriver,
args$2$0 = arguments;
return _regeneratorRuntime.async(function executeCommand$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
if (!this.shouldProxyToSafari(cmd)) {
context$2$0.next = 7;
break;
}
context$2$0.next = 3;
return _regeneratorRuntime.awrap(this.getSafariDriver());
case 3:
safariDriver = context$2$0.sent;
context$2$0.next = 6;
return _regeneratorRuntime.awrap(safariDriver.executeCommand.apply(safariDriver, args$2$0));
case 6:
return context$2$0.abrupt('return', context$2$0.sent);
case 7:
context$2$0.next = 9;
return _regeneratorRuntime.awrap(_get(Object.getPrototypeOf(WebDriverAgentDriver.prototype), 'executeCommand', this).apply(this, args$2$0));
case 9:
return context$2$0.abrupt('return', context$2$0.sent);
case 10:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
// Override Proxy methods from BaseDriver
}, {
key: 'proxyActive',
value: function proxyActive() {
return this.jwpProxyActive;
}
}, {
key: 'getProxyAvoidList',
value: function getProxyAvoidList() {
return [['GET', /context/], ['POST', /context/]];
}
}, {
key: 'canProxy',
value: function canProxy() {
return true;
}
}, {
key: 'isSafari',
value: function isSafari() {
return this.safari;
}
}, {
key: 'isRealDevice',
value: function isRealDevice() {
return this.opts.realDevice;
}
}, {
key: 'isSimulator',
value: function isSimulator() {
return !this.opts.realDevice;
}
}, {
key: 'inWebview',
value: function inWebview() {
return this.isSafari() || this.webview;
}
}, {
key: 'driverData',
get: function get() {
// TODO fill out resource info here
return {};
}
}]);
return WebDriverAgentDriver;
})(_appiumBaseDriver.BaseDriver);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = _getIterator(_lodash2['default'].pairs(_commandsIndexJs2['default'])), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = _slicedToArray(_step.value, 2);
var cmd = _step$value[0];
var fn = _step$value[1];
WebDriverAgentDriver.prototype[cmd] = fn;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator['return']) {
_iterator['return']();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
exports.WebDriverAgentDriver = WebDriverAgentDriver;
// TODO add validation on caps
// TODO handle otherSessionData for multiple sessions
// fail very early if the app doesn't actually exist
// TODO: this kills all simulators, so needs to be changed if running multiple sims
//TODO check for real device
// TODO for now just kill all sims unless specified udid is booted.
// if booted, use it. if not booted, start it up
// if no udid, well lets see if we can start one up based on desired caps
// if we support multiple sims we need to change this
//# sourceMappingURL=data:application/json;base64,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NlExQixvQ0FBc0Isb0JBQUUsS0FBSyw4QkFBVSw0R0FBRTs7O1FBQS9CLEdBQUc7UUFBRSxFQUFFOztBQUNmLHdCQUFvQixDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7R0FDMUM7Ozs7Ozs7Ozs7Ozs7Ozs7UUFFUSxvQkFBb0IsR0FBcEIsb0JBQW9CIiwiZmlsZSI6ImxpYi9kcml2ZXIuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBCYXNlRHJpdmVyIH0gZnJvbSAnYXBwaXVtLWJhc2UtZHJpdmVyJztcbmltcG9ydCB7IGZzIH0gZnJvbSAnYXBwaXVtLXN1cHBvcnQnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IGxhdW5jaCB9IGZyb20gJ25vZGUtc2ltY3RsJztcbmltcG9ydCB7IGV4dHJhY3RCdW5kbGVJZCB9IGZyb20gJ2lvcy1hcHAtdXRpbHMnO1xuaW1wb3J0IFdlYkRyaXZlckFnZW50IGZyb20gJy4vd2ViZHJpdmVyYWdlbnQnO1xuaW1wb3J0IGxvZyBmcm9tICcuL2xvZ2dlcic7XG5pbXBvcnQgeyBzaW1Cb290ZWQsIGNyZWF0ZVNpbSwgbGF1bmNoU2FmYXJpT25TaW0gfSBmcm9tICcuL3NpbXVsYXRvck1hbmFnZW1lbnQuanMnO1xuaW1wb3J0IHsga2lsbEFsbFNpbXVsYXRvcnMsIGdldFNpbXVsYXRvciwgc2ltRXhpc3RzIH0gZnJvbSAnYXBwaXVtLWlvcy1zaW11bGF0b3InO1xuaW1wb3J0IHsgcmV0cnlJbnRlcnZhbCB9IGZyb20gJ2FzeW5jYm94JztcbmltcG9ydCB7IGVycm9ycywgaXNTZXNzaW9uQ29tbWFuZCB9IGZyb20gJ21vYmlsZS1qc29uLXdpcmUtcHJvdG9jb2wnO1xuaW1wb3J0IHsgU2FmYXJpRHJpdmVyIH0gZnJvbSAnYXBwaXVtLXNhZmFyaS1kcml2ZXInO1xuaW1wb3J0IGNvbW1hbmRzIGZyb20gJy4vY29tbWFuZHMvaW5kZXguanMnO1xuXG5jb25zdCBTQUZBUklfQlVORExFX0lEID0gJ2NvbS5hcHBsZS5tb2JpbGVzYWZhcmknO1xuXG5jbGFzcyBXZWJEcml2ZXJBZ2VudERyaXZlciBleHRlbmRzIEJhc2VEcml2ZXIge1xuICBjb25zdHJ1Y3RvciAob3B0cyA9IHt9LCBzaG91bGRWYWxpZGF0ZUNhcHMgPSB0cnVlKSB7XG4gICAgc3VwZXIob3B0cywgc2hvdWxkVmFsaWRhdGVDYXBzKTtcblxuICAgIC8vIFRPRE8gc2V0IHVwIGRlc2lyZWQgY2FwIGNvbnN0cmFpbnRzXG4gICAgLy90aGlzLmRlc2lyZWRDYXBDb25zdHJhaW50cyA9IGRlc2lyZWRDYXBDb25zdHJhaW50cztcbiAgICB0aGlzLndkYSA9IG51bGw7XG4gICAgdGhpcy5zaW0gPSBudWxsO1xuICAgIHRoaXMuandwUHJveHlBY3RpdmUgPSBmYWxzZTtcbiAgICB0aGlzLnByb3h5UmVxUmVzID0gbnVsbDtcbiAgICB0aGlzLmp3cFByb3h5QXZvaWQgPSBbXTtcbiAgICB0aGlzLl9zYWZhcmlEcml2ZXIgPSBudWxsOyAvLyBsYXp5LWxvYWRlZCBpbiBnZXR0ZXJcbiAgfVxuXG4gIGdldCBkcml2ZXJEYXRhICgpIHtcbiAgICAvLyBUT0RPIGZpbGwgb3V0IHJlc291cmNlIGluZm8gaGVyZVxuICAgIHJldHVybiB7fTtcbiAgfVxuXG4gIGFzeW5jIGdldFNhZmFyaURyaXZlciAoKSB7XG4gICAgaWYgKHRoaXMuX3NhZmFyaURyaXZlcikge1xuICAgICAgcmV0dXJuIHRoaXMuX3NhZmFyaURyaXZlcjtcbiAgICB9XG5cbiAgICBsb2cuaW5mbygnQ3JlYXRpbmcgU2FmYXJpIERyaXZlcicpO1xuICAgIGxldCBzYWZhcmlEcml2ZXIgPSBuZXcgU2FmYXJpRHJpdmVyKCk7XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgc2FmYXJpRHJpdmVyLmNyZWF0ZVNlc3Npb24odGhpcy5vcHRzKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2cud2FybignU2FmYXJpIERyaXZlciBjb3VsZCBub3Qgc3RhcnQgYSBzZXNzaW9uLCBubyB3ZWJ2aWV3cyBmb3VuZCcpO1xuICAgICAgbG9nLmRlYnVnKGUsIGUubWVzc2FnZSk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgbG9nLmluZm8oJ1NhZmFyaSBEcml2ZXIgY29ubmVjdGVkJyk7XG4gICAgdGhpcy5fc2FmYXJpRHJpdmVyID0gc2FmYXJpRHJpdmVyO1xuICAgIHJldHVybiBzYWZhcmlEcml2ZXI7XG4gIH1cblxuICBhc3luYyBjcmVhdGVTZXNzaW9uIChjYXBzKSB7XG4gICAgdGhpcy5saWZlY3ljbGVEYXRhID0ge307IC8vIHRoaXMgaXMgdXNlZCBmb3Iga2VlcGluZyB0cmFjayBvZiB0aGUgc3RhdGUgd2Ugc3RhcnQgc28gd2hlbiB3ZSBkZWxldGUgdGhlIHNlc3Npb24gd2UgY2FuIHB1dCB0aGluZ3MgYmFja1xuICAgIHRyeSB7XG4gICAgICAvLyBUT0RPIGFkZCB2YWxpZGF0aW9uIG9uIGNhcHNcbiAgICAgIC8vIFRPRE8gaGFuZGxlIG90aGVyU2Vzc2lvbkRhdGEgZm9yIG11bHRpcGxlIHNlc3Npb25zXG4gICAgICBsZXQgc2Vzc2lvbklkO1xuICAgICAgW3Nlc3Npb25JZF0gPSBhd2FpdCBzdXBlci5jcmVhdGVTZXNzaW9uKGNhcHMpO1xuXG4gICAgICBsb2cuaW5mbygnRGV0ZXJtaW5pbmcgZGV2aWNlIHRvIHJ1biB0ZXN0cyBvbicpO1xuICAgICAgbGV0IHsgdWRpZCwgcmVhbERldmljZSB9ID0gYXdhaXQgdGhpcy5kZXRlcm1pbmVEZXZpY2UoKTtcbiAgICAgIHRoaXMub3B0cy51ZGlkID0gdWRpZDtcbiAgICAgIHRoaXMub3B0cy5pc1JlYWxEZXZpY2UgPSByZWFsRGV2aWNlO1xuXG4gICAgICBpZiAodGhpcy5vcHRzLmJyb3dzZXJOYW1lKSB7XG4gICAgICAgIGxvZy5pbmZvKCdzYWZhcmkgdGVzdCByZXF1ZXN0ZWQnKTtcbiAgICAgICAgdGhpcy5zYWZhcmkgPSB0cnVlO1xuICAgICAgICB0aGlzLm9wdHMuYnVuZGxlSWQgPSBTQUZBUklfQlVORExFX0lEO1xuICAgICAgICBpZiAodGhpcy5vcHRzLmlzUmVhbERldmljZSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcigncmVhbCBkZXZpY2Ugc2FmYXJpIHN1cHBvcnQgbm90IHlldCBpbXBsZW1lbnRlZC4gQ29udGFjdCBhbiBhcHBpdW0gZGV2Jyk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gZmFpbCB2ZXJ5IGVhcmx5IGlmIHRoZSBhcHAgZG9lc24ndCBhY3R1YWxseSBleGlzdFxuICAgICAgaWYgKHRoaXMub3B0cy5hcHApIHtcbiAgICAgICAgYXdhaXQgdGhpcy5jaGVja0FwcFByZXNlbnQoKTtcbiAgICAgIH1cblxuICAgICAgaWYgKCF0aGlzLm9wdHMuYnVuZGxlSWQpIHtcbiAgICAgICAgdGhpcy5vcHRzLmJ1bmRsZUlkID0gYXdhaXQgZXh0cmFjdEJ1bmRsZUlkKHRoaXMub3B0cy5hcHApO1xuICAgICAgfVxuXG4gICAgICBpZiAoIXRoaXMub3B0cy5pc1JlYWxEZXZpY2UpIHtcbiAgICAgICAgbG9nLmluZm8oJ3NldHRpbmcgdXAgc2ltdWxhdG9yJyk7XG4gICAgICAgIHRoaXMuc2ltID0gYXdhaXQgdGhpcy5zdGFydFNpbSgpO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5pc1NhZmFyaSgpICYmIHRoaXMuaXNTaW11bGF0b3IoKSkge1xuICAgICAgICBhd2FpdCBsYXVuY2hTYWZhcmlPblNpbSh0aGlzLm9wdHMuc2FmYXJpSW5pdGlhbFVybCwgdGhpcy5zaW0pO1xuICAgICAgICByZXR1cm4gW3Nlc3Npb25JZCwgY2Fwc107XG4gICAgICB9XG5cbiAgICAgIHRoaXMud2RhID0gbmV3IFdlYkRyaXZlckFnZW50KHtcbiAgICAgICAgc2ltOiB0aGlzLnNpbSwgLy9UT0RPIGNhbid0IGJlIGEgc2ltIGlmIHJlYWwgZGV2aWNlLiBmaW5kIGEgZGlmZmVyZW50IHdheSB0byBzdXBwbHkgbG9ncyB0byB3ZWJkcml2ZXJhZ2VudFxuICAgICAgICBwbGF0Zm9ybVZlcnNpb246IHRoaXMub3B0cy5wbGF0Zm9ybVZlcnNpb24sXG4gICAgICAgIGhvc3Q6IHRoaXMub3B0cy5ob3N0LFxuICAgICAgICBhZ2VudFBhdGg6IHRoaXMub3B0cy5hZ2VudFBhdGhcbiAgICAgIH0pO1xuXG4gICAgICBhd2FpdCB0aGlzLndkYS5sYXVuY2goc2Vzc2lvbklkKTtcblxuICAgICAgdGhpcy5wcm94eVJlcVJlcyA9IHRoaXMud2RhLnByb3h5UmVxUmVzLmJpbmQodGhpcy53ZGEpO1xuICAgICAgdGhpcy5qd3BQcm94eUFjdGl2ZSA9IHRydWU7XG5cbiAgICAgIGF3YWl0IHRoaXMuc3RhcnRXZGFTZXNzaW9uKHRoaXMub3B0cy5hcHAsIHRoaXMub3B0cy5idW5kbGVJZCk7XG5cbiAgICAgIHJldHVybiBbc2Vzc2lvbklkLCBjYXBzXTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2cuZXJyb3IoZSk7XG4gICAgICBhd2FpdCB0aGlzLmRlbGV0ZVNlc3Npb24oKTtcbiAgICAgIHRocm93IGU7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZGVsZXRlU2Vzc2lvbiAoKSB7XG4gICAgdGhpcy5qd3BQcm94eUFjdGl2ZSA9IGZhbHNlO1xuICAgIHRoaXMucHJveHlSZXFSZXMgPSBudWxsO1xuXG4gICAgbGV0IG5vUmVzZXQgPSB0aGlzLm9wdHMubm9SZXNldDtcblxuICAgIGlmICh0aGlzLndkYSkge1xuICAgICAgYXdhaXQgdGhpcy53ZGEucXVpdCgpO1xuICAgIH1cbiAgICAvLyBUT0RPOiB0aGlzIGtpbGxzIGFsbCBzaW11bGF0b3JzLCBzbyBuZWVkcyB0byBiZSBjaGFuZ2VkIGlmIHJ1bm5pbmcgbXVsdGlwbGUgc2ltc1xuICAgIGlmICghbm9SZXNldCAmJiB0aGlzLnNpbSkge1xuICAgICAgaWYgKHRoaXMubGlmZWN5Y2xlRGF0YS5ib290U2ltKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuc2ltLnNodXRkb3duKCk7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5saWZlY3ljbGVEYXRhLmNyZWF0ZVNpbSkge1xuICAgICAgICBhd2FpdCB0aGlzLnNpbS5kZWxldGUoKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBhd2FpdCBzdXBlci5kZWxldGVTZXNzaW9uKCk7XG4gIH1cblxuICBhc3luYyBjaGVja0FwcFByZXNlbnQgKCkge1xuICAgIGxvZy5kZWJ1ZyhcIkNoZWNraW5nIHdoZXRoZXIgYXBwIGlzIGFjdHVhbGx5IHByZXNlbnRcIik7XG4gICAgaWYgKCEoYXdhaXQgZnMuZXhpc3RzKHRoaXMub3B0cy5hcHApKSkge1xuICAgICAgbG9nLmVycm9yQW5kVGhyb3coYENvdWxkIG5vdCBmaW5kIGFwcCBhdCAke3RoaXMub3B0cy5hcHB9YCk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZGV0ZXJtaW5lRGV2aWNlKCkge1xuICAgIGxldCByZWFsRGV2aWNlLCB1ZGlkO1xuXG4gICAgaWYgKHRoaXMub3B0cy51ZGlkKSB7XG4gICAgICBpZiAoYXdhaXQgc2ltRXhpc3RzKHRoaXMub3B0cy51ZGlkKSkge1xuICAgICAgICB0aGlzLmxpZmVjeWNsZURhdGEuY3JlYXRlU2ltID0gZmFsc2U7XG4gICAgICAgIHVkaWQgPSB0aGlzLm9wdHMudWRpZDtcbiAgICAgICAgcmVhbERldmljZSA9IGZhbHNlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvL1RPRE8gY2hlY2sgZm9yIHJlYWwgZGV2aWNlXG4gICAgICAgICAgdGhyb3cgbmV3IGVycm9ycy5TZXNzaW9uTm90Q3JlYXRlZEVycm9yKGBubyBzaW11bGF0b3IgZm91bmQgd2l0aCB1ZGlkICR7dGhpcy5vcHRzLnVkaWR9YCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIG5vIHVkaWQ6IGNyZWF0ZSBhIG5ldyBzaW1cbiAgICAgIGxvZy5pbmZvKGB1ZGlkIG5vdCBwcm92aWRlZCwgdXNpbmcgZGVzaXJlZCBjYXBzIHRvIGNyZWF0ZSBhIG5ldyBzaW1gKTtcbiAgICAgIGxldCBzaW0gPSBhd2FpdCB0aGlzLmNyZWF0ZVNpbSgpO1xuICAgICAgdWRpZCA9IHNpbS51ZGlkO1xuICAgICAgcmVhbERldmljZSA9IGZhbHNlO1xuICAgIH1cblxuICAgIHJldHVybiB7IHVkaWQsIHJlYWxEZXZpY2UgfTtcbiAgfVxuXG4gIGFzeW5jIHN0YXJ0U2ltICgpIHtcbiAgICBsZXQgc2ltO1xuICAgIC8vIFRPRE8gZm9yIG5vdyBqdXN0IGtpbGwgYWxsIHNpbXMgdW5sZXNzIHNwZWNpZmllZCB1ZGlkIGlzIGJvb3RlZC5cbiAgICAvLyBpZiBib290ZWQsIHVzZSBpdC4gaWYgbm90IGJvb3RlZCwgc3RhcnQgaXQgdXBcbiAgICAvLyBpZiBubyB1ZGlkLCB3ZWxsIGxldHMgc2VlIGlmIHdlIGNhbiBzdGFydCBvbmUgdXAgYmFzZWQgb24gZGVzaXJlZCBjYXBzXG4gICAgLy8gaWYgd2Ugc3VwcG9ydCBtdWx0aXBsZSBzaW1zIHdlIG5lZWQgdG8gY2hhbmdlIHRoaXNcblxuICAgIHNpbSA9IGF3YWl0IGdldFNpbXVsYXRvcih0aGlzLm9wdHMudWRpZCk7XG4gICAgaWYgKCFhd2FpdCBzaW1Cb290ZWQodGhpcy5vcHRzLnVkaWQpKSB7XG4gICAgICBsb2cuaW5mbyhgc2ltdWxhdG9yIHdpdGggdWRpZCAke3RoaXMub3B0cy51ZGlkfSBub3QgYm9vdGVkLiBCb290aW5nIHVwIG5vd2ApO1xuICAgICAgYXdhaXQga2lsbEFsbFNpbXVsYXRvcnMoKTtcbiAgICAgIGF3YWl0IHNpbS5ydW4oKTtcbiAgICAgIHRoaXMubGlmZWN5Y2xlRGF0YS5ib290U2ltID0gdHJ1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgbG9nLmluZm8oYHNpbXVsYXRvciAke3RoaXMub3B0cy51ZGlkfSBhbHJlYWR5IGJvb3RlZGApO1xuICAgICAgdGhpcy5saWZlY3ljbGVEYXRhLmJvb3RTaW0gPSBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHNpbTtcbiAgfVxuXG4gIGFzeW5jIGNyZWF0ZVNpbSAoKSB7XG5cbiAgICB0aGlzLmxpZmVjeWNsZURhdGEuY3JlYXRlU2ltID0gdHJ1ZTtcblxuICAgIC8vIGNyZWF0ZSBzaW0gZm9yIGNhcHNcbiAgICBsZXQgc2ltID0gYXdhaXQgY3JlYXRlU2ltKHRoaXMuY2FwcywgdGhpcy5zZXNzaW9uSWQpO1xuICAgIGxvZy5pbmZvKGBjcmVhdGVkIHNpbXVsYXRvciAke3NpbS51ZGlkfS4gQm9vdGluZyBpdCB1cGApO1xuXG4gICAgcmV0dXJuIHNpbTtcbiAgfVxuXG4gIGFzeW5jIGxhdW5jaEFwcCAoKSB7XG4gICAgY29uc3QgQVBQX0xBVU5DSF9USU1FT1VUID0gMjAgKiAxMDAwO1xuXG4gICAgYXdhaXQgbGF1bmNoKHRoaXMuc2ltLnVkaWQsIHRoaXMub3B0cy5idW5kbGVJZCk7XG5cbiAgICBsZXQgY2hlY2tTdGF0dXMgPSBhc3luYyAoKSA9PiB7XG4gICAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLndkYS5qd3Byb3h5LmNvbW1hbmQoJy9zdGF0dXMnLCAnR0VUJyk7XG4gICAgICBsZXQgY3VycmVudEFwcCA9IHJlc3BvbnNlLmN1cnJlbnRBcHAuYnVuZGxlSUQ7XG4gICAgICBpZiAoY3VycmVudEFwcCAhPT0gdGhpcy5vcHRzLmJ1bmRsZUlkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgJHt0aGlzLm9wdHMuYnVuZGxlSWR9IG5vdCBpbiBmb3JlZ3JvdW5kLiAke2N1cnJlbnRBcHB9IGlzIGluIGZvcmVncm91bmRgKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgbG9nLmluZm8oYHdhaXRpbmcgZm9yICR7dGhpcy5vcHRzLmJ1bmRsZUlkfSB0byBiZSBpbiBmb3JlZ3JvdW5kYCk7XG4gICAgYXdhaXQgcmV0cnlJbnRlcnZhbChBUFBfTEFVTkNIX1RJTUVPVVQgLyAyMDAsIDIwMCwgY2hlY2tTdGF0dXMpO1xuICAgIGxvZy5pbmZvKGAke3RoaXMub3B0cy5idW5kbGVJZH0gaXMgaW4gZm9yZWdyb3VuZGApO1xuICB9XG5cbiAgYXN5bmMgc3RhcnRXZGFTZXNzaW9uKGFwcFBhdGgsIGJ1bmRsZUlkKSB7XG4gICAgbGV0IGRlc2lyZWQgPSB7XG4gICAgICBkZXNpcmVkQ2FwYWJpbGl0aWVzOiB7XG4gICAgICAgIGFwcDogYXBwUGF0aCxcbiAgICAgICAgYnVuZGxlSWQ6IGJ1bmRsZUlkXG4gICAgICB9XG4gICAgfTtcbiAgICBhd2FpdCB0aGlzLndkYS5qd3Byb3h5LmNvbW1hbmQoJy9zZXNzaW9uJywgJ1BPU1QnLCBkZXNpcmVkKTtcbiAgfVxuXG4gIHNob3VsZFByb3h5VG9TYWZhcmkgKGNvbW1hbmQpIHtcbiAgICBsZXQgY29tbWFuZHNUb05vdFByb3h5VG9TYWZhcmkgPSBbXG4gICAgICAnZ2V0Q29udGV4dHMnLFxuICAgICAgJ3NldENvbnRleHQnLFxuICAgICAgJ2dldEN1cnJlbnRDb250ZXh0JyxcbiAgICAgICdkZWxldGVTZXNzaW9uJ1xuICAgIF07XG5cbiAgICByZXR1cm4gdGhpcy5pbldlYnZpZXcoKSAmJlxuICAgICAgICAgICBpc1Nlc3Npb25Db21tYW5kKGNvbW1hbmQpICYmXG4gICAgICAgICAgICFfLmNvbnRhaW5zKGNvbW1hbmRzVG9Ob3RQcm94eVRvU2FmYXJpLCBjb21tYW5kKTtcbiAgfVxuXG4gIGFzeW5jIGV4ZWN1dGVDb21tYW5kIChjbWQpIHtcbiAgICBpZiAodGhpcy5zaG91bGRQcm94eVRvU2FmYXJpKGNtZCkpIHtcbiAgICAgIGxldCBzYWZhcmlEcml2ZXIgPSBhd2FpdCB0aGlzLmdldFNhZmFyaURyaXZlcigpO1xuICAgICAgcmV0dXJuIGF3YWl0IHNhZmFyaURyaXZlci5leGVjdXRlQ29tbWFuZCguLi5hcmd1bWVudHMpO1xuICAgIH1cblxuICAgIHJldHVybiBhd2FpdCBzdXBlci5leGVjdXRlQ29tbWFuZCguLi5hcmd1bWVudHMpO1xuICB9XG5cbiAgLy8gT3ZlcnJpZGUgUHJveHkgbWV0aG9kcyBmcm9tIEJhc2VEcml2ZXJcbiAgcHJveHlBY3RpdmUgKCkge1xuICAgIHJldHVybiB0aGlzLmp3cFByb3h5QWN0aXZlO1xuICB9XG5cbiAgZ2V0UHJveHlBdm9pZExpc3QgKCkge1xuICAgIHJldHVybiBbXG4gICAgICBbJ0dFVCcsIC9jb250ZXh0L10sXG4gICAgICBbJ1BPU1QnLCAvY29udGV4dC9dXG4gICAgXTtcbiAgfVxuXG4gIGNhblByb3h5ICgpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIGlzU2FmYXJpICgpIHtcbiAgICByZXR1cm4gdGhpcy5zYWZhcmk7XG4gIH1cblxuICBpc1JlYWxEZXZpY2UgKCkge1xuICAgIHJldHVybiB0aGlzLm9wdHMucmVhbERldmljZTtcbiAgfVxuXG4gIGlzU2ltdWxhdG9yICgpIHtcbiAgICByZXR1cm4gIXRoaXMub3B0cy5yZWFsRGV2aWNlO1xuICB9XG5cbiAgaW5XZWJ2aWV3ICgpIHtcbiAgICByZXR1cm4gdGhpcy5pc1NhZmFyaSgpIHx8IHRoaXMud2VidmlldztcbiAgfVxuXG59XG5cbmZvciAobGV0IFtjbWQsIGZuXSBvZiBfLnBhaXJzKGNvbW1hbmRzKSkge1xuICBXZWJEcml2ZXJBZ2VudERyaXZlci5wcm90b3R5cGVbY21kXSA9IGZuO1xufVxuXG5leHBvcnQgeyBXZWJEcml2ZXJBZ2VudERyaXZlciB9O1xuIl19