Best JavaScript code snippet using appium-xcuitest-driver
ios.js
Source:ios.js
1"use strict";2var path = require('path')3 , rimraf = require('rimraf')4 , ncp = require('ncp').ncp5 , fs = require('fs')6 , _ = require('underscore')7 , which = require('which')8 , logger = require('../../server/logger.js').get('appium')9 , exec = require('child_process').exec10 , spawn = require('child_process').spawn11 , bplistCreate = require('bplist-creator')12 , bplistParse = require('bplist-parser')13 , xmlplist = require('plist')14 , Device = require('../device.js')15 , Instruments = require('./instruments.js')16 , xcode = require('../../future.js').xcode17 , errors = require('../../server/errors.js')18 , deviceCommon = require('../common.js')19 , iOSLog = require('./ios-log.js')20 , iOSCrashLog = require('./ios-crash-log.js')21 , status = require("../../server/status.js")22 , iDevice = require('node-idevice')23 , async = require('async')24 , iOSController = require('./ios-controller.js')25 , iOSHybrid = require('./ios-hybrid.js')26 , settings = require('./settings.js')27 , Simulator = require('./simulator.js')28 , prepareBootstrap = require('./uiauto').prepareBootstrap29 , CommandProxy = require('./uiauto').CommandProxy30 , UnknownError = errors.UnknownError31 , binaryPlist = true32 , Args = require("vargs").Constructor33 , logCustomDeprecationWarning = require('../../helpers.js').logCustomDeprecationWarning;34// XML Plist library helper35var parseXmlPlistFile = function (plistFilename, cb) {36 try {37 var xmlContent = fs.readFileSync(plistFilename, 'utf8');38 var result = xmlplist.parse(xmlContent);39 return cb(null, result);40 } catch (ex) {41 return cb(ex);42 }43};44var parsePlistFile = function (plist, cb) {45 bplistParse.parseFile(plist, function (err, obj) {46 if (err) {47 logger.debug("Could not parse plist file (as binary) at " + plist);48 logger.info("Will try to parse the plist file as XML");49 parseXmlPlistFile(plist, function (err, obj) {50 if (err) {51 logger.debug("Could not parse plist file (as XML) at " + plist);52 return cb(err, null);53 } else {54 logger.debug("Parsed app Info.plist (as XML)");55 binaryPlist = false;56 cb(null, obj);57 }58 });59 } else {60 binaryPlist = true;61 if (obj.length) {62 logger.debug("Parsed app Info.plist (as binary)");63 cb(null, obj[0]);64 } else {65 cb(new Error("Binary Info.plist appears to be empty"));66 }67 }68 });69};70var IOS = function () {71 this.init();72};73_.extend(IOS.prototype, Device.prototype);74IOS.prototype._deviceInit = Device.prototype.init;75IOS.prototype.init = function () {76 this._deviceInit();77 this.appExt = ".app";78 this.capabilities = {79 webStorageEnabled: false80 , locationContextEnabled: false81 , browserName: 'iOS'82 , platform: 'MAC'83 , javascriptEnabled: true84 , databaseEnabled: false85 , takesScreenshot: true86 , networkConnectionEnabled: false87 };88 this.xcodeVersion = null;89 this.iOSSDKVersion = null;90 this.iosSimProcess = null;91 this.iOSSimUdid = null;92 this.logs = {};93 this.instruments = null;94 this.commandProxy = null;95 this.initQueue();96 this.onInstrumentsDie = function () {};97 this.stopping = false;98 this.cbForCurrentCmd = null;99 this.remote = null;100 this.curContext = null;101 this.curWebFrames = [];102 this.selectingNewPage = false;103 this.processingRemoteCmd = false;104 this.remoteAppKey = null;105 this.windowHandleCache = [];106 this.webElementIds = [];107 this.implicitWaitMs = 0;108 this.asyncWaitMs = 0;109 this.pageLoadMs = 60000;110 this.asyncResponseCb = null;111 this.returnedFromExecuteAtom = {};112 this.executedAtomsCounter = 0;113 this.curCoords = null;114 this.curWebCoords = null;115 this.onPageChangeCb = null;116 this.supportedStrategies = ["name", "xpath", "id", "-ios uiautomation",117 "class name", "accessibility id"];118 this.landscapeWebCoordsOffset = 0;119 this.localizableStrings = {};120 this.keepAppToRetainPrefs = false;121 this.isShuttingDown = false;122};123IOS.prototype._deviceConfigure = Device.prototype.configure;124IOS.prototype.configure = function (args, caps, cb) {125 var msg;126 this._deviceConfigure(args, caps);127 this.setIOSArgs();128 if (this.args.locationServicesAuthorized && !this.args.bundleId) {129 msg = "You must set the bundleId cap if using locationServicesEnabled";130 logger.error(msg);131 return cb(new Error(msg));132 }133 // on iOS8 we can use a bundleId to launch an app on the simulator, but134 // on previous versions we can only do so on a real device, so we need135 // to do a check of which situation we're in136 var ios8 = caps.platformVersion &&137 parseFloat(caps.platformVersion) >= 8;138 if (!this.args.app &&139 !((ios8 || this.args.udid) && this.args.bundleId)) {140 msg = "Please provide the 'app' or 'browserName' capability or start " +141 "appium with the --app or --browser-name argument. Alternatively, " +142 "you may provide the 'bundleId' and 'udid' capabilities for an app " +143 "under test on a real device.";144 logger.error(msg);145 return cb(new Error(msg));146 }147 if (parseFloat(caps.platformVersion) < 7.1) {148 logCustomDeprecationWarning('iOS version', caps.platformVersion,149 'iOS ' + caps.platformVersion + ' support has ' +150 'been deprecated and will be removed in a ' +151 'future version of Appium.');152 }153 return this.configureApp(cb);154};155IOS.prototype.setIOSArgs = function () {156 this.args.withoutDelay = !this.args.nativeInstrumentsLib;157 this.args.reset = !this.args.noReset;158 this.args.initialOrientation = this.capabilities.deviceOrientation ||159 this.args.orientation ||160 "PORTRAIT";161 this.useRobot = this.args.robotPort > 0;162 this.args.robotUrl = this.useRobot ?163 "http://" + this.args.robotAddress + ":" + this.args.robotPort + "" :164 null;165 this.curOrientation = this.args.initialOrientation;166 this.sock = path.resolve(this.args.tmpDir || '/tmp', 'instruments_sock');167 this.perfLogEnabled = !!(typeof this.args.loggingPrefs === 'object' && this.args.loggingPrefs.performance);168};169IOS.prototype.configureApp = function (cb) {170 var _cb = cb;171 cb = function (err) {172 if (err) {173 err = new Error("Bad app: " + this.args.app + ". App paths need to " +174 "be absolute, or relative to the appium server " +175 "install dir, or a URL to compressed file, or a " +176 "special app name. cause: " + err);177 }178 _cb(err);179 }.bind(this);180 var app = this.appString();181 // if the app name is a bundleId assign it to the bundleId property182 if (!this.args.bundleId && this.appIsPackageOrBundle(app)) {183 this.args.bundleId = app;184 }185 if (app !== "" && app.toLowerCase() === "settings") {186 if (parseFloat(this.args.platformVersion) >= 8) {187 logger.debug("We're on iOS8+ so not copying preferences app");188 this.args.bundleId = "com.apple.Preferences";189 this.args.app = null;190 }191 cb();192 } else if (this.args.bundleId &&193 this.appIsPackageOrBundle(this.args.bundleId) &&194 (app === "" || this.appIsPackageOrBundle(app))) {195 // we have a bundle ID, but no app, or app is also a bundle196 logger.debug("App is an iOS bundle, will attempt to run as pre-existing");197 cb();198 } else {199 Device.prototype.configureApp.call(this, cb);200 }201};202IOS.prototype.removeInstrumentsSocket = function (cb) {203 var removeSocket = function (innerCb) {204 logger.debug("Removing any remaining instruments sockets");205 rimraf(this.sock, function (err) {206 if (err) return innerCb(err);207 logger.debug("Cleaned up instruments socket " + this.sock);208 innerCb();209 }.bind(this));210 }.bind(this);211 removeSocket(cb);212};213IOS.prototype.getNumericVersion = function () {214 return parseFloat(this.args.platformVersion);215};216IOS.prototype.startRealDevice = function (cb) {217 async.series([218 this.removeInstrumentsSocket.bind(this),219 this.detectUdid.bind(this),220 this.parseLocalizableStrings.bind(this),221 this.setBundleIdFromApp.bind(this),222 this.createInstruments.bind(this),223 this.startLogCapture.bind(this),224 this.installToRealDevice.bind(this),225 this.startInstruments.bind(this),226 this.onInstrumentsLaunch.bind(this),227 this.configureBootstrap.bind(this),228 this.setBundleId.bind(this),229 this.setInitialOrientation.bind(this),230 this.initAutoWebview.bind(this),231 this.waitForAppLaunched.bind(this),232 ], function (err) {233 cb(err);234 });235};236IOS.prototype.startSimulator = function (cb) {237 async.series([238 this.removeInstrumentsSocket.bind(this),239 this.setXcodeVersion.bind(this),240 this.setiOSSDKVersion.bind(this),241 this.checkSimAvailable.bind(this),242 this.createSimulator.bind(this),243 this.moveBuiltInApp.bind(this),244 this.detectUdid.bind(this),245 this.parseLocalizableStrings.bind(this),246 this.setBundleIdFromApp.bind(this),247 this.createInstruments.bind(this),248 this.setDeviceInfo.bind(this),249 this.checkPreferences.bind(this),250 this.runSimReset.bind(this),251 this.isolateSimDevice.bind(this),252 this.setLocale.bind(this),253 this.setPreferences.bind(this),254 this.startLogCapture.bind(this),255 this.prelaunchSimulator.bind(this),256 this.startInstruments.bind(this),257 this.onInstrumentsLaunch.bind(this),258 this.configureBootstrap.bind(this),259 this.setBundleId.bind(this),260 this.setInitialOrientation.bind(this),261 this.initAutoWebview.bind(this),262 this.waitForAppLaunched.bind(this),263 ], function (err) {264 cb(err);265 });266};267IOS.prototype.start = function (cb, onDie) {268 if (this.instruments !== null) {269 var msg = "Trying to start a session but instruments is still around";270 logger.error(msg);271 return cb(new Error(msg));272 }273 if (typeof onDie === "function") {274 this.onInstrumentsDie = onDie;275 }276 if (this.args.udid) {277 this.startRealDevice(cb);278 } else {279 this.startSimulator(cb);280 }281};282IOS.prototype.createInstruments = function (cb) {283 logger.debug("Creating instruments");284 this.commandProxy = new CommandProxy({ sock: this.sock });285 this.makeInstruments(function (err, instruments) {286 if (err) return cb(err);287 this.instruments = instruments;288 cb();289 }.bind(this));290};291IOS.prototype.startInstruments = function (cb) {292 cb = _.once(cb);293 var treatError = function (err, cb) {294 if (!_.isEmpty(this.logs)) {295 this.logs.syslog.stopCapture();296 this.logs = {};297 }298 this.postCleanup(function () {299 cb(err);300 });301 }.bind(this);302 logger.debug("Starting command proxy.");303 this.commandProxy.start(304 function onFirstConnection(err) {305 // first let instruments know so that it does not restart itself306 this.instruments.launchHandler(err);307 // then we call the callback308 cb(err);309 }.bind(this)310 , function regularCallback(err) {311 if (err) return treatError(err, cb);312 logger.debug("Starting instruments");313 this.instruments.start(314 function (err) {315 if (err) return treatError(err, cb);316 // we don't call cb here, waiting for first connection or error317 }.bind(this)318 , function (code) {319 if (!this.shouldIgnoreInstrumentsExit()) {320 this.onUnexpectedInstrumentsExit(code);321 }322 }.bind(this)323 );324 }.bind(this)325 );326};327IOS.prototype.makeInstruments = function (cb) {328 // at the moment all the logging in uiauto is at debug level329 // TODO: be able to use info in appium-uiauto330 var bootstrap = prepareBootstrap({331 sock: this.sock,332 interKeyDelay: this.args.interKeyDelay,333 justLoopInfinitely: false,334 autoAcceptAlerts: !(!this.args.autoAcceptAlerts || this.args.autoAcceptAlerts === 'false'),335 autoDismissAlerts: !(!this.args.autoDismissAlerts || this.args.autoDismissAlerts === 'false'),336 sendKeyStrategy: this.args.sendKeyStrategy || (this.args.udid ? 'grouped' : 'oneByOne')337 });338 bootstrap.then(function (bootstrapPath) {339 var instruments = new Instruments({340 // on real devices bundleId is always used341 app: (!this.args.udid ? this.args.app : null) || this.args.bundleId342 , udid: this.args.udid343 , processArguments: this.args.processArguments344 , ignoreStartupExit: this.shouldIgnoreInstrumentsExit()345 , bootstrap: bootstrapPath346 , template: this.args.automationTraceTemplatePath347 , instrumentsPath: this.args.instrumentsPath348 , withoutDelay: this.args.withoutDelay349 , platformVersion: this.args.platformVersion350 , webSocket: this.args.webSocket351 , launchTimeout: this.args.launchTimeout352 , flakeyRetries: this.args.backendRetries353 , simulatorSdkAndDevice: this.iOSSDKVersion >= 7.1 ? this.getDeviceString() : null354 , tmpDir: path.resolve(this.args.tmpDir , 'appium-instruments')355 , traceDir: this.args.traceDir356 });357 cb(null, instruments);358 }.bind(this), cb).fail(cb);359};360IOS.prototype.shouldIgnoreInstrumentsExit = function () {361 return false;362};363IOS.prototype.onInstrumentsLaunch = function (cb) {364 logger.debug('Instruments launched. Starting poll loop for new commands.');365 this.instruments.setDebug(true);366 if (this.args.origAppPath) {367 logger.debug("Copying app back to its original place");368 return ncp(this.args.app, this.args.origAppPath, cb);369 }370 cb();371};372IOS.prototype.setBundleId = function (cb) {373 if (this.args.bundleId) {374 // We already have a bundle Id375 cb();376 } else {377 this.proxy('au.bundleId()', function (err, bId) {378 if (err) return cb(err);379 logger.debug('Bundle ID for open app is ' + bId.value);380 this.args.bundleId = bId.value;381 cb();382 }.bind(this));383 }384};385IOS.prototype.setInitialOrientation = function (cb) {386 if (typeof this.args.initialOrientation === "string" &&387 _.contains(["LANDSCAPE", "PORTRAIT"],388 this.args.initialOrientation.toUpperCase())389 ) {390 logger.debug("Setting initial orientation to " + this.args.initialOrientation);391 var command = ["au.setScreenOrientation('",392 this.args.initialOrientation.toUpperCase(), "')"].join('');393 this.proxy(command, function (err, res) {394 if (err || res.status !== status.codes.Success.code) {395 logger.warn("Setting initial orientation did not work!");396 } else {397 this.curOrientation = this.args.initialOrientation;398 }399 cb();400 }.bind(this));401 } else {402 cb();403 }404};405IOS.isSpringBoard = function (uiAppObj) {406// Test for iOS homescreen (SpringBoard). AUT occassionally start the sim, but fails to load407// the app. If that occurs, getSourceForElementFoXML will return a doc object that meets our408// app-check conditions, resulting in a false positive. This function tests the UiApplication409// property's meta data to ensure that the Appium doesn't confuse SpringBoard with the app410// under test.411 return _.propertyOf(uiAppObj['@'])('name') === 'SpringBoard';412};413IOS.prototype.waitForAppLaunched = function (cb) {414 // on iOS8 in particular, we can get a working session before the app415 // is ready to respond to commands; in that case the source will be empty416 // so we just spin until it's not417 var condFn;418 if (this.args.waitForAppScript) {419 // the default getSourceForElementForXML does not fit some use case, so making this customizable.420 // TODO: collect script from customer and propose several options, please comment in issue #4190.421 logger.debug("Using custom script to wait for app start:" + this.args.waitForAppScript);422 condFn = function (cb) {423 this.proxy('try{\n' + this.args.waitForAppScript +424 '\n} catch(err) { $.log("waitForAppScript err: " + error); false; };',425 function (err, res) {426 cb(!!res.value, err);427 });428 }.bind(this);429 } else {430 logger.debug("Waiting for app source to contain elements");431 condFn = function (cb) {432 this.getSourceForElementForXML(null, function (err, res) {433 if (err || !res || res.status !== status.codes.Success.code) {434 return cb(false, err);435 }436 var sourceObj, appEls;437 try {438 sourceObj = JSON.parse(res.value);439 appEls = sourceObj.UIAApplication['>'];440 if (appEls.length > 0 && !IOS.isSpringBoard(sourceObj.UIAApplication)) {441 return cb(true);442 } else {443 return cb(false, new Error("App did not have elements"));444 }445 } catch (e) {446 return cb(false, new Error("Couldn't parse JSON source"));447 }448 return cb(true, err);449 });450 }.bind(this);451 }452 this.waitForCondition(10000, condFn, cb, 500);453};454IOS.prototype.configureBootstrap = function (cb) {455 logger.debug("Setting bootstrap config keys/values");456 var isVerbose = logger.transports.console.level === 'debug';457 var cmd = '';458 cmd += 'target = $.target();\n';459 cmd += 'au = $;\n';460 cmd += '$.isVerbose = ' + isVerbose + ';\n';461 // Not using uiauto grace period because of bug.462 // cmd += '$.target().setTimeout(1);\n';463 this.proxy(cmd, cb);464};465IOS.prototype.onUnexpectedInstrumentsExit = function (code) {466 logger.debug("Instruments exited unexpectedly");467 this.isShuttingDown = true;468 var postShutdown = function () {469 if (typeof this.cbForCurrentCmd === "function") {470 logger.debug("We were in the middle of processing a command when " +471 "instruments died; responding with a generic error");472 var error = new UnknownError("Instruments died while responding to " +473 "command, please check appium logs");474 this.onInstrumentsDie(error, this.cbForCurrentCmd);475 } else {476 this.onInstrumentsDie();477 }478 }.bind(this);479 if (this.commandProxy) {480 this.commandProxy.safeShutdown(function () {481 this.shutdown(code, postShutdown);482 }.bind(this));483 } else {484 this.shutdown(code, postShutdown);485 }486};487IOS.prototype.setXcodeVersion = function (cb) {488 logger.debug("Setting Xcode version");489 xcode.getVersion(function (err, versionNumber) {490 if (err) {491 logger.error("Could not determine Xcode version:" + err.message);492 } else {493 var minorVersion = parseFloat(versionNumber.slice(0, 3));494 var pv = parseFloat(this.args.platformVersion);495 // we deprecate Xcodes < 6.3, except for iOS 8.0 in which case we496 // support Xcode 6.0 as well497 if (minorVersion < 6.3 && (!(minorVersion === 6.0 && pv === 8.0))) {498 logCustomDeprecationWarning('Xcode version', versionNumber,499 'Support for Xcode ' + versionNumber + ' ' +500 'has been deprecated and will be removed ' +501 'in a future version. Please upgrade ' +502 'to version 6.3 or higher (or version ' +503 '6.0.1 for iOS 8.0)');504 }505 }506 this.xcodeVersion = versionNumber;507 logger.debug("Xcode version set to " + versionNumber);508 cb();509 }.bind(this));510};511IOS.prototype.setiOSSDKVersion = function (cb) {512 logger.debug("Setting iOS SDK Version");513 xcode.getMaxIOSSDK(function (err, versionNumber) {514 if (err) {515 logger.error("Could not determine iOS SDK version");516 return cb(err);517 }518 this.iOSSDKVersion = versionNumber;519 logger.debug("iOS SDK Version set to " + this.iOSSDKVersion);520 cb();521 }.bind(this));522};523IOS.prototype.setLocale = function (cb) {524 var msg;525 var setLoc = function (err) {526 logger.debug("Setting locale information");527 if (err) return cb(err);528 var needSimRestart = false;529 this.localeConfig = this.localeConfig || {};530 _(['language', 'locale', 'calendarFormat']).each(function (key) {531 needSimRestart = needSimRestart ||532 (this.args[key] &&533 this.args[key] !== this.localeConfig[key]);534 }, this);535 this.localeConfig = {536 language: this.args.language,537 locale: this.args.locale,538 calendarFormat: this.args.calendarFormat539 };540 var simRoots = this.sim.getDirs();541 if (simRoots.length < 1) {542 msg = "Cannot set locale information because the iOS Simulator directory could not be determined.";543 logger.error(msg);544 return cb(new Error(msg));545 }546 try {547 this.sim.setLocale(this.args.language, this.args.locale, this.args.calendarFormat);548 } catch (e) {549 msg = "Appium was unable to set locale info: " + e;550 logger.error(msg);551 return cb(new Error(msg));552 }553 logger.debug("Locale was set");554 if (needSimRestart) {555 logger.debug("First time setting locale, or locale changed, killing existing Instruments and Sim procs.");556 Instruments.killAllSim();557 Instruments.killAll();558 setTimeout(cb, 250);559 } else {560 cb();561 }562 }.bind(this);563 if ((this.args.language || this.args.locale || this.args.calendarFormat) && this.args.udid === null) {564 if (this.args.fullReset && this.args.platformVersion <= 6.1) {565 msg = "Cannot set locale information because a full-reset was requested. full-reset interferes with the language/locale caps on iOS 6.1 and older";566 logger.error(msg);567 return cb(new Error(msg));568 }569 if (!this.sim.dirsExist()) {570 this.instantLaunchAndQuit(false, setLoc);571 } else {572 setLoc();573 }574 } else if (this.args.udid) {575 logger.debug("Not setting locale because we're using a real device");576 cb();577 } else {578 logger.debug("Not setting locale");579 cb();580 }581};582IOS.prototype.checkPreferences = function (cb) {583 logger.debug("Checking whether we need to set app preferences");584 if (this.args.udid !== null) {585 logger.debug("Not setting iOS and app preferences since we're on a real " +586 "device");587 return cb();588 }589 var settingsCaps = [590 'locationServicesEnabled',591 'locationServicesAuthorized',592 'safariAllowPopups',593 'safariIgnoreFraudWarning',594 'safariOpenLinksInBackground'595 ];596 var safariSettingsCaps = settingsCaps.slice(2, 5);597 this.needToSetPrefs = false;598 this.needToSetSafariPrefs = false;599 _.each(settingsCaps, function (cap) {600 if (_.has(this.capabilities, cap)) {601 this.needToSetPrefs = true;602 if (_.contains(safariSettingsCaps, cap)) {603 this.needToSetSafariPrefs = true;604 }605 }606 }.bind(this));607 this.keepAppToRetainPrefs = this.needToSetPrefs;608 cb();609};610IOS.prototype.setPreferences = function (cb) {611 if (!this.needToSetPrefs) {612 logger.debug("No iOS / app preferences to set");613 return cb();614 } else if (this.args.fullReset) {615 var msg = "Cannot set preferences because a full-reset was requested";616 logger.debug(msg);617 logger.error(msg);618 return cb(new Error(msg));619 }620 var setPrefs = function (err) {621 if (err) return cb(err);622 try {623 this.setLocServicesPrefs();624 } catch (e) {625 logger.error("Error setting location services preferences, prefs will not work");626 logger.error(e);627 logger.error(e.stack);628 }629 try {630 this.setSafariPrefs();631 } catch (e) {632 logger.error("Error setting safari preferences, prefs will not work");633 logger.error(e);634 logger.error(e.stack);635 }636 cb();637 }.bind(this);638 logger.debug("Setting iOS and app preferences");639 if (!this.sim.dirsExist() ||640 !settings.locServicesDirsExist(this.sim) ||641 (this.needToSetSafariPrefs && !this.sim.safariDirsExist())) {642 this.instantLaunchAndQuit(this.needToSetSafariPrefs, setPrefs);643 } else {644 setPrefs();645 }646};647IOS.prototype.instantLaunchAndQuit = function (needSafariDirs, cb) {648 logger.debug("Sim files for the " + this.iOSSDKVersion + " SDK do not yet exist, launching the sim " +649 "to populate the applications and preference dirs");650 var condition = function () {651 var simDirsExist = this.sim.dirsExist();652 var locServicesExist = settings.locServicesDirsExist(this.sim);653 var safariDirsExist = this.args.platformVersion < 7.0 ||654 (this.sim.safariDirsExist() &&655 (this.args.platformVersion < 8.0 ||656 this.sim.userSettingsPlistExists())657 );658 var okToGo = simDirsExist && locServicesExist &&659 (!needSafariDirs || safariDirsExist);660 if (!okToGo) {661 logger.debug("We launched the simulator but the required dirs don't " +662 "yet exist. Waiting some more...");663 }664 return okToGo;665 }.bind(this);666 this.prelaunchSimulator(function (err) {667 if (err) return cb(err);668 this.makeInstruments(function (err, instruments) {669 instruments.launchAndKill(condition, function (err) {670 if (err) return cb(err);671 this.endSimulator(cb);672 }.bind(this));673 }.bind(this));674 }.bind(this));675};676IOS.prototype.setLocServicesPrefs = function () {677 if (typeof this.capabilities.locationServicesEnabled !== "undefined" ||678 this.capabilities.locationServicesAuthorized) {679 var locServ = this.capabilities.locationServicesEnabled;680 locServ = locServ || this.capabilities.locationServicesAuthorized;681 locServ = locServ ? 1 : 0;682 logger.debug("Setting location services to " + locServ);683 settings.updateSettings(this.sim, 'locationServices', {684 LocationServicesEnabled: locServ,685 'LocationServicesEnabledIn7.0': locServ,686 'LocationServicesEnabledIn8.0': locServ687 }688 );689 }690 if (typeof this.capabilities.locationServicesAuthorized !== "undefined") {691 if (!this.args.bundleId) {692 var msg = "Can't set location services for app without bundle ID";693 logger.error(msg);694 throw new Error(msg);695 }696 var locAuth = !!this.capabilities.locationServicesAuthorized;697 if (locAuth) {698 logger.debug("Authorizing location services for app");699 } else {700 logger.debug("De-authorizing location services for app");701 }702 settings.updateLocationSettings(this.sim, this.args.bundleId, locAuth);703 }704};705IOS.prototype.setSafariPrefs = function () {706 var safariSettings = {};707 var val;708 if (_.has(this.capabilities, 'safariAllowPopups')) {709 val = !!this.capabilities.safariAllowPopups;710 logger.debug("Setting javascript window opening to " + val);711 safariSettings.WebKitJavaScriptCanOpenWindowsAutomatically = val;712 safariSettings.JavaScriptCanOpenWindowsAutomatically = val;713 }714 if (_.has(this.capabilities, 'safariIgnoreFraudWarning')) {715 val = !this.capabilities.safariIgnoreFraudWarning;716 logger.debug("Setting fraudulent website warning to " + val);717 safariSettings.WarnAboutFraudulentWebsites = val;718 }719 if (_.has(this.capabilities, 'safariOpenLinksInBackground')) {720 val = this.capabilities.safariOpenLinksInBackground ? 1 : 0;721 logger.debug("Setting opening links in background to " + !!val);722 safariSettings.OpenLinksInBackground = val;723 }724 if (_.size(safariSettings) > 0) {725 settings.updateSafariSettings(this.sim, safariSettings);726 }727};728IOS.prototype.detectUdid = function (cb) {729 var msg;730 logger.debug("Auto-detecting iOS udid...");731 if (this.args.udid !== null && this.args.udid === "auto") {732 which('idevice_id', function (notFound, cmdPath) {733 var udidetectPath;734 if (notFound) {735 udidetectPath = require.resolve('udidetect');736 } else {737 udidetectPath = cmdPath + " -l";738 }739 exec(udidetectPath, { maxBuffer: 524288, timeout: 3000 }, function (err, stdout) {740 if (err) {741 msg = "Error detecting udid: " + err.message;742 logger.error(msg);743 cb(err);744 }745 if (stdout && stdout.length > 2) {746 this.args.udid = stdout.split("\n")[0];747 logger.debug("Detected udid as " + this.args.udid);748 cb();749 } else {750 msg = "Could not detect udid.";751 logger.error(msg);752 cb(new Error(msg));753 }754 }.bind(this));755 }.bind(this));756 } else {757 logger.debug("Not auto-detecting udid, running on sim");758 cb();759 }760};761IOS.prototype.setBundleIdFromApp = function (cb) {762 // This method will try to extract the bundleId from the app763 if (this.args.bundleId) {764 // We aleady have a bundle Id765 cb();766 } else {767 this.getBundleIdFromApp(function (err, bundleId) {768 if (err) {769 logger.error("Could not set the bundleId from app.");770 return cb(err);771 }772 this.args.bundleId = bundleId;773 cb();774 }.bind(this));775 }776};777IOS.prototype.installToRealDevice = function (cb) {778 // if user has passed in desiredCaps.autoLaunch = false779 // meaning they will manage app install / launching780 if (this.args.autoLaunch === false) {781 cb();782 } else {783 if (this.args.udid) {784 try {785 this.realDevice = this.getIDeviceObj();786 } catch (e) {787 return cb(e);788 }789 this.isAppInstalled(this.args.bundleId, function (err, installed) {790 if (err || !installed) {791 logger.debug("App is not installed. Will try to install the app.");792 } else {793 logger.debug("App is installed.");794 if (this.args.fullReset) {795 logger.debug("fullReset requested. Forcing app install.");796 } else {797 logger.debug("fullReset not requested. No need to install.");798 return cb();799 }800 }801 if (this.args.ipa && this.args.bundleId) {802 this.installIpa(cb);803 } else if (this.args.ipa) {804 var msg = "You specified a UDID and ipa but did not include the bundle " +805 "id";806 logger.error(msg);807 cb(new Error(msg));808 } else if (this.args.app) {809 this.installApp(this.args.app, cb);810 } else {811 logger.debug("Real device specified but no ipa or app path, assuming bundle ID is " +812 "on device");813 cb();814 }815 }.bind(this));816 } else {817 logger.debug("No device id or app, not installing to real device.");818 cb();819 }820 }821};822IOS.prototype.getIDeviceObj = function () {823 var idiPath = path.resolve(__dirname, "../../../build/",824 "libimobiledevice-macosx/ideviceinstaller");825 logger.debug("Creating iDevice object with udid " + this.args.udid);826 try {827 return iDevice(this.args.udid);828 } catch (e1) {829 logger.debug("Couldn't find ideviceinstaller, trying built-in at " +830 idiPath);831 try {832 return iDevice(this.args.udid, {cmd: idiPath});833 } catch (e2) {834 var msg = "Could not initialize ideviceinstaller; make sure it is " +835 "installed and works on your system";836 logger.error(msg);837 throw new Error(msg);838 }839 }840};841IOS.prototype.installIpa = function (cb) {842 logger.debug("Installing ipa found at " + this.args.ipa);843 if (!this.realDevice) {844 this.realDevice = this.getIDeviceObj();845 }846 var d = this.realDevice;847 async.waterfall([848 function (cb) { d.isInstalled(this.args.bundleId, cb); }.bind(this),849 function (installed, cb) {850 if (installed) {851 logger.debug("Bundle found on device, removing before reinstalling.");852 d.remove(this.args.bundleId, cb);853 } else {854 logger.debug("Nothing found on device, going ahead and installing.");855 cb();856 }857 }.bind(this),858 function (cb) { d.installAndWait(this.args.ipa, this.args.bundleId, cb); }.bind(this)859 ], cb);860};861IOS.getDeviceStringFromOpts = function (opts) {862 logger.debug("Getting device string from opts: " + JSON.stringify({863 forceIphone: opts.forceIphone,864 forceIpad: opts.forceIpad,865 xcodeVersion: opts.xcodeVersion,866 iOSSDKVersion: opts.iOSSDKVersion,867 deviceName: opts.deviceName,868 platformVersion: opts.platformVersion869 }));870 var isiPhone = opts.forceIphone || opts.forceIpad === null || (opts.forceIpad !== null && !opts.forceIpad);871 var isTall = isiPhone;872 var isRetina = opts.xcodeVersion[0] !== '4';873 var is64bit = false;874 var deviceName = opts.deviceName;875 var fixDevice = true;876 if (deviceName && deviceName[0] === '=') {877 return deviceName.substring(1);878 }879 logger.debug("fixDevice is " + (fixDevice ? "on" : "off"));880 if (deviceName) {881 var device = deviceName.toLowerCase();882 if (device.indexOf("iphone") !== -1) {883 isiPhone = true;884 } else if (device.indexOf("ipad") !== -1) {885 isiPhone = false;886 }887 if (deviceName !== opts.platformName) {888 isTall = isiPhone && (device.indexOf("4-inch") !== -1);889 isRetina = (device.indexOf("retina") !== -1);890 is64bit = (device.indexOf("64-bit") !== -1);891 }892 }893 var iosDeviceString = isiPhone ? "iPhone" : "iPad";894 if (opts.xcodeVersion[0] === '4') {895 if (isiPhone && isRetina) {896 iosDeviceString += isTall ? " (Retina 4-inch)" : " (Retina 3.5-inch)";897 } else {898 iosDeviceString += isRetina ? " (Retina)" : "";899 }900 } else if (opts.xcodeVersion[0] === '5') {901 iosDeviceString += isRetina ? " Retina" : "";902 if (isiPhone) {903 if (isRetina && isTall) {904 iosDeviceString += is64bit ? " (4-inch 64-bit)" : " (4-inch)";905 } else if (deviceName.toLowerCase().indexOf("3.5") !== -1) {906 iosDeviceString += " (3.5-inch)";907 }908 } else {909 iosDeviceString += is64bit ? " (64-bit)" : "";910 }911 } else if (opts.xcodeVersion[0] === '6') {912 iosDeviceString = opts.deviceName ||913 (isiPhone ? "iPhone Simulator" : "iPad Simulator");914 }915 var reqVersion = opts.platformVersion || opts.iOSSDKVersion;916 if (opts.iOSSDKVersion >= 8) {917 iosDeviceString += " (" + reqVersion + " Simulator)";918 } else if (opts.iOSSDKVersion >= 7.1) {919 iosDeviceString += " - Simulator - iOS " + reqVersion;920 }921 if (fixDevice) {922 // Some device config are broken in 5.1923 var CONFIG_FIX = {924 'iPhone - Simulator - iOS 7.1': 'iPhone Retina (4-inch 64-bit) - ' +925 'Simulator - iOS 7.1',926 'iPad - Simulator - iOS 7.1': 'iPad Retina (64-bit) - Simulator - ' +927 'iOS 7.1',928 'iPad Simulator (8.0 Simulator)': 'iPad 2 (8.0 Simulator)',929 'iPad Simulator (8.1 Simulator)': 'iPad 2 (8.1 Simulator)',930 'iPad Simulator (8.2 Simulator)': 'iPad 2 (8.2 Simulator)',931 'iPad Simulator (8.3 Simulator)': 'iPad 2 (8.3 Simulator)',932 'iPad Simulator (8.4 Simulator)': 'iPad 2 (8.4 Simulator)',933 'iPad Simulator (7.1 Simulator)': 'iPad 2 (7.1 Simulator)',934 'iPhone Simulator (8.4 Simulator)': 'iPhone 6 (8.4 Simulator)',935 'iPhone Simulator (8.3 Simulator)': 'iPhone 6 (8.3 Simulator)',936 'iPhone Simulator (8.2 Simulator)': 'iPhone 6 (8.2 Simulator)',937 'iPhone Simulator (8.1 Simulator)': 'iPhone 6 (8.1 Simulator)',938 'iPhone Simulator (8.0 Simulator)': 'iPhone 6 (8.0 Simulator)',939 'iPhone Simulator (7.1 Simulator)': 'iPhone 5s (7.1 Simulator)'940 };941 if (CONFIG_FIX[iosDeviceString]) {942 var oldDeviceString = iosDeviceString;943 iosDeviceString = CONFIG_FIX[iosDeviceString];944 logger.debug("Fixing device. Changed from: \"" + oldDeviceString +945 "\" to: \"" + iosDeviceString + "\"");946 }947 }948 logger.debug("Final device string is: '" + iosDeviceString + "'");949 return iosDeviceString;950};951IOS.prototype.getDeviceString = function () {952 var opts = _.clone(this.args);953 _.extend(opts, {954 xcodeVersion: this.xcodeVersion,955 iOSSDKVersion: this.iOSSDKVersion956 });957 return IOS.getDeviceStringFromOpts(opts);958};959IOS.prototype.setDeviceTypeInInfoPlist = function (cb) {960 var plist = path.resolve(this.args.app, "Info.plist");961 var dString = this.getDeviceString();962 var isiPhone = dString.toLowerCase().indexOf("ipad") === -1;963 var deviceTypeCode = isiPhone ? 1 : 2;964 parsePlistFile(plist, function (err, obj) {965 if (err) {966 logger.error("Could not set the device type in Info.plist");967 return cb(err, null);968 } else {969 var newPlist;970 obj.UIDeviceFamily = [deviceTypeCode];971 if (binaryPlist) {972 newPlist = bplistCreate(obj);973 } else {974 newPlist = xmlplist.build(obj);975 }976 fs.writeFile(plist, newPlist, function (err) {977 if (err) {978 logger.error("Could not save new Info.plist");979 cb(err);980 } else {981 logger.debug("Wrote new app Info.plist with device type");982 cb();983 }984 }.bind(this));985 }986 }.bind(this));987};988IOS.prototype.getBundleIdFromApp = function (cb) {989 logger.debug("Getting bundle ID from app");990 var plist = path.resolve(this.args.app, "Info.plist");991 parsePlistFile(plist, function (err, obj) {992 if (err) {993 logger.error("Could not get the bundleId from app.");994 cb(err, null);995 } else {996 cb(null, obj.CFBundleIdentifier);997 }998 }.bind(this));999};1000IOS.getSimForDeviceString = function (dString, availDevices) {1001 var matchedDevice = null;1002 var matchedUdid = null;1003 _.each(availDevices, function (device) {1004 if (device.indexOf(dString) !== -1) {1005 matchedDevice = device;1006 try {1007 matchedUdid = /.+\[([^\]]+)\]/.exec(device)[1];1008 } catch (e) {1009 matchedUdid = null;1010 }1011 }1012 });1013 return [matchedDevice, matchedUdid];1014};1015IOS.prototype.checkSimAvailable = function (cb) {1016 if (this.args.udid) {1017 logger.debug("Not checking whether simulator is available since we're on " +1018 "a real device");1019 return cb();1020 }1021 if (this.iOSSDKVersion < 7.1) {1022 logger.debug("Instruments v < 7.1, not checking device string support");1023 return cb();1024 }1025 logger.debug("Checking whether instruments supports our device string");1026 Instruments.getAvailableDevicesWithRetry(3, function (err, availDevices) {1027 if (err) return cb(err);1028 var noDevicesError = function () {1029 var msg = "Could not find a device to launch. You requested '" +1030 dString + "', but the available devices were: " +1031 JSON.stringify(availDevices);1032 logger.error(msg);1033 cb(new Error(msg));1034 };1035 var dString = this.getDeviceString();1036 if (this.iOSSDKVersion >= 8) {1037 var sim = IOS.getSimForDeviceString(dString, availDevices);1038 if (sim[0] === null || sim[1] === null) {1039 return noDevicesError();1040 }1041 this.iOSSimUdid = sim[1];1042 logger.debug("iOS sim UDID is " + this.iOSSimUdid);1043 return cb();1044 } else if (!_.contains(availDevices, dString)) {1045 return noDevicesError();1046 }1047 cb();1048 }.bind(this));1049};1050IOS.prototype.setDeviceInfo = function (cb) {1051 this.shouldPrelaunchSimulator = false;1052 if (this.args.udid) {1053 logger.debug("Not setting device type since we're on a real device");1054 return cb();1055 }1056 if (!this.args.app && this.args.bundleId) {1057 logger.debug("Not setting device type since we're using bundle ID and " +1058 "assuming app is already installed");1059 return cb();1060 }1061 if (!this.args.deviceName &&1062 this.args.forceIphone === null &&1063 this.args.forceIpad === null) {1064 logger.debug("No device specified, current device in the iOS " +1065 "simulator will be used.");1066 return cb();1067 }1068 if (this.args.defaultDevice || this.iOSSDKVersion >= 7.1) {1069 if (this.iOSSDKVersion >= 7.1) {1070 logger.debug("We're on iOS7.1+ so forcing defaultDevice on");1071 } else {1072 logger.debug("User specified default device, letting instruments launch it");1073 }1074 } else {1075 this.shouldPrelaunchSimulator = true;1076 }1077 this.setDeviceTypeInInfoPlist(cb);1078};1079IOS.prototype.createSimulator = function (cb) {1080 this.sim = new Simulator({1081 platformVer: this.args.platformVersion,1082 sdkVer: this.iOSSDKVersion,1083 udid: this.iOSSimUdid1084 });1085 cb();1086};1087IOS.prototype.moveBuiltInApp = function (cb) {1088 if (this.appString().toLowerCase() === "settings") {1089 logger.debug("Trying to use settings app, version " +1090 this.args.platformVersion);1091 this.sim.preparePreferencesApp(this.args.tmpDir, function (err, attemptedApp, origApp) {1092 if (err) {1093 logger.error("Could not prepare settings app: " + err);1094 return cb(err);1095 }1096 logger.debug("Using settings app at " + attemptedApp);1097 this.args.app = attemptedApp;1098 this.args.origAppPath = origApp;1099 cb();1100 }.bind(this));1101 } else {1102 cb();1103 }1104};1105IOS.prototype.prelaunchSimulator = function (cb) {1106 var msg;1107 if (!this.shouldPrelaunchSimulator) {1108 logger.debug("Not pre-launching simulator");1109 return cb();1110 }1111 xcode.getPath(function (err, xcodePath) {1112 if (err) {1113 return cb(new Error('Could not find xcode folder. Needed to start simulator. ' + err.message));1114 }1115 logger.debug("Pre-launching simulator");1116 var iosSimPath = path.resolve(xcodePath,1117 "Platforms/iPhoneSimulator.platform/Developer/Applications" +1118 "/iPhone Simulator.app/Contents/MacOS/iPhone Simulator");1119 if (!fs.existsSync(iosSimPath)) {1120 msg = "Could not find ios simulator binary at " + iosSimPath;1121 logger.error(msg);1122 return cb(new Error(msg));1123 }1124 this.endSimulator(function (err) {1125 if (err) return cb(err);1126 logger.debug("Launching device: " + this.getDeviceString());1127 var iosSimArgs = ["-SimulateDevice", this.getDeviceString()];1128 this.iosSimProcess = spawn(iosSimPath, iosSimArgs);1129 var waitForSimulatorLogs = function (countdown) {1130 if (countdown <= 0 ||1131 (this.logs.syslog && (this.logs.syslog.getAllLogs().length > 0 ||1132 (this.logs.crashlog && this.logs.crashlog.getAllLogs().length > 0)))) {1133 logger.debug(countdown > 0 ? "Simulator is now ready." :1134 "Waited 10 seconds for simulator to start.");1135 cb();1136 } else {1137 setTimeout(function () {1138 waitForSimulatorLogs(countdown - 1);1139 }, 1000);1140 }1141 }.bind(this);1142 waitForSimulatorLogs(10);1143 }.bind(this));1144 }.bind(this));1145};1146IOS.prototype.parseLocalizableStrings = function (/* language, stringFile, cb */) {1147 var args = new Args(arguments);1148 var cb = args.callback;1149 if (this.args.app === null) {1150 logger.debug("Localizable.strings is not currently supported when using real devices.");1151 return cb();1152 }1153 var language = args.all[0] || this.args.language1154 , stringFile = args.all[1] || "Localizable.strings"1155 , strings = null;1156 if (language) {1157 strings = path.resolve(this.args.app, language + ".lproj", stringFile);1158 }1159 if (!fs.existsSync(strings)) {1160 if (language) {1161 logger.debug("No strings file '" + stringFile + "' for language '" + language + "', getting default strings");1162 }1163 strings = path.resolve(this.args.app, stringFile);1164 }1165 if (!fs.existsSync(strings)) {1166 strings = path.resolve(this.args.app, this.args.localizableStringsDir, stringFile);1167 }1168 parsePlistFile(strings, function (err, obj) {1169 if (err) {1170 logger.warn("Could not parse app " + stringFile +" assuming it " +1171 "doesn't exist");1172 } else {1173 logger.debug("Parsed app " + stringFile);1174 this.localizableStrings = obj;1175 }1176 cb();1177 }.bind(this));1178};1179IOS.prototype.deleteSim = function (cb) {1180 this.sim.deleteSim(cb);1181};1182IOS.prototype.clearAppData = function (cb) {1183 if (!this.keepAppToRetainPrefs && this.args.app && this.args.bundleId) {1184 this.sim.cleanCustomApp(path.basename(this.args.app), this.args.bundleId);1185 }1186 cb();1187};1188IOS.prototype.cleanupSimState = function (cb) {1189 if (this.realDevice && this.args.bundleId && this.args.fullReset) {1190 logger.debug("fullReset requested. Will try to uninstall the app.");1191 var bundleId = this.args.bundleId;1192 this.realDevice.remove(bundleId, function (err) {1193 if (err) {1194 this.removeApp(bundleId, function (err) {1195 if (err) {1196 logger.error("Could not remove " + bundleId + " from device");1197 cb(err);1198 } else {1199 logger.debug("Removed " + bundleId);1200 cb();1201 }1202 }.bind(this));1203 } else {1204 logger.debug("Removed " + bundleId);1205 cb();1206 }1207 }.bind(this));1208 } else if (!this.args.udid) {1209 this.sim.cleanSim(this.args.keepKeyChains, this.args.tmpDir, function (err) {1210 if (err) {1211 logger.error("Could not reset simulator. Leaving as is. Error: " + err.message);1212 }1213 this.clearAppData(cb);1214 }.bind(this));1215 } else {1216 logger.debug("On a real device; cannot clean device state");1217 cb();1218 }1219};1220IOS.prototype.runSimReset = function (cb) {1221 if (this.args.reset || this.args.fullReset) {1222 logger.debug("Running ios sim reset flow");1223 // The simulator process must be ended before we delete applications.1224 async.series([1225 this.endSimulator.bind(this),1226 function (cb) {1227 if (this.args.reset) {1228 this.cleanupSimState(cb);1229 } else {1230 cb();1231 }1232 }.bind(this),1233 function (cb) {1234 if (this.args.fullReset && !this.args.udid) {1235 this.deleteSim(cb);1236 } else {1237 cb();1238 }1239 }.bind(this)1240 ], cb);1241 } else {1242 logger.debug("Reset not set, not ending sim or cleaning up app state");1243 cb();1244 }1245};1246IOS.prototype.isolateSimDevice = function (cb) {1247 if (!this.args.udid && this.args.isolateSimDevice &&1248 this.iOSSDKVersion >= 8) {1249 this.sim.deleteOtherSims(cb);1250 } else {1251 cb();1252 }1253};1254IOS.prototype.postCleanup = function (cb) {1255 this.curCoords = null;1256 this.curOrientation = null;1257 if (!_.isEmpty(this.logs)) {1258 this.logs.syslog.stopCapture();1259 this.logs = {};1260 }1261 if (this.remote) {1262 this.stopRemote();1263 }1264 this.runSimReset(function () {1265 // ignore any errors during reset and continue shutting down1266 this.isShuttingDown = false;1267 cb();1268 }.bind(this));1269};1270IOS.prototype.endSimulator = function (cb) {1271 logger.debug("Killing the simulator process");1272 if (this.iosSimProcess) {1273 this.iosSimProcess.kill("SIGHUP");1274 this.iosSimProcess = null;1275 } else {1276 Instruments.killAllSim();1277 }1278 this.endSimulatorDaemons(cb);1279};1280IOS.prototype.endSimulatorDaemons = function (cb) {1281 logger.debug("Killing any other simulator daemons");1282 var stopCmd = 'launchctl list | grep com.apple.iphonesimulator | cut -f 3 | xargs -n 1 launchctl stop';1283 exec(stopCmd, { maxBuffer: 524288 }, function () {1284 var removeCmd = 'launchctl list | grep com.apple.iphonesimulator | cut -f 3 | xargs -n 1 launchctl remove';1285 exec(removeCmd, { maxBuffer: 524288 }, function () {1286 cb();1287 });1288 });1289};1290IOS.prototype.stop = function (cb) {1291 logger.debug("Stopping ios");1292 if (this.instruments === null) {1293 logger.debug("Trying to stop instruments but it already exited");1294 this.postCleanup(cb);1295 } else {1296 this.commandProxy.shutdown(function (err) {1297 if (err) logger.warn("Got warning when trying to close command proxy:", err);1298 this.instruments.shutdown(function (code) {1299 this.shutdown(code, cb);1300 }.bind(this));1301 }.bind(this));1302 }1303};1304IOS.prototype.shutdown = function (code, cb) {1305 this.commandProxy = null;1306 this.instruments = null;1307 this.postCleanup(cb);1308};1309IOS.prototype.resetTimeout = deviceCommon.resetTimeout;1310IOS.prototype.waitForCondition = deviceCommon.waitForCondition;1311IOS.prototype.implicitWaitForCondition = deviceCommon.implicitWaitForCondition;1312IOS.prototype.proxy = deviceCommon.proxy;1313IOS.prototype.proxyWithMinTime = deviceCommon.proxyWithMinTime;1314IOS.prototype.respond = deviceCommon.respond;1315IOS.prototype.getSettings = deviceCommon.getSettings;1316IOS.prototype.updateSettings = deviceCommon.updateSettings;1317IOS.prototype.initQueue = function () {1318 this.queue = async.queue(function (command, cb) {1319 if (!this.commandProxy) return cb();1320 async.series([1321 function (cb) {1322 async.whilst(1323 function () { return this.selectingNewPage && this.curContext; }.bind(this),1324 function (cb) {1325 logger.debug("We're in the middle of selecting a new page, " +1326 "waiting to run next command until done");1327 setTimeout(cb, 100);1328 },1329 cb1330 );1331 }.bind(this),1332 function (cb) {1333 var matched = false;1334 var matches = ["au.alertIsPresent", "au.getAlertText", "au.acceptAlert",1335 "au.dismissAlert", "au.setAlertText",1336 "au.waitForAlertToClose"];1337 _.each(matches, function (match) {1338 if (command.indexOf(match) === 0) {1339 matched = true;1340 }1341 });1342 async.whilst(1343 function () { return !matched && this.curContext && this.processingRemoteCmd; }.bind(this),1344 function (cb) {1345 logger.debug("We're in the middle of processing a remote debugger " +1346 "command, waiting to run next command until done");1347 setTimeout(cb, 100);1348 },1349 cb1350 );1351 }.bind(this)1352 ], function (err) {1353 if (err) return cb(err);1354 this.cbForCurrentCmd = cb;1355 if (this.commandProxy) {1356 this.commandProxy.sendCommand(command, function (response) {1357 this.cbForCurrentCmd = null;1358 if (typeof cb === 'function') {1359 this.respond(response, cb);1360 }1361 }.bind(this));1362 }1363 }.bind(this));1364 }.bind(this), 1);1365};1366IOS.prototype.push = function (elem) {1367 this.queue.push(elem[0], elem[1]);1368};1369IOS.prototype.isAppInstalled = function (bundleId, cb) {1370 if (this.args.udid) {1371 this.realDevice.isInstalled(bundleId, cb);1372 } else {1373 cb(new Error("You can not call isInstalled for the iOS simulator!"));1374 }1375};1376IOS.prototype.removeApp = function (bundleId, cb) {1377 if (this.args.udid) {1378 this.realDevice.remove(bundleId, cb);1379 } else {1380 this.sim.remove(bundleId, cb);1381 }1382};1383IOS.prototype.installApp = function (unzippedAppPath, cb) {1384 if (this.args.udid) {1385 this.realDevice.install(unzippedAppPath, cb);1386 } else {1387 this.sim.install(unzippedAppPath, cb);1388 }1389};1390IOS.prototype.startApp = function (args, cb) {1391 if (this.args.udid) {1392 cb(new Error("You can not call startApp for a real device!"));1393 } else {1394 this.sim.launch(args.appPackage, cb);1395 }1396};1397IOS.prototype.unpackApp = function (req, cb) {1398 deviceCommon.unpackApp(req, '.app', cb);1399};1400IOS.prototype.startLogCapture = function (cb) {1401 if (!_.isEmpty(this.logs)) {1402 cb(new Error("Trying to start iOS log capture but it's already started!"));1403 return;1404 }1405 this.logs.crashlog = new iOSCrashLog();1406 this.logs.syslog = new iOSLog({1407 udid: this.args.udid1408 , simUdid: this.iOSSimUdid1409 , showLogs: this.args.showSimulatorLog || this.args.showIOSLog1410 });1411 this.logs.syslog.startCapture(function (err) {1412 if (err) {1413 logger.warn("Could not capture logs from device. Continuing without capturing logs.");1414 return cb();1415 }1416 this.logs.crashlog.startCapture(cb);1417 }.bind(this));1418};1419IOS.prototype.initAutoWebview = function (cb) {1420 if (this.args.autoWebview) {1421 logger.debug('Setting auto webview');1422 this.navToInitialWebview(cb);1423 } else {1424 cb();1425 }1426};1427IOS.prototype.getContextsAndViews = function (cb) {1428 this.listWebFrames(function (err, webviews) {1429 if (err) return cb(err);1430 var ctxs = [{id: this.NATIVE_WIN}];1431 this.contexts = [this.NATIVE_WIN];1432 _.each(webviews, function (view) {1433 ctxs.push({id: this.WEBVIEW_BASE + view.id, view: view});1434 this.contexts.push(view.id.toString());1435 }.bind(this));1436 cb(null, ctxs);1437 }.bind(this));1438};1439IOS.prototype.getLatestWebviewContextForTitle = function (titleRegex, cb) {1440 this.getContextsAndViews(function (err, contexts) {1441 if (err) return cb(err);1442 var matchingCtx;1443 _(contexts).each(function (ctx) {1444 if (ctx.view && (ctx.view.title || "").match(titleRegex)) {1445 if (ctx.view.url === "about:blank") {1446 // in the case of Xcode < 5 (i.e., iOS SDK Version less than 7)1447 // and in the case of iOS 7.1 in a webview (not in Safari)1448 // we can have the url be `about:blank`1449 if (parseFloat(this.iOSSDKVersion) < 7 ||1450 (this.args.platformVersion === '7.1' && this.args.app && this.args.app.toLowerCase() !== 'safari')) {1451 matchingCtx = ctx;1452 }1453 } else {1454 matchingCtx = ctx;1455 }1456 }1457 }.bind(this));1458 cb(null, matchingCtx ? matchingCtx.id : undefined);1459 }.bind(this));1460};1461// Right now we don't necessarily wait for webview1462// and frame to load, which leads to race conditions and flakiness1463// , let see if we can transition to something better1464IOS.prototype.useNewSafari = function () {1465 return parseFloat(this.iOSSDKVersion) >= 8.1 &&1466 parseFloat(this.args.platformVersion) >= 8.1 &&1467 !this.args.udid &&1468 this.capabilities.safari;1469};1470IOS.prototype.navToInitialWebview = function (cb) {1471 var timeout = 0;1472 if (this.args.udid) {1473 timeout = 3000;1474 logger.debug('Waiting for ' + timeout + ' ms before navigating to view.');1475 }1476 setTimeout(function () {1477 if (this.useNewSafari()) {1478 return this.typeAndNavToUrl(cb);1479 } else if (parseInt(this.iOSSDKVersion, 10) >= 7 && !this.args.udid && this.capabilities.safari) {1480 this.navToViewThroughFavorites(cb);1481 } else {1482 this.navToViewWithTitle(/.*/, cb);1483 }1484 }.bind(this), timeout);1485};1486IOS.prototype.typeAndNavToUrl = function (cb) {1487 var initialUrl = this.args.safariInitialUrl || 'http://127.0.0.1:' + this.args.port + '/welcome';1488 var oldImpWait = this.implicitWaitMs;1489 this.implicitWaitMs = 7000;1490 function noArgsCb(cb) { return function (err) { cb(err); }; }1491 async.waterfall([1492 this.findElement.bind(this, 'name', 'URL'),1493 function (res, cb) {1494 this.implicitWaitMs = oldImpWait;1495 this.nativeTap(res.value.ELEMENT, noArgsCb(cb));1496 }.bind(this),1497 this.findElements.bind(this, 'name', 'Address'),1498 function (res, cb) {1499 var addressEl = res.value[res.value.length -1].ELEMENT;1500 this.setValueImmediate(addressEl, initialUrl, noArgsCb(cb));1501 }.bind(this),1502 this.findElement.bind(this, 'name', 'go'),1503 function (res, cb) {1504 this.nativeTap(res.value.ELEMENT, noArgsCb(cb));1505 }.bind(this)1506 ], function () {1507 this.navToViewWithTitle(/.*/i, function (err) {1508 if (err) return cb(err);1509 // Waits for page to finish loading.1510 this.remote.pageUnload(cb);1511 }.bind(this));1512 }.bind(this));1513};1514IOS.prototype.navToViewThroughFavorites = function (cb) {1515 logger.debug("We're on iOS7+ simulator: clicking apple button to get into " +1516 "a webview");1517 var oldImpWait = this.implicitWaitMs;1518 this.implicitWaitMs = 7000; // wait 7s for apple button to exist1519 this.findElement('xpath', '//UIAScrollView[1]/UIAButton[1]', function (err, res) {1520 this.implicitWaitMs = oldImpWait;1521 if (err || res.status !== status.codes.Success.code) {1522 var msg = "Could not find button to click to get into webview. " +1523 "Proceeding on the assumption we have a working one.";1524 logger.error(msg);1525 return this.navToViewWithTitle(/.*/i, cb);1526 }1527 this.nativeTap(res.value.ELEMENT, function (err, res) {1528 if (err || res.status !== status.codes.Success.code) {1529 var msg = "Could not click button to get into webview. " +1530 "Proceeding on the assumption we have a working one.";1531 logger.error(msg);1532 }1533 this.navToViewWithTitle(/apple/i, cb);1534 }.bind(this));1535 }.bind(this));1536};1537IOS.prototype.navToViewWithTitle = function (titleRegex, cb) {1538 logger.debug("Navigating to most recently opened webview");1539 var start = Date.now();1540 var spinTime = 500;1541 var spinHandles = function () {1542 this.getLatestWebviewContextForTitle(titleRegex, function (err, res) {1543 if (err) {1544 cb(new Error("Could not navigate to webview! Err: " + err));1545 } else if (!res) {1546 if ((Date.now() - start) < 90000) {1547 logger.warn("Could not find any webviews yet, refreshing/retrying");1548 if (this.args.udid || !this.capabilities.safari) {1549 return setTimeout(spinHandles, spinTime);1550 }1551 this.findUIElementOrElements('accessibility id', 'ReloadButton',1552 '', false, function (err, res) {1553 if (err || !res || !res.value || !res.value.ELEMENT) {1554 logger.warn("Could not find reload button, continuing");1555 setTimeout(spinHandles, spinTime);1556 } else {1557 this.nativeTap(res.value.ELEMENT, function (err, res) {1558 if (err || !res) {1559 logger.warn("Could not click reload button, continuing");1560 }1561 setTimeout(spinHandles, spinTime);1562 }.bind(this));1563 }1564 }.bind(this));1565 } else {1566 cb(new Error("Could not navigate to webview; there aren't any!"));1567 }1568 } else {1569 var latestWindow = res;1570 logger.debug("Picking webview " + latestWindow);1571 this.setContext(latestWindow, function (err) {1572 if (err) return cb(err);1573 this.remote.cancelPageLoad();1574 cb();1575 }.bind(this), true);1576 }1577 }.bind(this));1578 }.bind(this);1579 spinHandles();1580};1581_.extend(IOS.prototype, iOSHybrid);1582_.extend(IOS.prototype, iOSController);...
driver.js
Source:driver.js
...608 logger.info("'skipLogCapture' is set. Skipping the collection of system logs and crash reports.");609 return;610 }611 if (this.isRealDevice()) {612 await this.startLogCapture();613 } else {614 await this.startLogCapture(this.sim);615 }616 }617 get realDevice () {618 this._realDevice = this._realDevice || this.getIDeviceObj();619 return this._realDevice;620 }621 set realDevice (rd) {622 this._realDevice = rd;623 }624}625for (let [cmd, fn] of _.toPairs(commands)) {626 IosDriver.prototype[cmd] = fn;627}628export { IosDriver, defaultServerCaps };...
Using AI Code Generation
1commands.startLogCapture = async function (args = {}) {2 const { logType } = args;3 if (!logType) {4 throw new Error('You need to provide log type');5 }6 if (!_.includes(SUPPORTED_LOG_TYPES, logType)) {7 throw new Error(`Unsupported log type. Only ${SUPPORTED_LOG_TYPES.join(', ')} ` +8 `are supported. '${logType}' was given instead`);9 }10 this.log.debug(`Starting '${logType}' log capture`);11 this.logs[logType] = await this.createLogSubProcess(logType);12};13commands.stopLogCapture = async function (args = {}) {14 const { logType } = args;15 if (!logType) {16 throw new Error('You need to provide log type');17 }18 if (!_.includes(SUPPORTED_LOG_TYPES, logType)) {19 throw new Error(`Unsupported log type. Only ${SUPPORTED_LOG_TYPES.join(', ')} ` +20 `are supported. '${logType}' was given instead`);21 }22 if (this.logs[logType]) {23 this.log.debug(`Stopping '${logType}' log capture`);24 await this.logs[logType].stop();25 delete this.logs[logType];26 }27};28commands.createLogSubProcess = async function (logType) {29 const log = new SubProcess('xcrun', ['simctl', 'spawn', this.opts.device.udid, 'log', 'stream', '--level=debug', '--style=json']);30 log.on('output', (stdout, stderr) => {31 for (const line of (stdout || stderr || '').split(EOL)) {32 if (!line.length) {33 continue;34 }35 let logEntry = null;36 try {37 logEntry = JSON.parse(line);38 } catch (e) {39 log.warn(`Could not parse the line as a JSON object: '${line}'. ` +40 `Original error: ${e.message}`);41 continue;42 }43 if (logEntry) {44 this.logEvent(logType, logEntry);45 }
Using AI Code Generation
1const { startLogCapture } = require('appium-xcuitest-driver/lib/commands/log');2startLogCapture.call(this);3async function startLogCapture () {4 const logs = await this.proxyCommand('/wda/log/start', 'POST');5 console.log(logs);6}7[debug] [W3C] at JWProxy.command (/usr/local/lib/node_modules/appium/node_modules/appium-base-driver/lib/jsonwp-proxy/proxy.js:227:13)8[debug] [W3C] at XCUITestDriver.startLogCapture (/Users/username/Documents/Workspace/appium-xcuitest-driver/lib/commands/log.js:17:22)9[debug] [W3C] at XCUITestDriver.executeCommand (/Users/username/Documents/Workspace/appium-xcuitest-driver/lib/driver.js:414:7)10[debug] [W3C] at XCUITestDriver.executeCommand (/Users/username/Documents/Workspace/appium-xcuitest-driver/lib/commands/index.js:3:8)11[debug] [W3C] at AppiumDriver.executeCommand (/usr/local/lib/node_modules/appium/lib/appium.js:562:36)12[debug] [W3C] at asyncHandler (/usr/local/lib/node_modules/appium/node_modules/appium-base-driver/lib/protocol/protocol.js:303:21)
Using AI Code Generation
1var path = require('path');2var fs = require('fs');3var _ = require('lodash');4var log = require('./logger');5var xcode = require('./utils').xcode;6var system = require('./utils').system;7var exec = require('teen_process').exec;8var uuid = require('uuid-js');9var mkdirp = require('mkdirp');10var logger = require('./logger');11var logDir = path.resolve(__dirname, 'log');12var appiumXcuitestDriver = require('appium-xcuites
Using AI Code Generation
1Your name to display (optional):2Your name to display (optional):3const appium = require('appium');4const wd = require('wd');5const {exec} = require('child_process');6let appiumServer = appium.main;7let appiumServerArgs = {8};9appiumServer(appiumServerArgs, (err, appiumServer) => {10 if (err) {11 console.error(err);12 return;13 }14 console.log('Appium server started');15 driver.init({16 }).then(() => {17 console.log('App launched');18 driver.startLogCapture();19 driver.stopLogCapture().then((logs) => {20 console.log(logs);21 appiumServer.close();22 });23 });24});25Your name to display (optional):
Using AI Code Generation
1I have tried to use the above approach, but the startLogCapture and stopLogCapture methods are not available in the driver object. I am using the latest version of Appium (1.6.4-beta) and the latest version of XCUITest driver (2.21.0)2[debug] [BaseDriver] Event 'newSessionStarted' logged at 1495578873795 (17:21:13 GMT+0530 (IST))3 at XCUITestDriver.startLogCapture$ (../../lib/driver.js:151:18)4 at tryCatch (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:67:40)5 at GeneratorFunctionPrototype.invoke [as _invoke] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:315:22)6 at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:100:21)7 at GeneratorFunctionPrototype.invoke (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:136:37)8 at XCUITestDriver.startLogCapture$ (../../lib/driver.js:151:18)9 at tryCatch (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:67:40)10 at GeneratorFunctionPrototype.invoke [as _invoke] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:315:22)11 at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:100:21)
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!