rearchitect configuration methods

This commit is contained in:
Jonathan Lipps
2014-02-27 16:59:39 -08:00
parent bcf9d6c60e
commit 0cd14dd8fd
8 changed files with 197 additions and 182 deletions

View File

@@ -13,6 +13,12 @@ var routing = require('./server/routing.js')
, FirefoxOs = require('./devices/firefoxos/firefoxos.js')
, status = require("./server/status.js");
var DT_IOS = "ios"
, DT_ANDROID = "android"
, DT_SELENDROID = "selendroid"
, DT_MOCK_IOS = "mock_ios"
, DT_FIREFOX_OS = "firefoxos";
var Appium = function (args) {
this.args = args;
this.serverArgs = _.clone(args);
@@ -24,7 +30,6 @@ var Appium = function (args) {
this.desiredCapabilities = {};
this.oldDesiredCapabilities = {};
this.session = null;
this.tempFiles = [];
this.preLaunched = false;
this.fullReset = this.args.fullReset;
this.fastReset = !this.args.fullReset && !this.args.noReset;
@@ -34,7 +39,6 @@ var Appium = function (args) {
this.commandTimeoutMs = this.defCommandTimeoutMs;
this.origCommandTimeoutMs = this.commandTimeoutMs;
this.commandTimeout = null;
this.origAppPath = null;
};
Appium.prototype.attachTo = function (rest) {
@@ -76,31 +80,39 @@ Appium.prototype.start = function (desiredCaps, cb) {
}.bind(this));
};
Appium.prototype.getDeviceType = function (args, desiredCaps) {
Appium.prototype.getDeviceType = function (args, caps) {
if (args.ipa) {
return DT_IOS;
} else if (args.safari) {
return DT_IOS;
} else if (args.androidPackage || args.avd) {
return DT_ANDROID;
}
try {
return this.getDeviceTypeFromApp(args.app || caps.app);
} catch (e) {
return this.getDeviceTypeFromCaps(caps);
}
};
Appium.prototype.getDeviceTypeFromCaps = function (caps) {
var errMsg = "A valid device type is required in the capabilities list";
if (args.safari) {
return "ios";
} else if (args.forceIphone) {
return "ios";
} else if (args.forceIpad) {
return "ios";
}
if (desiredCaps.device) {
if (desiredCaps.device.toLowerCase().indexOf('iphone') !== -1) {
return "ios";
} else if (desiredCaps.device.toLowerCase().indexOf('ipad') !== -1) {
return "ios";
} else if (desiredCaps.device.toLowerCase().indexOf('selendroid') !== -1) {
return "selendroid";
} else if (desiredCaps.device.toLowerCase().indexOf('firefox') !== -1) {
return "firefoxos";
} else if (desiredCaps.device.toLowerCase().indexOf('android') !== -1) {
return "android";
} else if (desiredCaps.device === "mock_ios") {
return "mock_ios";
if (caps.device) {
if (caps.device.toLowerCase().indexOf('iphone') !== -1) {
return DT_IOS;
} else if (caps.device.toLowerCase().indexOf('ipad') !== -1) {
return DT_IOS;
} else if (caps.device.toLowerCase().indexOf('selendroid') !== -1) {
return DT_SELENDROID;
} else if (caps.device.toLowerCase().indexOf('firefox') !== -1) {
return DT_FIREFOX_OS;
} else if (caps.device.toLowerCase().indexOf('android') !== -1) {
return DT_ANDROID;
} else if (caps.device === DT_MOCK_IOS) {
return DT_MOCK_IOS;
} else {
throw new Error(errMsg);
}
}
@@ -108,32 +120,28 @@ Appium.prototype.getDeviceType = function (args, desiredCaps) {
};
Appium.prototype.getDeviceTypeFromApp = function (app) {
if (this.args.ipa) {
return "ios";
} else if (this.args.androidPackage || this.args.avd) {
return "android";
} else if (/\.app/.test(app)) {
return "ios";
if (/\.app/.test(app)) {
return DT_IOS;
} else if (/\.apk/.test(app)) {
return "android";
} else if ((app && app.toLowerCase() === "safari") || this.args.safari) {
return "ios";
return DT_ANDROID;
} else if ((app && app.toLowerCase() === "safari")) {
return DT_IOS;
} else if ((app && app.toLowerCase() === "settings")) {
return "ios";
return DT_IOS;
}
throw new Error("Could not determine your device type from --app");
throw new Error("Could not determine your device type from app '" + app + "'");
};
Appium.prototype.isMockIos = function () {
return this.deviceType === "mock_ios";
return this.deviceType === DT_MOCK_IOS;
};
Appium.prototype.isIos = function () {
return this.deviceType === "ios";
return this.deviceType === DT_IOS;
};
Appium.prototype.isAndroid = function () {
return this.deviceType === "android";
return this.deviceType === DT_ANDROID;
};
Appium.prototype.isChrome = function (args, caps) {
@@ -153,11 +161,11 @@ Appium.prototype.isChrome = function (args, caps) {
};
Appium.prototype.isSelendroid = function () {
return this.deviceType === "selendroid";
return this.deviceType === DT_SELENDROID;
};
Appium.prototype.isFirefoxOS = function () {
return this.deviceType === "firefoxos";
return this.deviceType === DT_FIREFOX_OS;
};
Appium.prototype.getAppExt = function () {
@@ -165,14 +173,9 @@ Appium.prototype.getAppExt = function () {
};
Appium.prototype.configure = function (args, desiredCaps, cb) {
this.origAppPath = null;
var deviceType;
try {
if (args.launch) {
deviceType = this.getDeviceTypeFromApp(args.app);
} else {
deviceType = this.getDeviceType(desiredCaps);
}
deviceType = this.getDeviceType(args, desiredCaps);
} catch (e) {
logger.error(e.message);
return cb(e);
@@ -195,7 +198,6 @@ Appium.prototype.configure = function (args, desiredCaps, cb) {
};
Appium.prototype.invoke = function (cb) {
if (this.sessionOverride || this.sessionId === null) {
this.sessionId = UUID.create().hex;
logger.info('Creating new appium session ' + this.sessionId);
@@ -239,20 +241,19 @@ Appium.prototype.invoke = function (cb) {
} else {
return cb(new Error("Requested a new session but one was in progress"));
}
};
Appium.prototype.getDevice = function (deviceType) {
var caps = this.desiredCapabilities;
var Device = (function () {
switch (deviceType) {
case "ios":
case DT_IOS:
return (caps.safari || caps.iwebview) ? Safari : IOS;
case "android":
case DT_ANDROID:
return this.isChrome(this.args, caps) ? Chrome : Android;
case "selendroid":
case DT_SELENDROID:
return Selendroid;
case "firefoxos":
case DT_FIREFOX_OS:
return FirefoxOs;
default:
throw new Error("Tried to start a device that doesn't exist: " +
@@ -261,13 +262,9 @@ Appium.prototype.getDevice = function (deviceType) {
});
return new Device();
if (deviceType === "ios") {
if (deviceType === DT_IOS) {
var iosOpts = {
rest: this.rest
, webSocket: this.webSocket
, app: this.args.app
, ipa: this.args.ipa
, bundleId: this.args.bundleId || this.desiredCapabilities.bundleId
, udid: this.args.udid
, fullReset: this.fullReset
@@ -294,7 +291,6 @@ Appium.prototype.getDevice = function (deviceType) {
, desiredCapabilities: this.desiredCapabilities
, logNoColors: this.args.logNoColors
, flakeyRetries: this.args.backendRetries
, origAppPath: this.origAppPath
, autoAcceptAlerts: this.desiredCapabilities.autoAcceptAlerts
, keepKeyChains: this.args.keepKeyChains || this.desiredCapabilities.keepKeyChains
};

View File

@@ -16,6 +16,60 @@ var logTypesSupported = {
var androidCommon = {};
androidCommon.configure = function (args, caps, cb) {
this._deviceConfigure(args, caps);
this.setAndroidArgs();
if (!this.args.androidActivity) {
return cb(new Error("You need to pass in the app-activity desired " +
"capability or server param"));
}
if (!this.args.androidPackage) {
return cb(new Error("You need to pass in the app-package desired " +
"capability or server param"));
}
if (this.args.app) {
this.configureApp(cb);
} else if (this.args.androidPackage) {
this.args.app = null;
logger.info("Didn't get app but did get Android package, will attempt to " +
"launch it on the device");
cb(null);
} else {
var msg = "No app set; either start appium with --app or pass in an 'app' " +
"value in desired capabilities, or set androidPackage to launch pre-" +
"existing app on device";
logger.error(msg);
cb(new Error(msg));
}
};
androidCommon.configureApp = function (args, caps, cb) {
this._deviceConfigureApp(args, caps, function (err) {
if (err) {
if (this.appIsPackageOrBundle(args.app)) {
// we have a package instead of app
this.args.androidPackage = args.app;
this.args.app = null;
logger.info("App is an Android package, will attempt to run on device");
return cb();
}
return cb(err);
}
cb();
}.bind(this));
};
androidCommon.setAndroidArgs = function () {
this.setArgFromCap("androidPackage", "app-package");
this.setArgFromCap("androidActivity", "app-activity");
this.setArgFromCap("androidWaitPackage", "app-wait-package");
this.setArgFromCap("androidWaitActivity", "app-wait-activity");
this.setArgFromCap("androidDeviceReadyTimeout", "device-ready-timeout");
this.setArgFromCap("androidCoverage", "androidCoverage");
this.setArgFromCap("compressXml", "compressXml");
};
androidCommon.background = function (secs, cb) {
this.adb.getFocusedPackageAndActivity(function (err, pack, activity) {
if (err) return cb(err);

View File

@@ -21,15 +21,15 @@ var errors = require('../../server/errors.js')
, UnknownError = errors.UnknownError;
var Android = function () {
this.init(opts);
this.init();
};
_.extend(Android.prototype, Device.prototype);
Android.prototype._device_init = Device.prototype.init;
Android.prototype._deviceInit = Device.prototype.init;
Android.prototype.init = function () {
this._device_init();
this._deviceInit();
this.appExt = ".apk";
this.compressXml = opts.compressXml;
this.skipUninstall = opts.fastReset || !opts.reset;
this.fastClear = opts.fastClear !== false;
@@ -91,70 +91,8 @@ Android.prototype.init = function () {
_.extend(this.capabilities, opts.desiredCapabilities);
};
Android.prototype.configure = function (args, caps, cb) {
args = this.setAndroidArgs(args, caps);
//chrome apps do not need this checking, native and normal apps do
var isChrome = args.app && _.contains(["chrome", "browser", "chromium"],
args.app.toLowerCase());
if (!isChrome) {
if (!args.androidActivity) {
return cb(new Error("You need to pass in the app-activity desired " +
"capability or server param"));
}
if (!args.androidPackage) {
return cb(new Error("You need to pass in the app-package desired " +
"capability or server param"));
}
}
this.setArgsAndCaps(args, caps);
if (args.app) {
this.configureApp(args, caps, cb);
} else if (args.androidPackage) {
this.args.app = null;
logger.info("Didn't get app but did get Android package, will attempt to " +
"launch it on the device");
cb(null);
} else {
var msg = "No app set; either start appium with --app or pass in an 'app' " +
"value in desired capabilities, or set androidPackage to launch pre-" +
"existing app on device";
logger.error(msg);
cb(new Error(msg));
}
};
Android.prototype._deviceConfigure = Device.prototype.configure;
Android.prototype._deviceConfigureApp = Device.prototype.configureApp;
Android.prototype.configureApp = function (args, caps, cb) {
this._deviceConfigureApp(args, caps, function (err) {
if (err) {
if (this.appIsPackageOrBundle(args.app)) {
// we have a package instead of app
this.args.androidPackage = args.app;
this.args.app = null;
logger.info("App is an Android package, will attempt to run on device");
return cb();
}
return cb(err);
}
cb();
}.bind(this));
};
Android.prototype.setAndroidArgs = function (args, caps) {
var setArgFromCaps = function (arg, cap) {
args[arg] = caps[cap] || args[arg];
};
setArgFromCaps("androidPackage", "app-package");
setArgFromCaps("androidActivity", "app-activity");
setArgFromCaps("androidWaitPackage", "app-wait-package");
setArgFromCaps("androidWaitActivity", "app-wait-activity");
setArgFromCaps("androidDeviceReadyTimeout", "device-ready-timeout");
setArgFromCaps("androidCoverage", "androidCoverage");
setArgFromCaps("compressXml", "compressXml");
return args;
};
Android.prototype.start = function (cb, onDie) {
this.launchCb = cb;

View File

@@ -20,11 +20,12 @@ _.extend(ChromeAndroid.prototype, Android.prototype);
ChromeAndroid.prototype.configure = function (args, caps, cb) {
logger.info("Looks like we want chrome on android");
this.setArgsAndCaps(args, caps);
if (args.app.toLowerCase() === "chromium") {
this._deviceConfigure(args, caps);
var app = this.appString();
if (app === "chromium") {
this.args.androidPackage = "org.chromium.chrome.testshell";
this.args.androidActivity = "org.chromium.chrome.testshell.Main";
} else if (args.app.toLowerCase() === "browser") {
} else if (app === "browser") {
this.args.androidPackage = "com.android.browser";
this.args.androidActivity = "com.android.browser.BrowserActivity";
} else {

View File

@@ -1,6 +1,8 @@
"use strict";
var ADB = require('./adb.js')
, Device = require('../device.js')
, Android = require('./android.js')
, mkdirp = require('mkdirp')
, _ = require('underscore')
, deviceCommon = require('../common.js')
@@ -17,7 +19,7 @@ var ADB = require('./adb.js')
, androidCommon = require('./android-common.js')
, path = require('path');
var Selendroid = function (opts) {
var Selendroid = function () {
this.opts = opts;
this.opts.devicePort = 8080;
this.skipUninstall = opts.fastReset || !opts.reset;
@@ -62,6 +64,10 @@ var Selendroid = function (opts) {
];
};
_.extend(Selendroid.prototype, Device.prototype);
Selendroid.prototype._deviceConfigure = Device.prototype.configure;
Selendroid.prototype._deviceConfigureApp = Device.prototype.configureApp;
Selendroid.prototype.start = function (cb) {
logger.info("Starting selendroid server");
this.adb = new ADB(this.opts);

View File

@@ -11,32 +11,52 @@ var fs = require('fs')
;
var Device = function () {
throw new Error("Cannot instantiate Device directly");
};
Device.prototype.init = function () {
this.appExt = null;
this.tempFiles = [];
this.args = {};
this.capabilities = {};
this.capOverrides = [
"app"
, "launchTimeout"
];
};
Device.prototype.setArgsAndCaps = function (args, caps) {
Device.prototype.configure = function (args, caps) {
_.each(this.capOverrides, function (cap) {
this.setArgFromCap(cap, cap);
}.bind(this));
_.extend(this.args, args);
_.extend(this.capabilities, caps);
};
Device.prototype.configureApp = function (args, caps, cb) {
if (this.appIsLocal(args.app)) {
this.configureLocalApp(args, caps, cb);
} else if (this.appIsDownloaded(args.app)) {
this.configureDownloadedApp(args, caps, cb);
} else {
cb(new Error("Bad app: " + args.app + ". Apps need to be absolute local " +
"path, URL to compressed file, or special app name"));
Device.prototype.setArgFromCap = function (arg, cap) {
if (typeof this.capabilities[cap] !== "undefined") {
this.args[arg] = this.capabilities[cap];
}
};
Device.prototype.configureLocalApp = function (args, caps, cb) {
var appPath = args.app;
var origin = caps.app ? "desired caps" : "command line";
Device.prototype.appString = function () {
return this.args.app ? this.args.app.toString().toLowerCase() : '';
};
Device.prototype.configureApp = function (cb) {
if (this.appIsLocal(this.args.app)) {
this.configureLocalApp(cb);
} else if (this.appIsDownloaded(this.args.app)) {
this.configureDownloadedApp(cb);
} else {
cb(new Error("Bad app: " + this.args.app + ". Apps need to be absolute " +
"local path, URL to compressed file, or special app name"));
}
};
Device.prototype.configureLocalApp = function (cb) {
var appPath = this.args.app;
var origin = this.capabilities.app ? "desired caps" : "command line";
var ext = appPath.substring(appPath.length - 4);
if (ext === this.appExt) {
this.args.app = appPath;
@@ -79,9 +99,9 @@ Device.prototype.appIsPackageOrBundle = function (app) {
return (/^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/).test(app);
};
Device.prototype.configureDownloadedApp = function (args, caps, cb) {
var origin = caps.app ? "desired caps" : "command line";
var appUrl = args.app;
Device.prototype.configureDownloadedApp = function (cb) {
var origin = this.capabilities.app ? "desired caps" : "command line";
var appUrl = this.args.app ? this.args.app : '';
if (appUrl.substring(appUrl.length - 4) === ".apk") {
try {
downloadFile(appUrl, ".apk", function (appPath) {

View File

@@ -48,24 +48,20 @@ var IOS = function () {
_.extend(IOS.prototype, Device.prototype);
IOS.prototype._device_init = Device.prototype.init;
IOS.prototype.init = function (args, caps) {
this._device_init();
this.args = {
app: null
};
IOS.prototype._deviceInit = Device.prototype.init;
IOS.prototype.init = function () {
this._deviceInit();
this.appExt = ".app";
this.capabilities = {
version: '0.0'
, webStorageEnabled: false
, locationContextEnabled: false
, browserName: 'iOS'
, platform: 'MAC'
, javascriptEnabled: true
, databaseEnabled: false
, takesScreenshot: true
};
this.origAppPath = args.origAppPath;
this.ipa = args.ipa;
version: '0.0'
, webStorageEnabled: false
, locationContextEnabled: false
, browserName: 'iOS'
, platform: 'MAC'
, javascriptEnabled: true
, databaseEnabled: false
, takesScreenshot: true
};
this.bundleId = args.bundleId || null;
this.udid = args.udid;
this.verbose = args.verbose;
@@ -122,10 +118,11 @@ IOS.prototype.init = function (args, caps) {
this.localizableStrings = {};
};
IOS.prototype._deviceConfigure = Device.prototype.configure;
IOS.prototype.configure = function (args, caps, cb) {
this.setArgsAndCaps(args, caps);
if (args.app) {
this.configureApp(args, caps, cb);
this._deviceConfigure(args, caps);
if (this.args.app) {
this.configureApp(cb);
} else {
var msg = "No app set; either start appium with --app or use 'app' cap";
logger.error(msg);
@@ -134,20 +131,21 @@ IOS.prototype.configure = function (args, caps, cb) {
};
IOS.prototype._deviceConfigureApp = Device.prototype.configureApp;
IOS.prototype.configureApp = function (args, caps, cb) {
IOS.prototype.configureApp = function (cb) {
this._deviceConfigureApp(function (err) {
var app = this.appString();
if (err) {
if (args.app.toLowerCase() === "iwebview") {
if (app === "iwebview") {
this.capabilities.iwebview = true;
this.args.app = path.resolve(__dirname,
"../../../build/WebViewApp/WebViewApp.app");
return this.configureLocalApp(this.args, this.capabilities, cb);
} else if (args.app.toLowerCase() === "settings") {
return this.configurePreferences(caps, cb);
} else if (this.appIsPackageOrBundle(args.app)) {
} else if (app === "settings") {
return this.configurePreferences(cb);
} else if (this.appIsPackageOrBundle(app)) {
// we have a bundle ID
logger.info("App is an iOS bundle, will attempt to run as pre-existing");
this.args.bundleId = args.app;
this.args.bundleId = app;
this.args.app = null;
return cb();
}
@@ -157,9 +155,10 @@ IOS.prototype.configureApp = function (args, caps, cb) {
}.bind(this));
};
IOS.prototype.configurePreferences = function (caps, cb) {
IOS.prototype.configurePreferences = function (cb) {
logger.info("Configuring settings app");
var prefsVer = null;
var caps = this.capabilities;
if (typeof caps.version !== "undefined" && caps.version) {
prefsVer = caps.version;
}
@@ -317,9 +316,9 @@ IOS.prototype.makeInstruments = function () {
IOS.prototype.onInstrumentsLaunch = function (cb) {
logger.info('Instruments launched. Starting poll loop for new commands.');
this.instruments.setDebug(true);
if (this.origAppPath) {
if (this.args.origAppPath) {
logger.info("Copying app back to its original place");
return ncp(this.args.app, this.origAppPath, cb);
return ncp(this.args.app, this.args.origAppPath, cb);
}
cb();
@@ -670,9 +669,9 @@ IOS.prototype.installToRealDevice = function (cb) {
if (this.udid) {
if (this.isSafariLauncherApp) {
this.installSafariLauncher(cb);
} else if (this.ipa && this.bundleId) {
} else if (this.args.ipa && this.bundleId) {
this.installIpa(cb);
} else if (this.ipa) {
} else if (this.args.ipa) {
var msg = "You specified a UDID and ipa but did not include the bundle " +
"id";
logger.error(msg);
@@ -727,7 +726,7 @@ IOS.prototype.getIDeviceObj = function () {
};
IOS.prototype.installIpa = function (cb) {
logger.info("Installing ipa found at " + this.ipa);
logger.info("Installing ipa found at " + this.args.ipa);
this.realDevice = this.getIDeviceObj();
var d = this.realDevice;
async.waterfall([
@@ -741,7 +740,7 @@ IOS.prototype.installIpa = function (cb) {
cb();
}
}.bind(this),
function (cb) { d.installAndWait(this.ipa, this.bundleId, cb); }.bind(this)
function (cb) { d.installAndWait(this.args.ipa, this.bundleId, cb); }.bind(this)
], cb);
};

View File

@@ -18,21 +18,22 @@ _.extend(Safari.prototype, IOS.prototype);
Safari.prototype.configure = function (args, caps, cb) {
logger.info("Configuring Safari session");
this.setArgsAndCaps(args, caps);
this._deviceConfigure(args, caps);
this.capabilities.safari = true;
if (args.udid) {
if (this.args.udid) {
this.dontCleanupSession = true;
this.args.isSafariLauncherApp = true;
this.args.app = path.resolve(__dirname,
"../../../build/SafariLauncher/SafariLauncher.zip");
this.configureLocalApp(this.args, this.capabilities, cb);
this.configureLocalApp(cb);
} else {
this.configureSafari(this.capabilities, cb);
this.configureSafari(cb);
}
};
Safari.prototype.configureSafari = function (caps, cb) {
Safari.prototype.configureSafari = function (cb) {
var safariVer = null;
var caps = this.capabilities;
if (typeof caps.version !== "undefined" && caps.version) {
safariVer = caps.version;
}