How to use this.initAutoWebview method in Appium Android Driver

Best JavaScript code snippet using appium-android-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 xcodeMajorVer = parseInt(opts.xcodeVersion.substr(0,871        opts.xcodeVersion.indexOf('.')), 10);872  var isiPhone = opts.forceIphone || opts.forceIpad === null || (opts.forceIpad !== null && !opts.forceIpad);873  var isTall = isiPhone;874  var isRetina = opts.xcodeVersion[0] !== '4';875  var is64bit = false;876  var deviceName = opts.deviceName;877  var fixDevice = true;878  if (deviceName && deviceName[0] === '=') {879    return deviceName.substring(1);880  }881  logger.debug("fixDevice is " + (fixDevice ? "on" : "off"));882  if (deviceName) {883    var device = deviceName.toLowerCase();884    if (device.indexOf("iphone") !== -1) {885      isiPhone = true;886    } else if (device.indexOf("ipad") !== -1) {887      isiPhone = false;888    }889    if (deviceName !== opts.platformName) {890      isTall = isiPhone && (device.indexOf("4-inch") !== -1);891      isRetina =  (device.indexOf("retina") !== -1);892      is64bit = (device.indexOf("64-bit") !== -1);893    }894  }895  var iosDeviceString = isiPhone ? "iPhone" : "iPad";896  if (xcodeMajorVer === 4) {897    if (isiPhone && isRetina) {898      iosDeviceString += isTall ? " (Retina 4-inch)" : " (Retina 3.5-inch)";899    } else {900      iosDeviceString += isRetina ? " (Retina)" : "";901    }902  } else if (xcodeMajorVer === 5) {903    iosDeviceString += isRetina ? " Retina" : "";904    if (isiPhone) {905      if (isRetina && isTall) {906        iosDeviceString += is64bit ? " (4-inch 64-bit)" : " (4-inch)";907      } else if (deviceName.toLowerCase().indexOf("3.5") !== -1) {908        iosDeviceString += " (3.5-inch)";909      }910    } else {911      iosDeviceString += is64bit ? " (64-bit)" : "";912    }913  } else if (_.contains([6, 7], xcodeMajorVer)) {914    iosDeviceString = opts.deviceName ||915      (isiPhone ? "iPhone Simulator" : "iPad Simulator");916  }917  var reqVersion = opts.platformVersion || opts.iOSSDKVersion;918  if (opts.iOSSDKVersion >= 8 && xcodeMajorVer === 7) {919    iosDeviceString += " (" + reqVersion + ")";920  } else if (opts.iOSSDKVersion >= 8) {921    iosDeviceString += " (" + reqVersion + " Simulator)";922  } else if (opts.iOSSDKVersion >= 7.1) {923    iosDeviceString += " - Simulator - iOS " + reqVersion;924  }925  if (fixDevice) {926    // Some device config are broken in 5.1927    var CONFIG_FIX = {928      'iPhone - Simulator - iOS 7.1': 'iPhone Retina (4-inch 64-bit) - ' +929                                      'Simulator - iOS 7.1',930      'iPad - Simulator - iOS 7.1': 'iPad Retina (64-bit) - Simulator - ' +931                                    'iOS 7.1',932      'iPad Simulator (8.0 Simulator)': 'iPad 2 (8.0 Simulator)',933      'iPad Simulator (8.1 Simulator)': 'iPad 2 (8.1 Simulator)',934      'iPad Simulator (8.2 Simulator)': 'iPad 2 (8.2 Simulator)',935      'iPad Simulator (8.3 Simulator)': 'iPad 2 (8.3 Simulator)',936      'iPad Simulator (8.4 Simulator)': 'iPad 2 (8.4 Simulator)',937      'iPad Simulator (7.1 Simulator)': 'iPad 2 (7.1 Simulator)',938      'iPhone Simulator (8.4 Simulator)': 'iPhone 6 (8.4 Simulator)',939      'iPhone Simulator (8.3 Simulator)': 'iPhone 6 (8.3 Simulator)',940      'iPhone Simulator (8.2 Simulator)': 'iPhone 6 (8.2 Simulator)',941      'iPhone Simulator (8.1 Simulator)': 'iPhone 6 (8.1 Simulator)',942      'iPhone Simulator (8.0 Simulator)': 'iPhone 6 (8.0 Simulator)',943      'iPhone Simulator (7.1 Simulator)': 'iPhone 5s (7.1 Simulator)'944    };945    // For xcode major version 7946    var CONFIG_FIX__XCODE_7 = {947      'iPad Simulator (8.1)': 'iPad 2 (8.1)',948      'iPad Simulator (8.2)': 'iPad 2 (8.2)',949      'iPad Simulator (8.3)': 'iPad 2 (8.3)',950      'iPad Simulator (8.4)': 'iPad 2 (8.4)',951      'iPhone Simulator (8.1)': 'iPhone 6 (8.1)',952      'iPhone Simulator (8.2)': 'iPhone 6 (8.2)',953      'iPhone Simulator (8.3)': 'iPhone 6 (8.3)',954      'iPhone Simulator (8.4)': 'iPhone 6 (8.4)',955      'iPad Simulator (9.0)': 'iPad 2 (9.0)',956      'iPad Simulator (9.1)': 'iPad 2 (9.1)',957      // Fixing ambiguous device name by adding '[' at the end so intruments958      // correctly starts iPhone 6 [udid] and not the iPhone 6 (9.0) + Apple Watch959      // for ios9.0 and above; see #5619960      'iPhone Simulator (9.0)': 'iPhone 6 (9.0) [',961      'iPhone Simulator (9.1)': 'iPhone 6 (9.1) [',962      'iPhone 6 (9.0)': 'iPhone 6 (9.0) [',963      'iPhone 6 (9.1)': 'iPhone 6 (9.1) ['964    };965    var configFix = xcodeMajorVer === 7 ? CONFIG_FIX__XCODE_7 : CONFIG_FIX;966    if (configFix[iosDeviceString]) {967      var oldDeviceString = iosDeviceString;968      iosDeviceString = configFix[iosDeviceString];969      logger.debug("Fixing device. Changed from: \"" + oldDeviceString +970                   "\" to: \"" + iosDeviceString + "\"");971    }972  }973  logger.debug("Final device string is: '" + iosDeviceString + "'");974  return iosDeviceString;975};976IOS.prototype.getDeviceString = function () {977  var opts = _.clone(this.args);978  _.extend(opts, {979    xcodeVersion: this.xcodeVersion,980    iOSSDKVersion: this.iOSSDKVersion981  });982  return IOS.getDeviceStringFromOpts(opts);983};984IOS.prototype.setDeviceTypeInInfoPlist = function (cb) {985  var plist = path.resolve(this.args.app, "Info.plist");986  var dString = this.getDeviceString();987  var isiPhone = dString.toLowerCase().indexOf("ipad") === -1;988  var deviceTypeCode = isiPhone ? 1 : 2;989  parsePlistFile(plist, function (err, obj) {990    if (err) {991      logger.error("Could not set the device type in Info.plist");992      return cb(err, null);993    } else {994      var newPlist;995      obj.UIDeviceFamily = [deviceTypeCode];996      if (binaryPlist) {997        newPlist = bplistCreate(obj);998      } else {999        newPlist = xmlplist.build(obj);1000      }1001      fs.writeFile(plist, newPlist, function (err) {1002        if (err) {1003          logger.error("Could not save new Info.plist");1004          cb(err);1005        } else {1006          logger.debug("Wrote new app Info.plist with device type");1007          cb();1008        }1009      }.bind(this));1010    }1011  }.bind(this));1012};1013IOS.prototype.getBundleIdFromApp = function (cb) {1014  logger.debug("Getting bundle ID from app");1015  var plist = path.resolve(this.args.app, "Info.plist");1016  parsePlistFile(plist, function (err, obj) {1017    if (err) {1018      logger.error("Could not get the bundleId from app.");1019      cb(err, null);1020    } else {1021      cb(null, obj.CFBundleIdentifier);1022    }1023  }.bind(this));1024};1025IOS.getSimForDeviceString = function (dString, availDevices) {1026  var matchedDevice = null;1027  var matchedUdid = null;1028  _.each(availDevices, function (device) {1029    if (device.indexOf(dString) !== -1) {1030      matchedDevice = device;1031      try {1032        matchedUdid = /.+\[([^\]]+)\]/.exec(device)[1];1033      } catch (e) {1034        matchedUdid = null;1035      }1036    }1037  });1038  return [matchedDevice, matchedUdid];1039};1040IOS.prototype.checkSimAvailable = function (cb) {1041  if (this.args.udid) {1042    logger.debug("Not checking whether simulator is available since we're on " +1043                 "a real device");1044    return cb();1045  }1046  if (this.iOSSDKVersion < 7.1) {1047    logger.debug("Instruments v < 7.1, not checking device string support");1048    return cb();1049  }1050  logger.debug("Checking whether instruments supports our device string");1051  Instruments.getAvailableDevicesWithRetry(3, function (err, availDevices) {1052    if (err) return cb(err);1053    var noDevicesError = function () {1054      var msg = "Could not find a device to launch. You requested '" +1055                dString + "', but the available devices were: " +1056                JSON.stringify(availDevices);1057      logger.error(msg);1058      cb(new Error(msg));1059    };1060    var dString = this.getDeviceString();1061    if (this.iOSSDKVersion >= 8) {1062      var sim = IOS.getSimForDeviceString(dString, availDevices);1063      if (sim[0] === null || sim[1] === null) {1064        return noDevicesError();1065      }1066      this.iOSSimUdid = sim[1];1067      logger.debug("iOS sim UDID is " + this.iOSSimUdid);1068      return cb();1069    } else if (!_.contains(availDevices, dString)) {1070      return noDevicesError();1071    }1072    cb();1073  }.bind(this));1074};1075IOS.prototype.setDeviceInfo = function (cb) {1076  this.shouldPrelaunchSimulator = false;1077  if (this.args.udid) {1078    logger.debug("Not setting device type since we're on a real device");1079    return cb();1080  }1081  if (!this.args.app && this.args.bundleId) {1082    logger.debug("Not setting device type since we're using bundle ID and " +1083                "assuming app is already installed");1084    return cb();1085  }1086  if (!this.args.deviceName &&1087      this.args.forceIphone === null &&1088      this.args.forceIpad === null) {1089    logger.debug("No device specified, current device in the iOS " +1090                 "simulator will be used.");1091    return cb();1092  }1093  if (this.args.defaultDevice || this.iOSSDKVersion >= 7.1) {1094    if (this.iOSSDKVersion >= 7.1) {1095      logger.debug("We're on iOS7.1+ so forcing defaultDevice on");1096    } else {1097      logger.debug("User specified default device, letting instruments launch it");1098    }1099  } else {1100    this.shouldPrelaunchSimulator = true;1101  }1102  this.setDeviceTypeInInfoPlist(cb);1103};1104IOS.prototype.createSimulator = function (cb) {1105  this.sim = new Simulator({1106    platformVer: this.args.platformVersion,1107    sdkVer: this.iOSSDKVersion,1108    udid: this.iOSSimUdid1109  });1110  cb();1111};1112IOS.prototype.moveBuiltInApp = function (cb) {1113  if (this.appString().toLowerCase() === "settings") {1114    logger.debug("Trying to use settings app, version " +1115                 this.args.platformVersion);1116    this.sim.preparePreferencesApp(this.args.tmpDir, function (err, attemptedApp, origApp) {1117      if (err) {1118        logger.error("Could not prepare settings app: " + err);1119        return cb(err);1120      }1121      logger.debug("Using settings app at " + attemptedApp);1122      this.args.app = attemptedApp;1123      this.args.origAppPath = origApp;1124      cb();1125    }.bind(this));1126  } else {1127    cb();1128  }1129};1130IOS.prototype.prelaunchSimulator = function (cb) {1131  var msg;1132  if (!this.shouldPrelaunchSimulator) {1133    logger.debug("Not pre-launching simulator");1134    return cb();1135  }1136  xcode.getPath(function (err, xcodePath) {1137    if (err) {1138      return cb(new Error('Could not find xcode folder. Needed to start simulator. ' + err.message));1139    }1140    logger.debug("Pre-launching simulator");1141    var iosSimPath = path.resolve(xcodePath,1142        "Platforms/iPhoneSimulator.platform/Developer/Applications" +1143        "/iPhone Simulator.app/Contents/MacOS/iPhone Simulator");1144    if (!fs.existsSync(iosSimPath)) {1145      msg = "Could not find ios simulator binary at " + iosSimPath;1146      logger.error(msg);1147      return cb(new Error(msg));1148    }1149    this.endSimulator(function (err) {1150      if (err) return cb(err);1151      logger.debug("Launching device: " + this.getDeviceString());1152      var iosSimArgs = ["-SimulateDevice", this.getDeviceString()];1153      this.iosSimProcess = spawn(iosSimPath, iosSimArgs);1154      var waitForSimulatorLogs = function (countdown) {1155        if (countdown <= 0 ||1156          (this.logs.syslog && (this.logs.syslog.getAllLogs().length > 0 ||1157          (this.logs.crashlog && this.logs.crashlog.getAllLogs().length > 0)))) {1158          logger.debug(countdown > 0 ? "Simulator is now ready." :1159                       "Waited 10 seconds for simulator to start.");1160          cb();1161        } else {1162          setTimeout(function () {1163            waitForSimulatorLogs(countdown - 1);1164          }, 1000);1165        }1166      }.bind(this);1167      waitForSimulatorLogs(10);1168    }.bind(this));1169  }.bind(this));1170};1171IOS.prototype.parseLocalizableStrings = function (/* language, stringFile, cb */) {1172  var args = new Args(arguments);1173  var cb = args.callback;1174  if (this.args.app === null) {1175    logger.debug("Localizable.strings is not currently supported when using real devices.");1176    return cb();1177  }1178  var language = args.all[0] || this.args.language1179    , stringFile = args.all[1] || "Localizable.strings"1180    , strings = null;1181  if (language) {1182    strings = path.resolve(this.args.app, language + ".lproj", stringFile);1183  }1184  if (!fs.existsSync(strings)) {1185    if (language) {1186      logger.debug("No strings file '" + stringFile + "' for language '" + language + "', getting default strings");1187    }1188    strings = path.resolve(this.args.app, stringFile);1189  }1190  if (!fs.existsSync(strings)) {1191    strings = path.resolve(this.args.app, this.args.localizableStringsDir, stringFile);1192  }1193  parsePlistFile(strings, function (err, obj) {1194    if (err) {1195      logger.warn("Could not parse app " + stringFile +" assuming it " +1196                  "doesn't exist");1197    } else {1198      logger.debug("Parsed app " + stringFile);1199      this.localizableStrings = obj;1200    }1201    cb();1202  }.bind(this));1203};1204IOS.prototype.deleteSim = function (cb) {1205  this.sim.deleteSim(cb);1206};1207IOS.prototype.clearAppData = function (cb) {1208  if (!this.keepAppToRetainPrefs && this.args.app && this.args.bundleId) {1209    this.sim.cleanCustomApp(path.basename(this.args.app), this.args.bundleId);1210  }1211  cb();1212};1213IOS.prototype.cleanupSimState = function (cb) {1214  if (this.realDevice && this.args.bundleId && this.args.fullReset) {1215    logger.debug("fullReset requested. Will try to uninstall the app.");1216    var bundleId = this.args.bundleId;1217    this.realDevice.remove(bundleId, function (err) {1218      if (err) {1219        this.removeApp(bundleId, function (err) {1220          if (err) {1221            logger.error("Could not remove " + bundleId + " from device");1222            cb(err);1223          } else {1224            logger.debug("Removed " + bundleId);1225            cb();1226          }1227        }.bind(this));1228      } else {1229        logger.debug("Removed " + bundleId);1230        cb();1231      }1232    }.bind(this));1233  } else if (!this.args.udid) {1234    this.sim.cleanSim(this.args.keepKeyChains, this.args.tmpDir, function (err) {1235      if (err) {1236        logger.error("Could not reset simulator. Leaving as is. Error: " + err.message);1237      }1238      this.clearAppData(cb);1239    }.bind(this));1240  } else {1241    logger.debug("On a real device; cannot clean device state");1242    cb();1243  }1244};1245IOS.prototype.runSimReset = function (cb) {1246  if (this.args.reset || this.args.fullReset) {1247    logger.debug("Running ios sim reset flow");1248    // The simulator process must be ended before we delete applications.1249    async.series([1250      this.endSimulator.bind(this),1251      function (cb) {1252        if (this.args.reset) {1253          this.cleanupSimState(cb);1254        } else {1255          cb();1256        }1257      }.bind(this),1258      function (cb) {1259        if (this.args.fullReset && !this.args.udid) {1260          this.deleteSim(cb);1261        } else {1262          cb();1263        }1264      }.bind(this)1265    ], cb);1266  } else {1267    logger.debug("Reset not set, not ending sim or cleaning up app state");1268    cb();1269  }1270};1271IOS.prototype.isolateSimDevice = function (cb) {1272  if (!this.args.udid && this.args.isolateSimDevice &&1273      this.iOSSDKVersion >= 8) {1274    this.sim.deleteOtherSims(cb);1275  } else {1276    cb();1277  }1278};1279IOS.prototype.postCleanup = function (cb) {1280  this.curCoords = null;1281  this.curOrientation = null;1282  if (!_.isEmpty(this.logs)) {1283    this.logs.syslog.stopCapture();1284    this.logs = {};1285  }1286  if (this.remote) {1287    this.stopRemote();1288  }1289  this.runSimReset(function () {1290    // ignore any errors during reset and continue shutting down1291    this.isShuttingDown = false;1292    cb();1293  }.bind(this));1294};1295IOS.prototype.endSimulator = function (cb) {1296  logger.debug("Killing the simulator process");1297  if (this.iosSimProcess) {1298    this.iosSimProcess.kill("SIGHUP");1299    this.iosSimProcess = null;1300  } else {1301    Instruments.killAllSim();1302  }1303  this.endSimulatorDaemons(cb);1304};1305IOS.prototype.endSimulatorDaemons = function (cb) {1306  logger.debug("Killing any other simulator daemons");1307  var stopCmd = 'launchctl list | grep com.apple.iphonesimulator | cut -f 3 | xargs -n 1 launchctl stop';1308  exec(stopCmd, { maxBuffer: 524288 }, function () {1309    var removeCmd = 'launchctl list | grep com.apple.iphonesimulator | cut -f 3 | xargs -n 1 launchctl remove';1310    exec(removeCmd, { maxBuffer: 524288 }, function () {1311      cb();1312    });1313  });1314};1315IOS.prototype.stop = function (cb) {1316  logger.debug("Stopping ios");1317  if (this.instruments === null) {1318    logger.debug("Trying to stop instruments but it already exited");1319    this.postCleanup(cb);1320  } else {1321    this.commandProxy.shutdown(function (err) {1322      if (err) logger.warn("Got warning when trying to close command proxy:", err);1323      this.instruments.shutdown(function (code) {1324        this.shutdown(code, cb);1325      }.bind(this));1326    }.bind(this));1327  }1328};1329IOS.prototype.shutdown = function (code, cb) {1330  this.commandProxy = null;1331  this.instruments = null;1332  this.postCleanup(cb);1333};1334IOS.prototype.resetTimeout = deviceCommon.resetTimeout;1335IOS.prototype.waitForCondition = deviceCommon.waitForCondition;1336IOS.prototype.implicitWaitForCondition = deviceCommon.implicitWaitForCondition;1337IOS.prototype.proxy = deviceCommon.proxy;1338IOS.prototype.proxyWithMinTime = deviceCommon.proxyWithMinTime;1339IOS.prototype.respond = deviceCommon.respond;1340IOS.prototype.getSettings = deviceCommon.getSettings;1341IOS.prototype.updateSettings = deviceCommon.updateSettings;1342IOS.prototype.initQueue = function () {1343  this.queue = async.queue(function (command, cb) {1344    if (!this.commandProxy) return cb();1345    async.series([1346      function (cb) {1347        async.whilst(1348          function () { return this.selectingNewPage && this.curContext; }.bind(this),1349          function (cb) {1350            logger.debug("We're in the middle of selecting a new page, " +1351                        "waiting to run next command until done");1352            setTimeout(cb, 100);1353          },1354          cb1355        );1356      }.bind(this),1357      function (cb) {1358        var matched = false;1359        var matches = ["au.alertIsPresent", "au.getAlertText", "au.acceptAlert",1360                       "au.dismissAlert", "au.setAlertText",1361                       "au.waitForAlertToClose"];1362        _.each(matches, function (match) {1363          if (command.indexOf(match) === 0) {1364            matched = true;1365          }1366        });1367        async.whilst(1368          function () { return !matched && this.curContext && this.processingRemoteCmd; }.bind(this),1369          function (cb) {1370            logger.debug("We're in the middle of processing a remote debugger " +1371                        "command, waiting to run next command until done");1372            setTimeout(cb, 100);1373          },1374          cb1375        );1376      }.bind(this)1377    ], function (err) {1378      if (err) return cb(err);1379      this.cbForCurrentCmd = cb;1380      if (this.commandProxy) {1381        this.commandProxy.sendCommand(command, function (response) {1382          this.cbForCurrentCmd = null;1383          if (typeof cb === 'function') {1384            this.respond(response, cb);1385          }1386        }.bind(this));1387      }1388    }.bind(this));1389  }.bind(this), 1);1390};1391IOS.prototype.push = function (elem) {1392  this.queue.push(elem[0], elem[1]);1393};1394IOS.prototype.isAppInstalled = function (bundleId, cb) {1395  if (this.args.udid) {1396    this.realDevice.isInstalled(bundleId, cb);1397  } else {1398    cb(new Error("You can not call isInstalled for the iOS simulator!"));1399  }1400};1401IOS.prototype.removeApp = function (bundleId, cb) {1402  if (this.args.udid) {1403    this.realDevice.remove(bundleId, cb);1404  } else {1405    cb(new Error("You can not call removeApp for the iOS simulator!"));1406  }1407};1408IOS.prototype.installApp = function (unzippedAppPath, cb) {1409  if (this.args.udid) {1410    this.realDevice.install(unzippedAppPath, cb);1411  } else {1412    cb(new Error("You can not call installApp for the iOS simulator!"));1413  }1414};1415IOS.prototype.unpackApp = function (req, cb) {1416  deviceCommon.unpackApp(req, '.app', cb);1417};1418IOS.prototype.startLogCapture = function (cb) {1419  if (!_.isEmpty(this.logs)) {1420    cb(new Error("Trying to start iOS log capture but it's already started!"));1421    return;1422  }1423  this.logs.crashlog = new iOSCrashLog();1424  this.logs.syslog = new iOSLog({1425    udid: this.args.udid1426  , simUdid: this.iOSSimUdid1427  , showLogs: this.args.showSimulatorLog || this.args.showIOSLog1428  });1429  this.logs.syslog.startCapture(function (err) {1430    if (err) {1431      logger.warn("Could not capture logs from device. Continuing without capturing logs.");1432      return cb();1433    }1434    this.logs.crashlog.startCapture(cb);1435  }.bind(this));1436};1437IOS.prototype.initAutoWebview = function (cb) {1438  if (this.args.autoWebview) {1439    logger.debug('Setting auto webview');1440    this.navToInitialWebview(cb);1441  } else {1442    cb();1443  }1444};1445IOS.prototype.getContextsAndViews = function (cb) {1446  this.listWebFrames(function (err, webviews) {1447    if (err) return cb(err);1448    var ctxs = [{id: this.NATIVE_WIN}];1449    this.contexts = [this.NATIVE_WIN];1450    _.each(webviews, function (view) {1451      ctxs.push({id: this.WEBVIEW_BASE + view.id, view: view});1452      this.contexts.push(view.id.toString());1453    }.bind(this));1454    cb(null, ctxs);1455  }.bind(this));1456};1457IOS.prototype.getLatestWebviewContextForTitle = function (titleRegex, cb) {1458  this.getContextsAndViews(function (err, contexts) {1459    if (err) return cb(err);1460    var matchingCtx;1461    _(contexts).each(function (ctx) {1462      if (ctx.view && (ctx.view.title || "").match(titleRegex)) {1463        if (ctx.view.url === "about:blank") {1464          // in the cases of Xcode < 5 (i.e., iOS SDK Version less than 7)1465          // iOS 7.1, iOS 9.0 & iOS 9.1 in a webview (not in Safari)1466          // we can have the url be `about:blank`1467          if (parseFloat(this.iOSSDKVersion) < 7 || parseFloat(this.iOSSDKVersion) >= 9 ||1468              (this.args.platformVersion === '7.1' && this.args.app && this.args.app.toLowerCase() !== 'safari')) {1469            matchingCtx = ctx;1470          }1471        } else {1472          matchingCtx = ctx;1473        }1474      }1475    }.bind(this));1476    cb(null, matchingCtx ? matchingCtx.id : undefined);1477  }.bind(this));1478};1479// Right now we don't necessarily wait for webview1480// and frame to load, which leads to race conditions and flakiness1481// , let see if we can transition to something better1482IOS.prototype.useNewSafari = function () {1483  return parseFloat(this.iOSSDKVersion) >= 8.1 &&1484         parseFloat(this.args.platformVersion) >= 8.1 &&1485         !this.args.udid &&1486         this.capabilities.safari;1487};1488IOS.prototype.navToInitialWebview = function (cb) {1489  var timeout = 0;1490  if (this.args.udid) timeout = parseInt(this.iOSSDKVersion, 10) >= 8 ? 4000 : 6000;1491  if (timeout > 0) logger.debug('Waiting for ' + timeout + ' ms before navigating to view.');1492  setTimeout(function () {1493    if (this.useNewSafari()) {1494      return this.typeAndNavToUrl(cb);1495    } else if (parseInt(this.iOSSDKVersion, 10) >= 7 && !this.args.udid && this.capabilities.safari) {1496      this.navToViewThroughFavorites(cb);1497    } else {1498      this.navToViewWithTitle(/.*/, cb);1499    }1500  }.bind(this), timeout);1501};1502IOS.prototype.typeAndNavToUrl = function (cb) {1503  var initialUrl = this.args.safariInitialUrl || 'http://127.0.0.1:' + this.args.port + '/welcome';1504  var oldImpWait = this.implicitWaitMs;1505  this.implicitWaitMs = 7000;1506  function noArgsCb(cb) { return function (err) { cb(err); }; }1507  async.waterfall([1508    this.findElement.bind(this, 'name', 'URL'),1509    function (res, cb) {1510      this.implicitWaitMs = oldImpWait;1511      this.nativeTap(res.value.ELEMENT, noArgsCb(cb));1512    }.bind(this),1513    this.findElements.bind(this, 'name', 'Address'),1514    function (res, cb) {1515      var addressEl = res.value[res.value.length -1].ELEMENT;1516      this.setValueImmediate(addressEl, initialUrl, noArgsCb(cb));1517    }.bind(this),1518    this.findElement.bind(this, 'name', 'Go'),1519    function (res, cb) {1520      this.nativeTap(res.value.ELEMENT, noArgsCb(cb));1521    }.bind(this)1522  ], function () {1523    this.navToViewWithTitle(/.*/i, function (err) {1524      if (err) return cb(err);1525      // Waits for page to finish loading.1526      this.remote.pageUnload(cb);1527    }.bind(this));1528  }.bind(this));1529};1530IOS.prototype.navToViewThroughFavorites = function (cb) {1531  logger.debug("We're on iOS7+ simulator: clicking apple button to get into " +1532              "a webview");1533  var oldImpWait = this.implicitWaitMs;1534  this.implicitWaitMs = 7000; // wait 7s for apple button to exist1535  this.findElement('xpath', '//UIAScrollView[1]/UIAButton[1]', function (err, res) {1536    this.implicitWaitMs = oldImpWait;1537    if (err || res.status !== status.codes.Success.code) {1538      var msg = "Could not find button to click to get into webview. " +1539                "Proceeding on the assumption we have a working one.";1540      logger.error(msg);1541      return this.navToViewWithTitle(/.*/i, cb);1542    }1543    this.nativeTap(res.value.ELEMENT, function (err, res) {1544      if (err || res.status !== status.codes.Success.code) {1545        var msg = "Could not click button to get into webview. " +1546                  "Proceeding on the assumption we have a working one.";1547        logger.error(msg);1548      }1549      this.navToViewWithTitle(/apple/i, cb);1550    }.bind(this));1551  }.bind(this));1552};1553IOS.prototype.navToViewWithTitle = function (titleRegex, cb) {1554  logger.debug("Navigating to most recently opened webview");1555  var start = Date.now();1556  var spinTime = 500;1557  var spinHandles = function () {1558    this.getLatestWebviewContextForTitle(titleRegex, function (err, res) {1559      if (err) {1560        cb(new Error("Could not navigate to webview! Err: " + err));1561      } else if (!res) {1562        if ((Date.now() - start) < 90000) {1563          logger.warn("Could not find any webviews yet, refreshing/retrying");1564          if (this.args.udid || !this.capabilities.safari) {1565            return setTimeout(spinHandles, spinTime);1566          }1567          this.findUIElementOrElements('accessibility id', 'ReloadButton',1568              '', false, function (err, res) {1569            if (err || !res || !res.value || !res.value.ELEMENT) {1570              logger.warn("Could not find reload button, continuing");1571              setTimeout(spinHandles, spinTime);1572            } else {1573              this.nativeTap(res.value.ELEMENT, function (err, res) {1574                if (err || !res) {1575                  logger.warn("Could not click reload button, continuing");1576                }1577                setTimeout(spinHandles, spinTime);1578              }.bind(this));1579            }1580          }.bind(this));1581        } else {1582          cb(new Error("Could not navigate to webview; there aren't any!"));1583        }1584      } else {1585        var latestWindow = res;1586        logger.debug("Picking webview " + latestWindow);1587        this.setContext(latestWindow, function (err) {1588          if (err) return cb(err);1589          this.remote.cancelPageLoad();1590          cb();1591        }.bind(this), true);1592      }1593    }.bind(this));1594  }.bind(this);1595  spinHandles();1596};1597_.extend(IOS.prototype, iOSHybrid);1598_.extend(IOS.prototype, iOSController);...

Full Screen

Full Screen

driver.js

Source:driver.js Github

copy

Full Screen

...302    await this.onInstrumentsLaunch();303    await this.configureBootstrap();304    await this.setBundleId();305    await this.setInitialOrientation();306    await this.initAutoWebview();307    await this.waitForAppLaunched();308  }309  async startRealDevice () {310    await utils.removeInstrumentsSocket(this.sock);311    this.opts.localizableStrings = await utils.parseLocalizableStrings(this.opts);312    await utils.setBundleIdFromApp(this.opts);313    await this.createInstruments();314    await runRealDeviceReset(this.realDevice, this.opts);315    await this.setUpLogCapture();316    await this.installToRealDevice();317    await this.startInstruments();318    await this.onInstrumentsLaunch();319    await this.configureBootstrap();320    await this.setBundleId();321    await this.setInitialOrientation();322    await this.initAutoWebview();323    await this.waitForAppLaunched();324  }325  async installToRealDevice () {326    // if user has passed in desiredCaps.autoLaunch = false327    // meaning they will manage app install / launching328    if (this.opts.autoLaunch === false) {329      return;330    }331    // if we have an ipa file, set it in opts332    if (this.opts.app) {333      let ext = this.opts.app.substring(this.opts.app.length - 3).toLowerCase();334      if (ext === 'ipa') {335        this.opts.ipa = this.opts.app;336      }...

Full Screen

Full Screen

selendroid.js

Source:selendroid.js Github

copy

Full Screen

1"use strict";2var Device = require('../device.js')3  , mkdirp = require('mkdirp')4  , _ = require('underscore')5  , deviceCommon = require('../common.js')6  , androidController = require('./android-controller.js')7  , androidContextController = require('./android-context-controller.js')8  , proxyTo = deviceCommon.proxyTo9  , doRequest = deviceCommon.doRequest10  , logger = require('../../server/logger.js').get('appium')11  , status = require("../../server/status.js")12  , fs = require('fs')13  , async = require('async')14  , androidCommon = require('./android-common.js')15  , androidHybrid = require('./android-hybrid.js')16  , path = require('path')17  , md5 = require('md5calculator')18  , utf7 = require('utf7').imap;19var Selendroid = function () {20  this.init();21};22_.extend(Selendroid.prototype, Device.prototype);23Selendroid.prototype._deviceInit = Device.prototype.init;24Selendroid.prototype.init = function () {25  this._deviceInit();26  this.selendroidHost = 'localhost';27  this.selendroidPort = 8080;28  this.selendroidSessionId = null;29  this.appExt = ".apk";30  this.args.devicePort = this.selendroidPort;31  this.serverApk = null;32  this.onStop = function () {};33  this.adb = null;34  this.isProxy = true;35  this.mobileMethodsSupported = [36    'setLocation'37    , 'setCommandTimeout'38    , 'reset'39    , 'lock'40    , 'background'41    , 'keyevent'42    , 'currentActivity'43    , 'installApp'44    , 'uninstallApp'45    , 'removeApp'46    , 'closeApp'47    , 'isAppInstalled'48    , 'launchApp'49    , 'toggleData'50    , 'toggleFlightMode'51    , 'toggleWiFi'52    , 'toggleLocationServices'53    , 'getStrings'54  ];55  this.proxyHost = this.selendroidHost;56  this.avoidProxy = [57    ['GET', new RegExp('^/wd/hub/session/[^/]+/log/types$')]58    , ['POST', new RegExp('^/wd/hub/session/[^/]+/log')]59    , ['POST', new RegExp('^/wd/hub/session/[^/]+/location')]60    , ['POST', new RegExp('^/wd/hub/session/[^/]+/appium')]61    , ['GET', new RegExp('^/wd/hub/session/[^/]+/appium')]62    , ['POST', new RegExp('^/wd/hub/session/[^/]+/context')]63    , ['GET', new RegExp('^/wd/hub/session/[^/]+/context')]64    , ['GET', new RegExp('^/wd/hub/session/[^/]+/contexts')]65    , ['POST', new RegExp('^/wd/hub/session/[^/]+/element/[^/]+/value')]66    , ['GET', new RegExp('^/wd/hub/session/[^/]+/network_connection')]67    , ['POST', new RegExp('^/wd/hub/session/[^/]+/network_connection')]68    , ['POST', new RegExp('^/wd/hub/session/[^/]+/ime')]69    , ['GET', new RegExp('^/wd/hub/session/[^/]+/ime')]70  ];71  this.curContext = this.defaultContext();72};73Selendroid.prototype.getSettings = deviceCommon.getSettings;74Selendroid.prototype.updateSettings = deviceCommon.updateSettings;75Selendroid.prototype.pushUnlock = androidController.pushUnlock;76Selendroid.prototype.unlock = androidController.unlock;77Selendroid.prototype.installApp = androidController.installApp;78Selendroid.prototype.isAppInstalled = androidController.isAppInstalled;79Selendroid.prototype.removeApp = androidController.removeApp;80_.extend(Selendroid.prototype, androidCommon);81Selendroid.prototype._deviceConfigure = Device.prototype.configure;82Selendroid.prototype._setAndroidArgs = androidCommon.setAndroidArgs;83Selendroid.prototype.setAndroidArgs = function () {84  this._setAndroidArgs();85  this.args.systemPort = this.args.selendroidPort;86  this.proxyPort = this.args.systemPort;87};88Selendroid.prototype.start = function (cb) {89  logger.debug("Starting selendroid server");90  var modServerExists = false91    , modAppPkg = null92    , resignedServerMd5Hash = null;93  var checkModServerExists = function (cb) {94    this.selendroidServerPath = path.resolve(this.args.tmpDir,95        'selendroid.' + this.args.appPackage + '.apk');96    modAppPkg = this.args.appPackage + '.selendroid';97    fs.exists(this.selendroidServerPath, function (exists) {98      modServerExists = exists;99      cb();100    });101  }.bind(this);102  var checkServerResigned = function (cb) {103    if (modServerExists) {104      md5(this.selendroidServerPath, function (err, md5Hash) {105        if (err) return cb(err);106        logger.debug("MD5 for selendroid server is " + md5Hash);107        if (resignedServerMd5Hash !== md5Hash) {108          resignedServerMd5Hash = md5Hash;109          modServerExists = false;110        }111      }.bind(this));112    }113    cb();114  }.bind(this);115  var conditionalUninstallSelendroid = function (cb) {116    if (!modServerExists) {117      logger.debug("Rebuilt selendroid apk does not exist, uninstalling " +118                  "any instances of it on device to make way for new one");119      this.adb.uninstallApk(modAppPkg, cb);120    } else {121      logger.debug("Rebuilt selendroid apk exists, doing nothing");122      cb();123    }124  }.bind(this);125  var conditionalInsertManifest = function (cb) {126    if (!modServerExists) {127      logger.debug("Rebuilt selendroid server does not exist, inserting " +128                  "modified manifest");129      this.insertSelendroidManifest(this.serverApk, cb);130    } else {131      logger.debug("Rebuilt selendroid server already exists, no need to " +132                  "rebuild it with a new manifest");133      cb();134    }135  }.bind(this);136  var conditionalInstallSelendroid = function (cb) {137    this.adb.isAppInstalled(modAppPkg, function (e, installed) {138      if (!installed) {139        logger.debug("Rebuilt selendroid is not installed, installing it");140        this.adb.install(this.selendroidServerPath, cb);141      } else {142        logger.debug("Rebuilt selendroid is already installed");143        cb();144      }145    }.bind(this));146  }.bind(this);147  async.series([148    this.initJavaVersion.bind(this),149    this.initAdb.bind(this),150    this.ensureServerExists.bind(this),151    this.prepareDevice.bind(this),152    this.checkInternetPermissionForApp.bind(this),153    this.packageAndLaunchActivityFromManifest.bind(this),154    checkModServerExists,155    conditionalInsertManifest,156    this.checkSelendroidCerts.bind(this),157    checkServerResigned,158    conditionalUninstallSelendroid,159    conditionalInstallSelendroid,160    this.extractStringsSelendroid.bind(this),161    this.uninstallApp.bind(this),162    this.installAppForTest.bind(this),163    this.forwardPort.bind(this),164    this.initUnicode.bind(this),165    this.pushSettingsApp.bind(this),166    this.pushUnlock.bind(this),167    this.unlock.bind(this),168    this.pushSelendroid.bind(this),169    this.waitForServer.bind(this)170  ], function (err) {171    if (err) return cb(err);172    async.series([173      this.createSession.bind(this),174      this.initAutoWebview.bind(this)175    ], function (err, res) {176      if (err) return cb(err);177      // `createSession` returns session id, so send that along178      cb(null, res[0]);179    });180  }.bind(this));181};182Selendroid.prototype.pushSelendroid = function (cb) {183  var instrumentWith = this.args.appPackage + ".selendroid/" +184                       "io.selendroid.server.ServerInstrumentation";185  this.adb.instrument(this.args.appPackage, this.args.appActivity, instrumentWith, cb);186};187Selendroid.prototype.checkInternetPermissionForApp = function (cb) {188  var apk = this.args.app;189  this.adb.hasInternetPermissionFromManifest(apk, function (err, hasInternetPermission) {190    if (err) return cb(err);191    if (hasInternetPermission) {192      return cb();193    }194    else {195      var errorMessg = "apk does not have INTERNET permissions. Selendroid needs internet " +196                       "permission to proceed, please check if you have <uses-permission " +197                       "android:name=\"android.**permission.INTERNET\"/> in your " +198                       "AndroidManifest.xml";199      cb(new Error(errorMessg));200    }201  });202};203Selendroid.prototype.checkSelendroidCerts = function (cb) {204  var alreadyReturned = false205    , checks = 0;206  var onDoneSigning = function () {207    checks++;208    if (checks === 2 && !alreadyReturned) {209      cb();210    }211  };212  // these run in parallel213  var apks = [this.selendroidServerPath, this.args.app];214  _.each(apks, function (apk) {215    logger.debug("Checking signed status of " + apk);216    this.adb.checkApkCert(apk, this.args.appPackage, function (err, isSigned) {217      if (err) return cb(err);218      if (isSigned) return onDoneSigning();219      this.adb.sign(apk, function (err) {220        if (err && !alreadyReturned) {221          alreadyReturned = true;222          return cb(err);223        }224        onDoneSigning();225      });226    }.bind(this));227  }.bind(this));228};229Selendroid.prototype.stop = function (ocb) {230  var completeShutdown = function (cb) {231    if (this.args.unicodeKeyboard && this.args.resetKeyboard && this.defaultIME) {232      logger.debug('Resetting IME to \'' + this.defaultIME + '\'');233      this.adb.setIME(this.defaultIME, function (err) {234        if (err) {235          // simply warn on error here, because we don't want to stop the shutdown236          // process237          logger.warn(err);238        }239        logger.debug("Stopping selendroid server");240        this.deleteSession(cb);241      }.bind(this));242    } else {243      logger.debug("Stopping selendroid server");244      this.deleteSession(cb);245    }246  }.bind(this);247  completeShutdown(function (err) {248    if (err) return ocb(err);249    // Remove the app _after_ stopping Selendroid, or Selendroid will fail250    if (this.args.fullReset) {251      logger.debug("Removing app from device");252      this.uninstallApp(function (err) {253        if (err) {254          // simply warn on error here, because we don't want to stop the shutdown255          // process256          logger.warn(err);257        }258        ocb();259      });260    } else {261      ocb();262    }263  }.bind(this));264};265Selendroid.prototype.keyevent = function (keycode, metastate, cb) {266  this.adb.keyevent(keycode, function () {267    cb(null, {268      status: status.codes.Success.code269    , value: null270    });271  });272};273/*274 * Execute an arbitrary function and handle potential ADB disconnection before275 * proceeding276 */277Selendroid.prototype.wrapActionAndHandleADBDisconnect = function (action, ocb) {278  async.series([279    function (cb) {280      action(cb);281    }.bind(this)282    , this.adb.restart.bind(this.adb)283    , this.forwardPort.bind(this)284  ], function (err) {285    ocb(err);286  }.bind(this));287};288Selendroid.prototype.ensureServerExists = function (cb) {289  logger.debug("Checking whether selendroid is built yet");290  var selBin = path.resolve(__dirname, "..", "..", "..", "build", "selendroid",291      "selendroid.apk");292  fs.stat(selBin, function (err) {293    if (err) {294      logger.debug("Selendroid needs to be built; please run ./reset.sh " +295                  "--selendroid");296      return cb(err);297    }298    logger.debug("Selendroid server exists!");299    this.serverApk = selBin;300    cb(null);301  }.bind(this));302};303Selendroid.prototype.waitForServer = function (cb) {304  var waitMs = 20000305    , intMs = 800306    , start = Date.now();307  var pingServer = function () {308    this.proxyTo('/wd/hub/status', 'GET', null, function (err, res, body) {309      if (body === null || typeof body === "undefined" || !body.trim()) {310        if (Date.now() - start < waitMs) {311          setTimeout(pingServer, intMs);312        } else {313          cb(new Error("Waited " + (waitMs / 1000) + " secs for " +314                       "selendroid server and it never showed up"));315        }316      } else {317        logger.debug("Selendroid server is alive!");318        cb(null);319      }320    });321  }.bind(this);322  pingServer();323};324Selendroid.prototype.createSession = function (cb) {325  logger.debug("Listening for Selendroid logs");326  this.adb.logcat.on('log', function (logObj) {327    if (/System/.test(logObj.message)) {328      var type = "";329      if (/System\.err/.test(logObj.message)) {330        type = " ERR";331      }332      var msg = logObj.message.replace(/^.+: /, '');333      logger.debug("[SELENDROID" + type + "] " + msg);334    }335  }.bind(this));336  logger.debug("Creating Selendroid session");337  var data = {desiredCapabilities: this.capabilities};338  this.proxyTo('/wd/hub/session', 'POST', data, function (err, res, body) {339    if (err) return cb(err);340    if (res.statusCode === 200 && body.sessionId) {341      logger.debug("Successfully started selendroid session");342      this.selendroidSessionId = body.sessionId;343      this.proxySessionId = this.selendroidSessionId;344      this.adb.waitForActivity(this.args.appWaitPackage, this.args.appWaitActivity, 1800,345          function (err) {346        if (err) {347          logger.debug("Selendroid hasn't started app yet, let's do it " +348                      "manually with adb.startApp");349          var onStart = function (err) {350            if (err) return cb(err);351            return cb(null, body.sessionId);352          }.bind(this);353          return this.adb.startApp({354                   pkg: this.args.appPackage,355                   activity: this.args.appActivity,356                   action: this.args.intentAction,357                   category: this.args.intentCategory,358                   flags: this.args.intentFlags,359                   waitPkg: this.args.appWaitPackage,360                   waitActivity: this.args.appWaitActivity,361                   optionalIntentArguments: this.args.optionalIntentArguments,362                   stopApp: this.args.stopAppOnReset,363                   retry: false364                 }, onStart);365        }366        return cb(null, body.sessionId);367      }.bind(this), 1800);368    } else {369      logger.error("Selendroid create session did not work. Status was " +370                   res.statusCode + " and body was " + body);371      cb(new Error("Selendroid session creation did not work."));372    }373  }.bind(this));374};375Selendroid.prototype.deleteSession = function (cb) {376  var url = '/wd/hub/session/' + this.selendroidSessionId;377  this.proxyTo(url, 'DELETE', null, function (err, res) {378    if (err) return cb(err);379    if (res.statusCode !== 200) return cb(new Error("Status was not 200"));380    this.adb.forceStop(this.args.appPackage, function (err) {381      if (err) return cb(err);382      this.adb.stopLogcat(cb);383    }.bind(this));384  }.bind(this));385};386Selendroid.prototype.proxyTo = proxyTo;387Selendroid.prototype.insertSelendroidManifest = function (serverPath, cb) {388  logger.debug("Inserting selendroid manifest");389  var newServerPath = this.selendroidServerPath390    , newPackage = this.args.appPackage + '.selendroid'391    , srcManifest = path.resolve(__dirname, '..', '..', '..', 'build',392        'selendroid', 'AndroidManifest.xml')393    , dstDir = path.resolve(this.args.tmpDir, this.args.appPackage)394    , dstManifest = path.resolve(dstDir, 'AndroidManifest.xml');395  try {396    fs.mkdirSync(dstDir);397  } catch (e) {398    if (e.message.indexOf("EEXIST") === -1) {399      throw e;400    }401  }402  fs.writeFileSync(dstManifest, fs.readFileSync(srcManifest, "utf8"), "utf8");403  async.series([404    function (cb) { mkdirp(dstDir, cb); }.bind(this),405    function (cb) { this.adb.checkSdkBinaryPresent("aapt", cb); }.bind(this),406    function (cb) {407      this.adb.compileManifest(dstManifest, newPackage,408      this.args.appPackage, cb);409    }.bind(this),410    function (cb) {411      this.adb.insertManifest(dstManifest, serverPath,412        newServerPath, cb);413    }.bind(this)414  ], cb);415};416Selendroid.prototype.setLocation = androidController.setLocation;417Selendroid.prototype.removeApp = androidController.removeApp;418Selendroid.prototype.unpackApp = androidController.unpackApp;419Selendroid.prototype.translatePath = function (req) {420  var path = req.originalUrl;421  if (path.indexOf("contexts") !== -1) {422    logger.debug("Temporarily translating 'contexts' to 'window_handles");423    path = path.replace("contexts", "window_handles");424  } else if (path.indexOf("context") !== -1) {425    logger.debug("Temporarily translating 'context' to 'window'");426    path = path.replace("context", "window");427  }428  req.originalUrl = path;429};430Selendroid.prototype.extractStringsSelendroid = function (cb) {431  this.extractStrings(function () {432    cb();433  });434};435Selendroid.prototype.getStrings = function (language, stringFile, cb) {436  if (this.language && this.language === language) {437    // Return last strings438    return cb(null, {439      status: status.codes.Success.code,440      value: this.apkStrings441    });442  }443  // Extract and return strings444  return this.extractStrings(function () {445    cb(null, {446      status: status.codes.Success.code,447      value: this.apkStrings448    });449  }.bind(this), language);450};451_.extend(Selendroid.prototype, androidHybrid);452_.extend(Selendroid.prototype, androidContextController);453Selendroid.prototype.isChromedriverContext = function (windowName) {454  return windowName === this.CHROMIUM_WIN;455};456Selendroid.prototype.getContexts = function (cb) {457  var chromiumViews = [];458  this.listWebviews(function (err, webviews) {459    if (err) return cb(err);460    if (_.contains(webviews, this.CHROMIUM_WIN)) {461      chromiumViews = [this.CHROMIUM_WIN];462    } else {463      chromiumViews = [];464    }465    var selendroidViews = [];466    var reqUrl = this.selendroidHost + ':' + this.args.selendroidPort + '/wd/hub/session/' + this.selendroidSessionId;467    doRequest(reqUrl + '/window_handles', 'GET', {}, null, function (err, res) {468      if (err) return cb(err);469      selendroidViews = JSON.parse(res.body).value;470      this.contexts = _.union(selendroidViews, chromiumViews);471      logger.debug("Available contexts: " + JSON.stringify(this.contexts));472      cb(null, {sessionId: this.selendroidSessionId, status: status.codes.Success.code, value: this.contexts});473    }.bind(this));474  }.bind(this));475};476Selendroid.prototype.defaultWebviewName = function () {477  return this.WEBVIEW_WIN + "_0";478};479Selendroid.prototype.setValue = function (elementId, value, cb) {480  logger.debug('Setting text on element \'' + elementId + '\': \'' + value + '\'');481  for (var i = 0; i < value.length; i++) {482    var c = value.charCodeAt(i);483    // if we're using the unicode keyboard, and this is unicode, maybe encode484    if (this.args.unicodeKeyboard && (c > 127 || c === 38)) {485      // this is not simple ascii, or it is an ampersand (`&`)486      if (c >= parseInt("E000", 16) && c <= parseInt("E040", 16)) {487        // Selenium uses a Unicode PUA to cover certain special characters488        // see https://code.google.com/p/selenium/source/browse/java/client/src/org/openqa/selenium/Keys.java489      } else {490        // encode the text491        value = utf7.encode(value);492        break;493      }494    }495  }496  var reqUrl = this.proxyHost + ':' + this.proxyPort +497      '/wd/hub/session/' + this.proxySessionId +498      '/element/' + elementId + '/value';499  doRequest(reqUrl, 'POST', { value: [value] }, null, function (err) {500    if (err) return cb(err);501    cb(null, {502      status: status.codes.Success.code,503      value: ''504    });505  });506};507Selendroid.prototype.back = function (cb) {508  this.keyevent(4, null, cb);509};...

Full Screen

Full Screen

android.js

Source:android.js Github

copy

Full Screen

1"use strict";2var errors = require('../../server/errors.js')3  , path = require('path')4  , fs = require('fs')5  , Device = require('../device.js')6  , _ = require('underscore')7  , logger = require('../../server/logger.js').get('appium')8  , deviceCommon = require('../common.js')9  , status = require("../../server/status.js")10  , async = require('async')11  , androidController = require('./android-controller.js')12  , androidContextController = require('./android-context-controller.js')13  , androidCommon = require('./android-common.js')14  , androidHybrid = require('./android-hybrid.js')15  , UiAutomator = require('./uiautomator.js')16  , UnknownError = errors.UnknownError;17var Android = function () {18  this.init();19};20_.extend(Android.prototype, Device.prototype);21Android.prototype._deviceInit = Device.prototype.init;22Android.prototype.init = function () {23  this._deviceInit();24  this.appExt = ".apk";25  this.capabilities = {26    platform: 'LINUX'27  , browserName: 'Android'28  , platformVersion: '4.1'29  , webStorageEnabled: false30  , takesScreenshot: true31  , javascriptEnabled: true32  , databaseEnabled: false33  , networkConnectionEnabled: true34  , locationContextEnabled: false35  };36  this.args.devicePort = 4724;37  this.appMd5Hash = null;38  this.args.avd = null;39  this.args.language = null;40  this.args.locale = null;41  this.args.javaVersion = null;42  this.initQueue();43  this.implicitWaitMs = 0;44  this.shuttingDown = false;45  this.adb = null;46  this.uiautomator = null;47  this.uiautomatorRestartOnExit = false;48  this.uiautomatorIgnoreExit = false;49  this.swipeStepsPerSec = 28;50  this.dragStepsPerSec = 40;51  this.asyncWaitMs = 0;52  this.remote = null;53  this.contexts = [];54  this.curContext = this.defaultContext();55  this.didLaunch = false;56  this.launchCb = function () {};57  this.uiautomatorExitCb = function () {};58  this.dataDir = null;59  this.isProxy = false;60  this.proxyHost = null;61  this.proxyPort = null;62  this.proxySessionId = null;63  this.avoidProxy = [64    ['POST', new RegExp('^/wd/hub/session/[^/]+/context')]65  , ['GET', new RegExp('^/wd/hub/session/[^/]+/context')]66  , ['GET', new RegExp('^/wd/hub/session/[^/]+/contexts')]67  , ['POST', new RegExp('^/wd/hub/session/[^/]+/appium')]68  , ['GET', new RegExp('^/wd/hub/session/[^/]+/appium')]69  ];70  // listen for changes to ignoreUnimportantViews71  this.settings.on("update", function (update) {72    if (update.key === "ignoreUnimportantViews") {73      this.setCompressedLayoutHierarchy(update.value, update.callback);74    } else {75      update.callback();76    }77  }.bind(this));78};79Android.prototype._deviceConfigure = Device.prototype.configure;80Android.prototype.noLaunchSetup = function (cb) {81  logger.debug("Setting up Android for 'autoLaunch: false'");82  async.series([83    this.initJavaVersion.bind(this),84    this.initAdb.bind(this),85  ], function (err) { cb(err); });86};87Android.prototype.start = function (cb, onDie) {88  this.launchCb = cb;89  this.uiautomatorExitCb = onDie;90  logger.info("Starting android appium");91  async.series([92    this.initJavaVersion.bind(this),93    this.initAdb.bind(this),94    this.packageAndLaunchActivityFromManifest.bind(this),95    this.initUiautomator.bind(this),96    this.prepareDevice.bind(this),97    this.checkApiLevel.bind(this),98    this.pushStrings.bind(this),99    this.processFromManifest.bind(this),100    this.uninstallApp.bind(this),101    this.installAppForTest.bind(this),102    this.forwardPort.bind(this),103    //this.pushAppium.bind(this),104    this.initUnicode.bind(this),105    //this.pushSettingsApp.bind(this),106    //this.pushUnlock.bind(this),107    function (cb) {this.uiautomator.start(cb);}.bind(this),108    this.wakeUp.bind(this),109    this.unlock.bind(this),110    this.getDataDir.bind(this),111    this.setupCompressedLayoutHierarchy.bind(this),112    this.startAppUnderTest.bind(this),113    this.initAutoWebview.bind(this),114    this.setActualCapabilities.bind(this)115  ], function (err) {116    if (err) {117      this.shutdown(function () {118        this.launchCb(err);119      }.bind(this));120    } else {121      this.didLaunch = true;122      this.launchCb(null, this.proxySessionId);123    }124  }.bind(this));125};126Android.prototype.initUiautomator = function (cb) {127  if (this.uiautomator === null) {128    this.uiautomator = new UiAutomator(this.adb, this.args);129    this.uiautomator.setExitHandler(this.onUiautomatorExit.bind(this));130  }131  return cb();132};133Android.prototype.onLaunch = function (err) {134  var readyToGo = function () {135    this.didLaunch = true;136    this.launchCb();137  }.bind(this);138  var giveUp = function (err) {139    this.shutdown(function () {140      this.launchCb(err);141    }.bind(this));142  }.bind(this);143  if (err) {144    if (this.checkShouldRelaunch(err)) {145      logger.error(err);146      logger.error("Above error isn't fatal, maybe relaunching adb will help....");147      this.adb.waitForDevice(function (err) {148        if (err) return giveUp(err);149        readyToGo();150      });151    } else {152      giveUp(err);153    }154  } else {155    readyToGo();156  }157};158Android.prototype.restartUiautomator = function (cb) {159  async.series([160    this.forwardPort.bind(this)161    , this.uiautomator.start.bind(this.uiautomator)162    , this.setupCompressedLayoutHierarchy.bind(this)163  ], cb);164};165/*166 * Execute an arbitrary function and handle potential ADB disconnection before167 * proceeding168 */169Android.prototype.wrapActionAndHandleADBDisconnect = function (action, ocb) {170  async.series([171    function (cb) {172      this.uiautomatorIgnoreExit = true;173      action(cb);174    }.bind(this)175    , this.adb.restart.bind(this.adb)176    , this.restartUiautomator.bind(this)177  ], function (err) {178    this.uiautomatorIgnoreExit = false;179    ocb(err);180  }.bind(this));181};182Android.prototype.onUiautomatorExit = function () {183  logger.debug("UiAutomator exited");184  var respondToClient = function () {185    this.stopChromedriverProxies(function () {186      this.cleanup();187      if (!this.didLaunch) {188        var msg = "UiAutomator quit before it successfully launched";189        logger.error(msg);190        this.launchCb(new Error(msg));191        return;192      } else if (typeof this.cbForCurrentCmd === "function") {193        var error = new UnknownError("UiAutomator died while responding to " +194                                      "command, please check appium logs!");195        this.cbForCurrentCmd(error, null);196      }197      // make sure appium.js knows we crashed so it can clean up198      this.uiautomatorExitCb();199    }.bind(this));200  }.bind(this);201  if (this.adb) {202    var uninstall = function () {203      logger.debug("Attempting to uninstall app");204      this.uninstallApp(function () {205        this.shuttingDown = false;206        respondToClient();207      }.bind(this));208    }.bind(this);209    if (!this.uiautomatorIgnoreExit) {210      this.adb.ping(function (err, ok) {211        if (ok) {212          uninstall();213        } else {214          logger.debug(err);215          this.adb.restart(function (err) {216            if (err) {217              logger.debug(err);218            }219            if (this.uiautomatorRestartOnExit) {220              this.uiautomatorRestartOnExit = false;221              this.restartUiautomator(function (err) {222                if (err) {223                  logger.debug(err);224                  uninstall();225                }226              }.bind(this));227            } else {228              uninstall();229            }230          }.bind(this));231        }232      }.bind(this));233    } else {234      this.uiautomatorIgnoreExit = false;235    }236  } else {237    logger.debug("We're in uiautomator's exit callback but adb is gone already");238    respondToClient();239  }240};241Android.prototype.checkShouldRelaunch = function (launchErr) {242  if (launchErr.message === null || typeof launchErr.message === 'undefined') {243    logger.error("We're checking if we should relaunch based on something " +244                 "which isn't an error object. Check the codez!");245    return false;246  }247  var msg = launchErr.message.toString();248  var relaunchOn = [249    'Could not find a connected Android device'250    , 'Device did not become ready'251  ];252  var relaunch = false;253  _.each(relaunchOn, function (relaunchMsg) {254    relaunch = relaunch || msg.indexOf(relaunchMsg) !== -1;255  });256  return relaunch;257};258Android.prototype.checkApiLevel = function (cb) {259  this.adb.getApiLevel(function (err, apiLevel) {260    if (err) return cb(err);261    logger.info('Device API level is:', parseInt(apiLevel, 10));262    if (parseInt(apiLevel) < 17) {263      var msg = "Android devices must be of API level 17 or higher. Please change your device to Selendroid or upgrade Android on your device.";264      logger.error(msg); // logs the error when we encounter it265      return cb(new Error(msg)); // send the error up the chain266    }267    cb();268  });269};270Android.prototype.decorateChromeOptions = function (caps) {271  // add options from appium session caps272  if (this.args.chromeOptions) {273    _.each(this.args.chromeOptions, function (val, option) {274      if (typeof caps.chromeOptions[option] === "undefined") {275        caps.chromeOptions[option] = val;276      } else {277        logger.warn("Cannot pass option " + caps.chromeOptions[option] + " because Appium needs it to make chromeDriver work");278      }279    });280  }281  // add device id from adb282  caps.chromeOptions.androidDeviceSerial = this.adb.curDeviceId;283  return caps;284};285Android.prototype.processFromManifest = function (cb) {286  if (!this.args.app) {287    return cb();288  } else { // apk must be local to process the manifest.289    this.adb.processFromManifest(this.args.app, function (err, process) {290      var value = process || this.args.appPackage;291      this.appProcess = value;292      logger.debug("Set app process to: " + this.appProcess);293      cb();294    }.bind(this));295  }296};297Android.prototype.pushStrings = function (cb, language) {298  var outputPath = path.resolve(this.args.tmpDir, this.args.appPackage);299  var remotePath = '/data/local/tmp';300  var stringsJson = 'strings.json';301  this.extractStrings(function (err) {302    if (err) {303      if (!fs.existsSync(this.args.app)) {304        // apk doesn't exist locally so remove old strings.json305        logger.debug("Could not get strings, but it looks like we had an " +306                     "old strings file anyway, so ignoring");307        return this.adb.rimraf(remotePath + '/' + stringsJson, function (err) {308          if (err) return cb(new Error("Could not remove old strings"));309          cb();310        });311      } else {312        // if we can't get strings, just dump an empty json and continue313        logger.warn("Could not get strings, continuing anyway");314        var remoteFile = remotePath + '/' + stringsJson;315        return this.adb.shell("echo '{}' > " + remoteFile, cb);316      }317    }318    var jsonFile = path.resolve(outputPath, stringsJson);319    this.adb.push(jsonFile, remotePath, function (err) {320      if (err) return cb(new Error("Could not push strings.json"));321      cb();322    });323  }.bind(this), language);324};325Android.prototype.getStrings = function (language, stringFile, cb) {326  if (this.language && this.language === language) {327    // Return last strings328    return cb(null, {329      status: status.codes.Success.code,330      value: this.apkStrings331    });332  }333  // Extract, push and return strings334  return this.pushStrings(function () {335    this.proxy(["updateStrings", {}], function (err, res) {336      if (err || res.status !== status.codes.Success.code) return cb(err, res);337      cb(null, {338        status: status.codes.Success.code,339        value: this.apkStrings340      });341    }.bind(this));342  }.bind(this), language);343};344Android.prototype.pushAppium = function (cb) {345  logger.debug("Pushing appium bootstrap to device...");346  var binPath = path.resolve(__dirname, "..", "..", "..", "build",347      "android_bootstrap", "AppiumBootstrap.jar");348  fs.stat(binPath, function (err) {349    if (err) {350      cb(new Error("Could not find AppiumBootstrap.jar; please run " +351                   "'grunt buildAndroidBootstrap'"));352    } else {353      this.adb.push(binPath, this.remoteTempPath(), cb);354    }355  }.bind(this));356};357Android.prototype.startAppUnderTest = function (cb) {358  this.startApp(this.args, cb);359};360Android.prototype.startApp = function (args, cb) {361  if (args.androidCoverage) {362    this.adb.androidCoverage(args.androidCoverage, args.appWaitPackage,363      args.appWaitActivity, cb);364  } else {365    this.adb.startApp({366      pkg: args.appPackage,367      activity: args.appActivity,368      action: args.intentAction,369      category: args.intentCategory,370      flags: args.intentFlags,371      waitPkg: args.appWaitPackage,372      waitActivity: args.appWaitActivity,373      optionalIntentArguments: args.optionalIntentArguments,374      stopApp: args.stopAppOnReset || !args.dontStopAppOnReset375    }, cb);376  }377};378Android.prototype.stop = function (cb) {379  if (this.shuttingDown) {380    logger.debug("Already in process of shutting down.");381    return cb();382  }383  this.shuttingDown = true;384  var completeShutdown = function (cb) {385    if (this.adb) {386      this.adb.goToHome(function () {387        this.shutdown(cb);388      }.bind(this));389    } else {390      this.shutdown(cb);391    }392  }.bind(this);393  if (this.args.fullReset) {394    logger.debug("Removing app from device");395    this.uninstallApp(function (err) {396      if (err) {397        // simply warn on error here, because we don't want to stop the shutdown398        // process399        logger.warn(err);400      }401      completeShutdown(cb);402    });403  } else {404    completeShutdown(cb);405  }406};407Android.prototype.cleanup = function () {408  logger.debug("Cleaning up android objects");409  this.adb = null;410  this.uiautomator = null;411  this.shuttingDown = false;412};413Android.prototype.shutdown = function (cb) {414  var next = function () {415    this.stopChromedriverProxies(function () {416      if (this.uiautomator) {417        this.uiautomator.shutdown(function () {418          this.cleanup();419          cb();420        }.bind(this));421      } else {422        this.cleanup();423        cb();424      }425    }.bind(this));426  }.bind(this);427  if (this.adb) {428    this.adb.endAndroidCoverage();429    if (this.args.unicodeKeyboard && this.args.resetKeyboard && this.defaultIME) {430      logger.debug('Resetting IME to \'' + this.defaultIME + '\'');431      this.adb.setIME(this.defaultIME, function (err) {432        if (err) {433          // simply warn on error here, because we don't want to stop the shutdown434          // process435          logger.warn(err);436        }437        if (this.adb) {438          this.adb.stopLogcat(function () {439            next();440          }.bind(this));441        }442      }.bind(this));443    } else {444      this.adb.stopLogcat(function () {445        next();446      }.bind(this));447    }448  } else {449    next();450  }451};452Android.prototype.proxy = deviceCommon.proxy;453Android.prototype.respond = deviceCommon.respond;454Android.prototype.initQueue = function () {455  this.queue = async.queue(function (task, cb) {456    var action = task.action,457        params = task.params;458    this.cbForCurrentCmd = cb;459    if (this.adb && !this.shuttingDown) {460      this.uiautomator.sendAction(action, params, function (response) {461        this.cbForCurrentCmd = null;462        if (typeof cb === 'function') {463          this.respond(response, cb);464        }465      }.bind(this));466    } else {467      this.cbForCurrentCmd = null;468      var msg = "Tried to send command to non-existent Android device, " +469                 "maybe it shut down?";470      if (this.shuttingDown) {471        msg = "We're in the middle of shutting down the Android device, " +472              "so your request won't be executed. Sorry!";473      }474      this.respond({475        status: status.codes.UnknownError.code476      , value: msg477      }, cb);478    }479  }.bind(this), 1);480};481Android.prototype.push = function (elem) {482  this.queue.push({action: elem[0][0], params: elem[0][1] || {}}, elem[1]);483};484Android.prototype.wakeUp = function (cb) {485  // requires an appium bootstrap connection loaded486  logger.debug("Waking up device if it's not alive");487  this.proxy(["wake", {}], cb);488};489Android.prototype.getDataDir = function (cb) {490  this.proxy(["getDataDir", {}], function (err, res) {491    if (err) return cb(err);492    this.dataDir = res.value;493    logger.debug("dataDir set to: " + this.dataDir);494    cb();495  }.bind(this));496};497// Set CompressedLayoutHierarchy on the device based on current settings object498Android.prototype.setupCompressedLayoutHierarchy = function (cb) {499  // setup using cap500  if (_.has(this.args, 'ignoreUnimportantViews')) {501    // set the setting directly on the internal _settings object, this way we don't trigger an update event502    this.settings._settings.ignoreUnimportantViews = this.args.ignoreUnimportantViews;503  }504  if (_.isUndefined(this.getSetting("ignoreUnimportantViews"))) {505    return cb();506  }507  this.setCompressedLayoutHierarchy(this.getSetting("ignoreUnimportantViews"), cb);508};509// Set CompressedLayoutHierarchy on the device510Android.prototype.setCompressedLayoutHierarchy = function (compress, cb) {511  this.proxy(["compressedLayoutHierarchy", {compressLayout: compress}], cb);512};513Android.prototype.waitForActivityToStop = function (cb) {514  this.adb.waitForNotActivity(this.args.appWaitPackage, this.args.appWaitActivity, cb);515};516Android.prototype.setActualCapabilities = function (cb) {517  this.capabilities.deviceName = this.adb.udid || this.adb.curDeviceId;518  this.adb.shell("getprop ro.build.version.release", function (err, version) {519    if (err) {520      logger.warn(err);521    } else {522      logger.debug("Device is at release version " + version);523      this.capabilities.platformVersion = version;524    }525    return cb();526  }.bind(this));527};528Android.prototype.resetTimeout = deviceCommon.resetTimeout;529Android.prototype.waitForCondition = deviceCommon.waitForCondition;530Android.prototype.implicitWaitForCondition = deviceCommon.implicitWaitForCondition;531Android.prototype.getSettings = deviceCommon.getSettings;532Android.prototype.updateSettings = deviceCommon.updateSettings;533_.extend(Android.prototype, androidController);534_.extend(Android.prototype, androidContextController);535_.extend(Android.prototype, androidCommon);536_.extend(Android.prototype, androidHybrid);...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wd = require('wd');2var assert = require('assert');3var desired = require('./desired');4describe('android', function () {5  this.timeout(300000);6  var driver;7  before(function () {8    driver = wd.promiseChainRemote('localhost', 4723);9    require('../helpers/logging').configure(driver);10    return driver.init(desired);11  });12  after(function () {13    return driver.quit();14  });15  it('should find webview', function () {16      .initAutoWebview()17      .sleep(10000)18      .elementByTagName('h1')19      .text()20      .should.become('Hello, world!');21  });22});23module.exports = {

Full Screen

Using AI Code Generation

copy

Full Screen

1var wd = require('wd');2driver.init({3}, function() {4    driver.initAutoWebview(function() {5        driver.elementById("com.example.androidtestapp:id/textView1", function(err, el) {6            el.text(function(err, text) {7                console.log("Text: " + text);8            });9        });10    });11});

Full Screen

Using AI Code Generation

copy

Full Screen

1var autoWebview = new AutoWebview(driver);2autoWebview.initAutoWebview();3autoWebview.switchToWebview();4autoWebview.switchToNative();5autoWebview.switchToWebview();6autoWebview.switchToNative();7autoWebview.switchToWebview();8autoWebview.switchToNative();9autoWebview.switchToWebview();10autoWebview.switchToNative();

Full Screen

Using AI Code Generation

copy

Full Screen

1var driver = new webdriver.Builder()2    .forBrowser('chrome')3    .build();4driver.initAutoWebview();5driver.quit();6initAutoWebview: function() {7  var self = this;8  self.contexts().then(function(contexts) {9    var webviewContext = contexts.filter(function(context) {10      return context.indexOf('WEBVIEW') > -1;11    });12    if (webviewContext.length > 0) {13      self.context(webviewContext[0]);14    }15  });16}17var webdriver = require('selenium-webdriver');18var driver = new webdriver.Builder()19    .forBrowser('chrome')20    .build();21driver.initAutoWebview();22driver.quit();23initAutoWebview: function() {24  var self = this;25  self.contexts().then(function(contexts) {26    var webviewContext = contexts.filter(function(context) {27      return context.indexOf('WEBVIEW') > -1;28    });29    if (webviewContext.length > 0) {30      self.context(webviewContext[0]);31    }32  });33}

Full Screen

Using AI Code Generation

copy

Full Screen

1var webdriver = require('selenium-webdriver');2var driver = new webdriver.Builder()3.forBrowser('chrome')4.build();5driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');6driver.findElement(webdriver.By.name('btnG')).click();7driver.wait(webdriver.until.titleIs('webdriver - Google Search'), 1000);8driver.quit();9var webdriver = require('selenium-webdriver');10var driver = new webdriver.Builder()11.forBrowser('chrome')12.build();13driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');14driver.findElement(webdriver.By.name('btnG')).click();15driver.wait(webdriver.until.titleIs('webdriver - Google Search'), 1000);16driver.quit();17var webdriver = require('selenium-webdriver');18var driver = new webdriver.Builder()19.forBrowser('chrome')20.build();21driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');22driver.findElement(webdriver.By.name('btnG')).click();23driver.wait(webdriver.until.titleIs('webdriver - Google Search'), 1000);24driver.quit();25var webdriver = require('selenium-webdriver');26var driver = new webdriver.Builder()27.forBrowser('chrome')28.build();29driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');30driver.findElement(webdriver.By.name('btnG')).click();31driver.wait(webdriver.until.titleIs('webdriver - Google Search'), 1000);32driver.quit();33var webdriver = require('selenium-webdriver');34var driver = new webdriver.Builder()35.forBrowser('chrome')36.build();

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 Android Driver automation tests on LambdaTest cloud grid

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

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful