diff --git a/lib/appium.js b/lib/appium.js index 39566b0b135..90569da499f 100644 --- a/lib/appium.js +++ b/lib/appium.js @@ -275,7 +275,14 @@ Appium.prototype.invoke = function (cb) { if (this.device.args.autoLaunch === false) { // if user has passed in desiredCaps.autoLaunch = false // meaning they will manage app install / launching - cb(null, this.device); + if (typeof this.device.noLaunchSetup === "function") { + this.device.noLaunchSetup(function (err) { + if (err) return cb(err); + cb(null, this.device); + }.bind(this)); + } else { + cb(null, this.device); + } } else { // the normal case, where we launch the device for folks diff --git a/lib/devices/android/android-common.js b/lib/devices/android/android-common.js index 0f8ca38b0ac..14a1fa8e3bc 100644 --- a/lib/devices/android/android-common.js +++ b/lib/devices/android/android-common.js @@ -64,6 +64,11 @@ androidCommon.configureApp = function (cb) { // we have a package instead of app this.args.appPackage = this.args.app; this.args.app = null; + if (!this.args.appActivity) { + return cb(new Error("You passed in an app package as the 'app' " + + "capability, but didn't include appActivity. We " + + "need to know that too in order to start your app")); + } logger.debug("App is an Android package, will attempt to run on device"); cb(); } else { diff --git a/lib/devices/android/android.js b/lib/devices/android/android.js index 64a60de4c9d..a4c683d2d3c 100644 --- a/lib/devices/android/android.js +++ b/lib/devices/android/android.js @@ -76,50 +76,56 @@ Android.prototype.init = function () { Android.prototype._deviceConfigure = Device.prototype.configure; +Android.prototype.noLaunchSetup = function (cb) { + logger.debug("Setting up Android for 'autoLaunch: false'"); + this.adb = new ADB(this.args); + cb(); +}; + Android.prototype.start = function (cb, onDie) { this.launchCb = cb; this.uiautomatorExitCb = onDie; + logger.info("Starting android appium"); if (this.adb === null) { - // Pass Android opts and Android ref to adb. - logger.info("Starting android appium"); this.adb = new ADB(this.args); + } + + if (this.uiautomator === null) { this.uiautomator = new UiAutomator(this.adb, this.args); this.uiautomator.setExitHandler(this.onUiautomatorExit.bind(this)); - - logger.debug("Using fast reset? " + this.args.fastReset); - async.series([ - this.prepareDevice.bind(this), - this.packageAndLaunchActivityFromManifest.bind(this), - this.checkApiLevel.bind(this), - this.pushStrings.bind(this), - this.processFromManifest.bind(this), - this.uninstallApp.bind(this), - this.installAppForTest.bind(this), - this.forwardPort.bind(this), - this.pushAppium.bind(this), - this.initUnicode.bind(this), - this.pushSettingsApp.bind(this), - this.pushUnlock.bind(this), - this.uiautomator.start.bind(this.uiautomator), - this.wakeUp.bind(this), - this.unlockScreen.bind(this), - this.getDataDir.bind(this), - this.startApp.bind(this), - this.initAutoWebview.bind(this) - ], function (err) { - if (err) { - this.shutdown(function () { - this.launchCb(err); - }.bind(this)); - } else { - this.didLaunch = true; - this.launchCb(null, this.proxySessionId); - } - }.bind(this)); - } else { - logger.error("Tried to start ADB when we already have one running!"); } + + logger.debug("Using fast reset? " + this.args.fastReset); + async.series([ + this.prepareDevice.bind(this), + this.packageAndLaunchActivityFromManifest.bind(this), + this.checkApiLevel.bind(this), + this.pushStrings.bind(this), + this.processFromManifest.bind(this), + this.uninstallApp.bind(this), + this.installAppForTest.bind(this), + this.forwardPort.bind(this), + this.pushAppium.bind(this), + this.initUnicode.bind(this), + this.pushSettingsApp.bind(this), + this.pushUnlock.bind(this), + this.uiautomator.start.bind(this.uiautomator), + this.wakeUp.bind(this), + this.unlockScreen.bind(this), + this.getDataDir.bind(this), + this.startApp.bind(this), + this.initAutoWebview.bind(this) + ], function (err) { + if (err) { + this.shutdown(function () { + this.launchCb(err); + }.bind(this)); + } else { + this.didLaunch = true; + this.launchCb(null, this.proxySessionId); + } + }.bind(this)); }; Android.prototype.onLaunch = function (err) { diff --git a/lib/server/controller.js b/lib/server/controller.js index 95d26f68f77..695ddb58335 100644 --- a/lib/server/controller.js +++ b/lib/server/controller.js @@ -23,10 +23,6 @@ exports.getGlobalBeforeFilter = function (appium) { return function (req, res, next) { req.appium = appium; req.device = appium.device; - logger.debug("Appium request initiated at " + req.url); - if (typeof req.body === "object") { - logger.debug("Request received with params: " + JSON.stringify(req.body)); - } if (proxy.shouldProxy(req)) { if (req.appium.commandTimeout) { // if we're proxying, we never get into the sessionBeforeFilter, @@ -126,10 +122,14 @@ exports.isAppInstalled = function (req, res) { }; exports.launchApp = function (req, res) { - req.device.start(function () { - respondSuccess(req, res, "Successfully launched the [" + req.device.args.app + "] app."); + var onErr = function (err) { + respondError(req, res, "Unable to launch the app: " + err); + }; + req.device.start(function (err) { + if (err) return onErr(err); + respondSuccess(req, res, "Successfully launched the app."); }, function () { - respondError(req, res, "Unable to launch the [" + req.device.args.app + "] app."); + onErr(new Error("UiAutomator died")); }); }; diff --git a/test/functional/android/apidemos/basic-specs.js b/test/functional/android/apidemos/basic-specs.js index 7b7b7970e86..102f47838f3 100644 --- a/test/functional/android/apidemos/basic-specs.js +++ b/test/functional/android/apidemos/basic-specs.js @@ -12,6 +12,7 @@ var env = require('../../../helpers/env') , should = chai.should() , spawn = require('child_process').spawn , _ = require('underscore') + , ChaiAsserter = require('../../../helpers/asserter.js').ChaiAsserter , getAppPath = require('../../../helpers/app').getAppPath , androidReset = require('../../../helpers/reset').androidReset; @@ -291,4 +292,61 @@ describe("apidemo - basic @skip-ci", function () { }); + describe('appium android', function () { + this.timeout(env.MOCHA_INIT_TIMEOUT); + + var session; + var title = getTitle(this); + + before(function (done) { + var adb = new ADB({}); + adb.uninstallApk("io.appium.android.apis", done); + }); + + afterEach(function () { return session.tearDown(this.currentTest.state === 'passed'); }); + + it('should be able to start session without launching app', function (done) { + var appPath = path.resolve(desired.app); + var caps = _.defaults({'app': appPath, 'autoLaunch': false}, desired); + session = initSession(caps, desired); + var driver = session.setUp(title + "- autoLaunch"); + var activityToBeBlank = new ChaiAsserter(function (driver) { + return driver + .getCurrentActivity() + .should.eventually.not.include(".ApiDemos"); + }); + driver + .waitFor(activityToBeBlank, 10000, 700) + .launchApp() + .getCurrentActivity() + .should.eventually.include(".ApiDemos") + .nodeify(done); + }); + + it('should be able to start session without installing app', function (done) { + var appPath = path.resolve(desired.app); + var appPkg = "io.appium.android.apis"; + var caps = _.defaults({ + app: appPkg, + autoLaunch: false, + appActivity: ".ApiDemos" + }, desired); + session = initSession(caps, desired); + var driver = session.setUp(title + "- autoLaunch"); + var activityToBeBlank = new ChaiAsserter(function (driver) { + return driver + .getCurrentActivity() + .should.eventually.not.include(".ApiDemos"); + }); + driver + .waitFor(activityToBeBlank, 10000, 700) + .installApp(appPath) + .launchApp() + .getCurrentActivity() + .should.eventually.include(".ApiDemos") + .nodeify(done); + }); + + }); + });