appium-xcuitest-driver-conan
Version:
Appium driver for iOS using XCUITest for backend
1,043 lines (829 loc) • 78.8 kB
JavaScript
'use strict';
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 _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _url2 = require('url');
var _url3 = _interopRequireDefault(_url2);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _asyncbox = require('asyncbox');
var _teen_process = require('teen_process');
var _appiumBaseDriver = require('appium-base-driver');
var _appiumSupport = require('appium-support');
var _logger = require('./logger');
var _logger2 = _interopRequireDefault(_logger);
var _noSessionProxy = require("./no-session-proxy");
var _utilsJs = require('./utils.js');
var _webdriveragentUtils = require('./webdriveragent-utils');
var xcodeLog = _appiumSupport.logger.getLogger('Xcode');
var iproxyLog = _appiumSupport.logger.getLogger('iProxy');
var BOOTSTRAP_PATH = _path2['default'].resolve(__dirname, '..', '..', 'WebDriverAgent');
var WDA_BUNDLE_ID = 'com.apple.test.WebDriverAgentRunner-Runner';
var DEFAULT_SIGNING_ID = "iPhone Developer";
var WDA_LAUNCH_TIMEOUT = 60 * 1000;
var IPROXY_TIMEOUT = 5000;
var WDA_AGENT_PORT = 8100;
var WDA_BASE_URL = 'http://localhost';
var BUILD_TEST_DELAY = 1000;
var WebDriverAgent = (function () {
// agentPath (optional): Path to WebdriverAgent Executable (inside WebDriverAgent.app)
function WebDriverAgent(xcodeVersion) {
var args = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
_classCallCheck(this, WebDriverAgent);
this.xcodeVersion = xcodeVersion;
this.device = args.device;
this.platformVersion = args.platformVersion;
this.host = args.host;
this.realDevice = !!args.realDevice;
this.setWDAPaths(args.bootstrapPath, args.agentPath);
this.wdaLocalPort = args.wdaLocalPort;
this.showXcodeLog = !!args.showXcodeLog;
this.xcodeConfigFile = args.xcodeConfigFile;
this.xcodeOrgId = args.xcodeOrgId;
this.xcodeSigningId = args.xcodeSigningId || DEFAULT_SIGNING_ID;
this.keychainPath = args.keychainPath;
this.keychainPassword = args.keychainPassword;
this.prebuildWDA = args.prebuildWDA;
this.usePrebuiltWDA = args.usePrebuiltWDA;
this.useSimpleBuildTest = args.useSimpleBuildTest;
this.webDriverAgentUrl = args.webDriverAgentUrl;
this.updatedWDABundleId = args.updatedWDABundleId;
this.expectIProxyErrors = true;
this.wdaLaunchTimeout = args.wdaLaunchTimeout || WDA_LAUNCH_TIMEOUT;
this.wdaConnectionTimeout = args.wdaConnectionTimeout;
this.useCarthageSsl = _lodash2['default'].isBoolean(args.useCarthageSsl) && args.useCarthageSsl;
}
_createClass(WebDriverAgent, [{
key: 'setWDAPaths',
value: function setWDAPaths(bootstrapPath, agentPath) {
// allow the user to specify a place for WDA. This is undocumented and
// only here for the purposes of testing development of WDA
this.bootstrapPath = bootstrapPath || BOOTSTRAP_PATH;
_logger2['default'].info('Using WDA path: \'' + this.bootstrapPath + '\'');
// for backward compatibility we need to be able to specify agentPath too
this.agentPath = agentPath || _path2['default'].resolve(this.bootstrapPath, 'WebDriverAgent.xcodeproj');
_logger2['default'].info('Using WDA agent: \'' + this.agentPath + '\'');
}
}, {
key: 'uninstall',
value: function uninstall() {
return _regeneratorRuntime.async(function uninstall$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
_logger2['default'].debug('Removing WDA application from device');
context$2$0.next = 3;
return _regeneratorRuntime.awrap(this.device.removeApp(WDA_BUNDLE_ID));
case 3:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'launch',
value: function launch(sessionId) {
return _regeneratorRuntime.async(function launch$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
if (!this.webDriverAgentUrl) {
context$2$0.next = 5;
break;
}
_logger2['default'].info('Using provided WebdriverAgent at \'' + this.webDriverAgentUrl + '\'');
this.url = this.webDriverAgentUrl;
this.setupProxies(sessionId);
return context$2$0.abrupt('return', this.webDriverAgentUrl);
case 5:
_logger2['default'].info('Launching WebDriverAgent on the device');
this.setupProxies(sessionId);
context$2$0.next = 9;
return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(this.agentPath));
case 9:
if (context$2$0.sent) {
context$2$0.next = 11;
break;
}
throw new Error('Trying to use WebDriverAgent project at \'' + this.agentPath + '\' but the ' + 'file does not exist');
case 11:
context$2$0.next = 13;
return _regeneratorRuntime.awrap((0, _webdriveragentUtils.checkForDependencies)(this.bootstrapPath, this.useCarthageSsl));
case 13:
if (!(this.realDevice && this.updatedWDABundleId)) {
context$2$0.next = 16;
break;
}
context$2$0.next = 16;
return _regeneratorRuntime.awrap((0, _webdriveragentUtils.updateProjectFile)(this.agentPath, this.updatedWDABundleId));
case 16:
context$2$0.next = 18;
return _regeneratorRuntime.awrap(this.killHangingProcesses());
case 18:
if (!(this.xcodeVersion.major === 7 || this.xcodeVersion.major === 8 && this.xcodeVersion.minor === 0)) {
context$2$0.next = 22;
break;
}
_logger2['default'].debug('Using Xcode ' + this.xcodeVersion.versionString + ', so fixing WDA codebase');
context$2$0.next = 22;
return _regeneratorRuntime.awrap((0, _webdriveragentUtils.fixForXcode7)(this.bootstrapPath, true));
case 22:
if (!this.prebuildWDA) {
context$2$0.next = 37;
break;
}
if (!(this.xcodeVersion.major === 7)) {
context$2$0.next = 27;
break;
}
_logger2['default'].debug('Capability \'prebuildWDA\' set, but on xcode version ' + this.xcodeVersion.versionString + ' so skipping');
context$2$0.next = 37;
break;
case 27:
// first do a build phase
_logger2['default'].debug('Pre-building WDA before launching test');
this.usePrebuiltWDA = true;
context$2$0.next = 31;
return _regeneratorRuntime.awrap(this.createXcodeBuildSubProcess(true));
case 31:
this.xcodebuild = context$2$0.sent;
context$2$0.next = 34;
return _regeneratorRuntime.awrap(this.startXcodebuild(true));
case 34:
this.xcodebuild = null;
// pause a moment
context$2$0.next = 37;
return _regeneratorRuntime.awrap(_bluebird2['default'].delay(BUILD_TEST_DELAY));
case 37:
context$2$0.next = 39;
return _regeneratorRuntime.awrap(this.createXcodeBuildSubProcess());
case 39:
this.xcodebuild = context$2$0.sent;
if (!this.realDevice) {
context$2$0.next = 44;
break;
}
this.iproxy = this.createiProxySubProcess(this.url.port, WDA_AGENT_PORT);
context$2$0.next = 44;
return _regeneratorRuntime.awrap(this.startiproxy());
case 44:
context$2$0.next = 46;
return _regeneratorRuntime.awrap(this.startXcodebuild());
case 46:
return context$2$0.abrupt('return', context$2$0.sent);
case 47:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'setupProxies',
value: function setupProxies(sessionId) {
var proxyOpts = {
server: this.url.hostname,
port: this.url.port,
base: '',
timeout: this.wdaConnectionTimeout
};
this.jwproxy = new _appiumBaseDriver.JWProxy(proxyOpts);
this.jwproxy.sessionId = sessionId;
this.proxyReqRes = this.jwproxy.proxyReqRes.bind(this.jwproxy);
this.noSessionProxy = new _noSessionProxy.NoSessionProxy(proxyOpts);
this.noSessionProxyReqRes = this.noSessionProxy.proxyReqRes.bind(this.noSessionProxy);
}
}, {
key: 'getXcodeBuildCommand',
value: function getXcodeBuildCommand() {
var _args;
var buildOnly = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
var cmd = 'xcodebuild';
var args = undefined;
// figure out the targets for xcodebuild
if (this.xcodeVersion.major < 8) {
if (this.usePrebuiltWDA) {
var msg = '\'usePrebuiltWDA\' set, but on Xcode ' + ('\'' + this.xcodeVersion.versionString + '\', so skipping, as it ') + 'needs a version >= 8';
_logger2['default'].warn(msg);
}
args = ['build', 'test'];
} else {
var _ref = this.useSimpleBuildTest ? ['build', 'test'] : ['build-for-testing', 'test-without-building'];
var _ref2 = _slicedToArray(_ref, 2);
var buildCmd = _ref2[0];
var testCmd = _ref2[1];
if (buildOnly) {
args = [buildCmd];
} else if (this.usePrebuiltWDA) {
args = [testCmd];
} else {
args = [buildCmd, testCmd];
}
}
// add the rest of the arguments for the xcodebuild command
var genericArgs = ['-project', this.agentPath, '-scheme', 'WebDriverAgentRunner', '-destination', 'id=' + this.device.udid, '-configuration', 'Debug'];
(_args = args).push.apply(_args, genericArgs);
var versionMatch = new RegExp(/^(\d+)\.(\d+)/).exec(this.platformVersion);
if (versionMatch) {
args.push('IPHONEOS_DEPLOYMENT_TARGET=' + versionMatch[1] + '.' + versionMatch[2]);
} else {
_logger2['default'].warn('Cannot parse major and minor version numbers from platformVersion "' + this.platformVersion + '". ' + 'Will build for the default platform instead');
}
if (this.realDevice && this.xcodeConfigFile) {
_logger2['default'].debug('Using Xcode configuration file: \'' + this.xcodeConfigFile + '\'');
args.push('-xcconfig', this.xcodeConfigFile);
}
return { cmd: cmd, args: args };
}
}, {
key: 'createXcodeBuildSubProcess',
value: function createXcodeBuildSubProcess() {
var buildOnly = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
var _getXcodeBuildCommand, cmd, args, xcodebuild, logXcodeOutput;
return _regeneratorRuntime.async(function createXcodeBuildSubProcess$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
if (!this.realDevice) {
context$2$0.next = 8;
break;
}
if (!(this.keychainPath && this.keychainPassword)) {
context$2$0.next = 4;
break;
}
context$2$0.next = 4;
return _regeneratorRuntime.awrap((0, _webdriveragentUtils.setRealDeviceSecurity)(this.keychainPath, this.keychainPassword));
case 4:
if (!(this.xcodeOrgId && this.xcodeSigningId && !this.xcodeConfigFile)) {
context$2$0.next = 8;
break;
}
context$2$0.next = 7;
return _regeneratorRuntime.awrap((0, _utilsJs.generateXcodeConfigFile)(this.xcodeOrgId, this.xcodeSigningId));
case 7:
this.xcodeConfigFile = context$2$0.sent;
case 8:
_getXcodeBuildCommand = this.getXcodeBuildCommand(buildOnly);
cmd = _getXcodeBuildCommand.cmd;
args = _getXcodeBuildCommand.args;
_logger2['default'].debug('Beginning ' + (buildOnly ? 'build' : 'test') + ' with command \'' + cmd + ' ' + args.join(' ') + '\' ' + ('in directory \'' + this.bootstrapPath + '\''));
xcodebuild = new _teen_process.SubProcess(cmd, args, { cwd: this.bootstrapPath });
logXcodeOutput = this.showXcodeLog;
_logger2['default'].debug('Output from xcodebuild ' + (logXcodeOutput ? 'will' : 'will not') + ' be logged');
xcodebuild.on('output', function (stdout, stderr) {
var out = stdout || stderr;
// we want to pull out the log file that is created, and highlight it
// for diagnostic purposes
if (out.indexOf('Writing diagnostic log for test session to') !== -1) {
// pull out the first line that begins with the path separator
// which *should* be the line indicating the log file generated
xcodebuild.logLocation = _lodash2['default'].first(_lodash2['default'].remove(out.trim().split('\n'), function (v) {
return v.indexOf(_path2['default'].sep) === 0;
}));
_logger2['default'].debug('Log file for xcodebuild test: ' + xcodebuild.logLocation);
}
// if we have an error we want to output the logs
// otherwise the failure is inscrutible
// but do not log permission errors from trying to write to attachments folder
if (out.indexOf('Error Domain=') !== -1 && out.indexOf('Error writing attachment data to file') === -1) {
logXcodeOutput = true;
// terrible hack to handle case where xcode return 0 but is failing
xcodebuild._wda_error_occurred = true;
}
if (logXcodeOutput) {
// do not log permission errors from trying to write to attachments folder
if (out.indexOf('Error writing attachment data to file') === -1) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = _getIterator(out.split('\n')), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var line = _step.value;
xcodeLog.info(line);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator['return']) {
_iterator['return']();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
}
});
return context$2$0.abrupt('return', xcodebuild);
case 17:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'createiProxySubProcess',
value: function createiProxySubProcess(localport, deviceport) {
_logger2['default'].debug('Starting iproxy to forward traffic from local port ' + localport + ' to device port ' + deviceport + ' over USB');
return new _teen_process.SubProcess('iproxy', [localport, deviceport, this.device.udid]);
}
}, {
key: 'startXcodebuild',
value: function startXcodebuild() {
var buildOnly = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
return _regeneratorRuntime.async(function startXcodebuild$(context$2$0) {
var _this = this;
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
context$2$0.next = 2;
return _regeneratorRuntime.awrap(new _bluebird2['default'](function (resolve, reject) {
_this.xcodebuild.on('exit', function callee$3$0(code, signal) {
var data, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, line;
return _regeneratorRuntime.async(function callee$3$0$(context$4$0) {
while (1) switch (context$4$0.prev = context$4$0.next) {
case 0:
_logger2['default'].info('xcodebuild exited with code \'' + code + '\' and signal \'' + signal + '\'');
// print out the xcodebuild file if users have asked for it
if (!(this.showXcodeLog && this.xcodebuild.logLocation)) {
context$4$0.next = 31;
break;
}
xcodeLog.info('Contents of xcodebuild log file \'' + this.xcodebuild.logLocation + '\':');
context$4$0.prev = 3;
context$4$0.next = 6;
return _regeneratorRuntime.awrap(_appiumSupport.fs.readFile(this.xcodebuild.logLocation, 'utf-8'));
case 6:
data = context$4$0.sent;
_iteratorNormalCompletion2 = true;
_didIteratorError2 = false;
_iteratorError2 = undefined;
context$4$0.prev = 10;
for (_iterator2 = _getIterator(data.split('\n')); !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
line = _step2.value;
xcodeLog.info(line);
}
context$4$0.next = 18;
break;
case 14:
context$4$0.prev = 14;
context$4$0.t0 = context$4$0['catch'](10);
_didIteratorError2 = true;
_iteratorError2 = context$4$0.t0;
case 18:
context$4$0.prev = 18;
context$4$0.prev = 19;
if (!_iteratorNormalCompletion2 && _iterator2['return']) {
_iterator2['return']();
}
case 21:
context$4$0.prev = 21;
if (!_didIteratorError2) {
context$4$0.next = 24;
break;
}
throw _iteratorError2;
case 24:
return context$4$0.finish(21);
case 25:
return context$4$0.finish(18);
case 26:
context$4$0.next = 31;
break;
case 28:
context$4$0.prev = 28;
context$4$0.t1 = context$4$0['catch'](3);
_logger2['default'].debug('Unable to access xcodebuild log file: \'' + context$4$0.t1.message + '\'');
case 31:
this.xcodebuild.processExited = true;
if (!(this.xcodebuild._wda_error_occurred || !signal && code !== 0)) {
context$4$0.next = 34;
break;
}
return context$4$0.abrupt('return', reject(new Error('xcodebuild failed with code ' + code)));
case 34:
if (!buildOnly) {
context$4$0.next = 36;
break;
}
return context$4$0.abrupt('return', resolve());
case 36:
case 'end':
return context$4$0.stop();
}
}, null, _this, [[3, 28], [10, 14, 18, 26], [19,, 21, 25]]);
});
return (function callee$3$0() {
var startTime, _status, msg;
return _regeneratorRuntime.async(function callee$3$0$(context$4$0) {
while (1) switch (context$4$0.prev = context$4$0.next) {
case 0:
context$4$0.prev = 0;
startTime = process.hrtime();
context$4$0.next = 4;
return _regeneratorRuntime.awrap(this.xcodebuild.start());
case 4:
if (buildOnly) {
context$4$0.next = 9;
break;
}
context$4$0.next = 7;
return _regeneratorRuntime.awrap(this.waitForStart(startTime));
case 7:
_status = context$4$0.sent;
resolve(_status);
case 9:
context$4$0.next = 16;
break;
case 11:
context$4$0.prev = 11;
context$4$0.t0 = context$4$0['catch'](0);
msg = 'Unable to start WebDriverAgent: ' + context$4$0.t0;
_logger2['default'].error(msg);
reject(new Error(msg));
case 16:
case 'end':
return context$4$0.stop();
}
}, null, _this, [[0, 11]]);
})();
}));
case 2:
return context$2$0.abrupt('return', context$2$0.sent);
case 3:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'waitForStart',
value: function waitForStart(startTime) {
var currentStatus, retries, endTime, startupTime;
return _regeneratorRuntime.async(function waitForStart$(context$2$0) {
var _this2 = this;
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
// try to connect once every 0.5 seconds, until `wdaLaunchTimeout` is up
_logger2['default'].debug('Waiting up to ' + this.wdaLaunchTimeout + 'ms for WebDriverAgent to start');
currentStatus = null;
context$2$0.prev = 2;
retries = parseInt(this.wdaLaunchTimeout / 500, 10);
context$2$0.next = 6;
return _regeneratorRuntime.awrap((0, _asyncbox.retryInterval)(retries, 500, function callee$2$0() {
var proxyTimeout;
return _regeneratorRuntime.async(function callee$2$0$(context$3$0) {
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
if (!this.xcodebuild.processExited) {
context$3$0.next = 2;
break;
}
return context$3$0.abrupt('return');
case 2:
proxyTimeout = this.noSessionProxy.timeout;
this.noSessionProxy.timeout = 1000;
context$3$0.prev = 4;
context$3$0.next = 7;
return _regeneratorRuntime.awrap(this.noSessionProxy.command('/status', 'GET'));
case 7:
currentStatus = context$3$0.sent;
if (currentStatus && currentStatus.ios && currentStatus.ios.ip) {
this.agentUrl = currentStatus.ios.ip;
_logger2['default'].debug('WebDriverAgent running on ip \'' + this.agentUrl + '\'');
}
context$3$0.next = 14;
break;
case 11:
context$3$0.prev = 11;
context$3$0.t0 = context$3$0['catch'](4);
throw new Error('Unable to connect to running WebDriverAgent: ' + context$3$0.t0.message);
case 14:
context$3$0.prev = 14;
this.noSessionProxy.timeout = proxyTimeout;
return context$3$0.finish(14);
case 17:
case 'end':
return context$3$0.stop();
}
}, null, _this2, [[4, 11, 14, 17]]);
}));
case 6:
if (!this.xcodebuild.processExited) {
context$2$0.next = 8;
break;
}
return context$2$0.abrupt('return', currentStatus);
case 8:
endTime = process.hrtime(startTime);
startupTime = parseInt((endTime[0] * 1e9 + endTime[1]) / 1e6, 10);
_logger2['default'].debug('WebDriverAgent successfully started after ' + startupTime + 'ms');
context$2$0.next = 17;
break;
case 13:
context$2$0.prev = 13;
context$2$0.t0 = context$2$0['catch'](2);
// at this point, if we have not had any errors from xcode itself (reported
// elsewhere), we can let this go through and try to create the session
_logger2['default'].debug(context$2$0.t0.message);
_logger2['default'].warn('Getting status of WebDriverAgent on device timed out. Continuing');
case 17:
return context$2$0.abrupt('return', currentStatus);
case 18:
case 'end':
return context$2$0.stop();
}
}, null, this, [[2, 13]]);
}
}, {
key: 'startiproxy',
value: function startiproxy() {
return _regeneratorRuntime.async(function startiproxy$(context$2$0) {
var _this3 = this;
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
context$2$0.next = 2;
return _regeneratorRuntime.awrap(new _bluebird2['default'](function (resolve, reject) {
_this3.iproxy.on('exit', function (code) {
_logger2['default'].debug('iproxy exited with code \'' + code + '\'');
if (code) {
return reject(new Error('iproxy exited with code \'' + code + '\''));
}
});
_this3.iproxy.on('output', function (stdout, stderr) {
// do nothing if we expect errors
if (_this3.expectIProxyErrors) {
return;
}
var out = stdout || stderr;
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = _getIterator(out.split('\n')), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var line = _step3.value;
if (!line.length) {
continue;
}
if (line.indexOf('Resource temporarily unavailable') !== -1) {
// this generally happens when WDA does not respond,
// so print a more useful message
_logger2['default'].debug('Connection to WDA timed out');
} else {
iproxyLog.debug(line);
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3['return']) {
_iterator3['return']();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
});
return (function callee$3$0() {
return _regeneratorRuntime.async(function callee$3$0$(context$4$0) {
while (1) switch (context$4$0.prev = context$4$0.next) {
case 0:
context$4$0.prev = 0;
context$4$0.next = 3;
return _regeneratorRuntime.awrap(this.iproxy.start(IPROXY_TIMEOUT));
case 3:
resolve();
context$4$0.next = 10;
break;
case 6:
context$4$0.prev = 6;
context$4$0.t0 = context$4$0['catch'](0);
_logger2['default'].error('Error starting iproxy: \'' + context$4$0.t0.message + '\'');
reject(new Error('Unable to start iproxy. Is it installed?'));
case 10:
case 'end':
return context$4$0.stop();
}
}, null, _this3, [[0, 6]]);
})();
}));
case 2:
return context$2$0.abrupt('return', context$2$0.sent);
case 3:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'killHangingProcesses',
value: function killHangingProcesses() {
var procNames, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, proc;
return _regeneratorRuntime.async(function killHangingProcesses$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
_logger2['default'].debug('Killing hanging processes');
context$2$0.next = 3;
return _regeneratorRuntime.awrap((0, _utilsJs.killAppUsingAppName)(this.device.udid, 'xcodebuild'));
case 3:
procNames = this.realDevice ? ['iproxy'] : ['XCTRunner'];
_iteratorNormalCompletion4 = true;
_didIteratorError4 = false;
_iteratorError4 = undefined;
context$2$0.prev = 7;
_iterator4 = _getIterator(procNames);
case 9:
if (_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done) {
context$2$0.next = 16;
break;
}
proc = _step4.value;
context$2$0.next = 13;
return _regeneratorRuntime.awrap((0, _utilsJs.killAppUsingAppName)(this.device.udid, proc));
case 13:
_iteratorNormalCompletion4 = true;
context$2$0.next = 9;
break;
case 16:
context$2$0.next = 22;
break;
case 18:
context$2$0.prev = 18;
context$2$0.t0 = context$2$0['catch'](7);
_didIteratorError4 = true;
_iteratorError4 = context$2$0.t0;
case 22:
context$2$0.prev = 22;
context$2$0.prev = 23;
if (!_iteratorNormalCompletion4 && _iterator4['return']) {
_iterator4['return']();
}
case 25:
context$2$0.prev = 25;
if (!_didIteratorError4) {
context$2$0.next = 28;
break;
}
throw _iteratorError4;
case 28:
return context$2$0.finish(25);
case 29:
return context$2$0.finish(22);
case 30:
case 'end':
return context$2$0.stop();
}
}, null, this, [[7, 18, 22, 30], [23,, 25, 29]]);
}
}, {
key: 'quit',
value: function quit() {
var killProcess;
return _regeneratorRuntime.async(function quit$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
killProcess = function killProcess(name, proc) {
return _regeneratorRuntime.async(function killProcess$(context$3$0) {
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
if (!(proc && proc.proc)) {
context$3$0.next = 22;
break;
}
_logger2['default'].info('Shutting down ' + name + ' process (pid ' + proc.proc.pid + ')');
context$3$0.prev = 2;
context$3$0.next = 5;
return _regeneratorRuntime.awrap(proc.stop('SIGTERM', 1000));
case 5:
context$3$0.next = 22;
break;
case 7:
context$3$0.prev = 7;
context$3$0.t0 = context$3$0['catch'](2);
if (!(context$3$0.t0.message.indexOf('Process didn\'t end after') === -1)) {
context$3$0.next = 11;
break;
}
throw context$3$0.t0;
case 11:
_logger2['default'].debug(name + ' process did not end in a timely fashion: \'' + context$3$0.t0.message + '\'. ' + 'Sending \'SIGKILL\'...');
context$3$0.prev = 12;
context$3$0.next = 15;
return _regeneratorRuntime.awrap(proc.stop('SIGKILL'));
case 15:
context$3$0.next = 22;
break;
case 17:
context$3$0.prev = 17;
context$3$0.t1 = context$3$0['catch'](12);
if (!(context$3$0.t1.message.indexOf('not currently running') !== -1)) {
context$3$0.next = 21;
break;
}
return context$3$0.abrupt('return');
case 21:
throw context$3$0.t1;
case 22:
case 'end':
return context$3$0.stop();
}
}, null, this, [[2, 7], [12, 17]]);
};
_logger2['default'].info('Shutting down sub-processes');
context$2$0.next = 4;
return _regeneratorRuntime.awrap(killProcess('xcodebuild', this.xcodebuild));
case 4:
context$2$0.next = 6;
return _regeneratorRuntime.awrap(killProcess('iproxy', this.iproxy));
case 6:
if (!(this.realDevice && this.updatedWDABundleId)) {
context$2$0.next = 9;
break;
}
context$2$0.next = 9;
return _regeneratorRuntime.awrap((0, _webdriveragentUtils.resetProjectFile)(this.agentPath, this.updatedWDABundleId));
case 9:
if (this.jwproxy) {
this.jwproxy.sessionId = null;
}
this.expectIProxyErrors = true;
case 11:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'url',
get: function get() {
if (!this._url) {
if (this.realDevice && this.wdaLocalPort) {
this._url = _url3['default'].parse(WDA_BASE_URL + ':' + this.wdaLocalPort);
} else {
this._url = _url3['default'].parse(WDA_BASE_URL + ':' + WDA_AGENT_PORT);
}
}
return this._url;
},
set: function set(_url) {
this._url = _url3['default'].parse(_url);
}
}, {
key: 'fullyStarted',
get: function get() {
return !this.expectIProxyErrors;
},
set: function set() {
var started = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
// before WDA is started we expect errors from iproxy, since it is not
// communicating with anything yet
this.expectIProxyErrors = !started;
}
}, {
key: 'derivedDataPath',
get: function get() {
if (!this._derivedDataPath && this.xcodebuild) {
// https://regex101.com/r/PqmX8I/1
var folderRegexp = /(.+\/WebDriverAgent-[^\/]+)/;
var match = folderRegexp.exec(this.xcodebuild.logLocation);
if (!match) {
return;
}
this._derivedDataPath = match[1];
}
return this._derivedDataPath;
}
}]);
return WebDriverAgent;
})();
exports['default'] = WebDriverAgent;
exports.WebDriverAgent = WebDriverAgent;
exports.WDA_BUNDLE_ID = WDA_BUNDLE_ID;
exports.BOOTSTRAP_PATH = BOOTSTRAP_PATH;
// make sure that the WDA dependencies have been built
// if necessary, update the bundleId to user's specification
//kill all hanging processes
// start the xcodebuild process
// wrap the start procedure in a promise so that we can catch, and report,
// any startup errors that are thrown as events
// in the case of just building, the process will exit and that is our finish
// there has been an error elsewhere and we need to short-circuit
// there has been an error elsewhere and we need to short-circuit
// must get [s, ns] array into ms
// the process ended but for some reason we were not informed
// if necessary, reset the bundleId to original value
//# sourceMappingURL=data:application/json;charset=utf8;base64,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