UNPKG

appium-xcuitest-driver-conan

Version:

Appium driver for iOS using XCUITest for backend

1,043 lines (829 loc) 78.8 kB
'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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