How to use this.startLogCapture method in Appium Xcuitest Driver

Best JavaScript code snippet using appium-xcuitest-driver

ios.js

Source:ios.js Github

copy

Full Screen

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);...

Full Screen

Full Screen

driver.js

Source:driver.js Github

copy

Full Screen

...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 };...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

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      }

Full Screen

Using AI Code Generation

copy

Full Screen

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)

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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):

Full Screen

Using AI Code Generation

copy

Full Screen

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)

Full Screen

Automation Testing Tutorials

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.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Appium Xcuitest Driver automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Sign up Free
_

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful