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