mirror of
https://github.com/appium/appium.git
synced 2026-02-15 06:20:22 -06:00
398
android/adb.js
398
android/adb.js
@@ -104,7 +104,7 @@ ADB.prototype.checkSdkBinaryPresent = function(binary, cb) {
|
||||
this.binaries[binary] = binaryLoc;
|
||||
cb(null, binaryLoc);
|
||||
} else {
|
||||
exec("which " + binary, { maxBuffer: 524288 }, _.bind(function(err, stdout) {
|
||||
exec("which " + binary, { maxBuffer: 524288 }, function(err, stdout) {
|
||||
if (stdout) {
|
||||
this.debug("Using " + binary + " from " + stdout);
|
||||
cb(null, stdout);
|
||||
@@ -113,16 +113,16 @@ ADB.prototype.checkSdkBinaryPresent = function(binary, cb) {
|
||||
"SDK installed?"),
|
||||
null);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
ADB.prototype.checkAdbPresent = function(cb) {
|
||||
this.checkSdkBinaryPresent("adb", _.bind(function(err, binaryLoc) {
|
||||
this.checkSdkBinaryPresent("adb", function(err, binaryLoc) {
|
||||
if (err) return cb(err);
|
||||
this.adb = binaryLoc.trim();
|
||||
cb(null);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.checkAppPresent = function(cb) {
|
||||
@@ -132,7 +132,7 @@ ADB.prototype.checkAppPresent = function(cb) {
|
||||
cb(null);
|
||||
} else {
|
||||
logger.info("Checking whether app is actually present");
|
||||
fs.stat(this.apkPath, _.bind(function(err) {
|
||||
fs.stat(this.apkPath, function(err) {
|
||||
if (err) {
|
||||
logger.error("Could not find app apk at " + this.apkPath);
|
||||
cb(new Error("Error locating the app apk, supposedly it's at " +
|
||||
@@ -141,7 +141,7 @@ ADB.prototype.checkAppPresent = function(cb) {
|
||||
} else {
|
||||
cb(null);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -149,10 +149,9 @@ ADB.prototype.checkAppPresent = function(cb) {
|
||||
ADB.prototype.buildFastReset = function(skipAppSign, cb) {
|
||||
logger.info("Building fast reset");
|
||||
// Create manifest
|
||||
var me = this
|
||||
, targetAPK = me.apkPath
|
||||
var targetAPK = this.apkPath
|
||||
, cleanAPKSrc = path.resolve(__dirname, '..', 'app', 'android', 'Clean.apk')
|
||||
, newPackage = me.appPackage + '.clean'
|
||||
, newPackage = this.appPackage + '.clean'
|
||||
, srcManifest = path.resolve(__dirname, '..', 'app', 'android',
|
||||
'AndroidManifest.xml.src')
|
||||
, dstManifest = path.resolve(getTempPath(), 'AndroidManifest.xml');
|
||||
@@ -160,29 +159,28 @@ ADB.prototype.buildFastReset = function(skipAppSign, cb) {
|
||||
fs.writeFileSync(dstManifest, fs.readFileSync(srcManifest, "utf8"), "utf8");
|
||||
var resignApks = function(cb) {
|
||||
// Resign clean apk and target apk
|
||||
var apks = [ me.cleanAPK ];
|
||||
var apks = [ this.cleanAPK ];
|
||||
if (!skipAppSign) {
|
||||
logger.debug("Signing app and clean apk.");
|
||||
apks.push(targetAPK);
|
||||
} else {
|
||||
logger.debug("Skip app sign. Sign clean apk.");
|
||||
}
|
||||
me.sign(apks, cb);
|
||||
};
|
||||
this.sign(apks, cb);
|
||||
}.bind(this);
|
||||
|
||||
async.series([
|
||||
function(cb) { me.checkSdkBinaryPresent("aapt", cb); },
|
||||
function(cb) { me.compileManifest(dstManifest, newPackage, me.appPackage, cb); },
|
||||
function(cb) { me.insertManifest(dstManifest, cleanAPKSrc, me.cleanAPK, cb); },
|
||||
function(cb) { resignApks(cb); }
|
||||
function(cb) { this.checkSdkBinaryPresent("aapt", cb); }.bind(this),
|
||||
function(cb) { this.compileManifest(dstManifest, newPackage, this.appPackage, cb); }.bind(this),
|
||||
function(cb) { this.insertManifest(dstManifest, cleanAPKSrc, this.cleanAPK, cb); }.bind(this),
|
||||
function(cb) { resignApks(cb); }.bind(this)
|
||||
], cb);
|
||||
};
|
||||
|
||||
ADB.prototype.insertSelendroidManifest = function(serverPath, cb) {
|
||||
logger.info("Inserting selendroid manifest");
|
||||
var me = this
|
||||
, newServerPath = me.selendroidServerPath
|
||||
, newPackage = me.appPackage + '.selendroid'
|
||||
var newServerPath = this.selendroidServerPath
|
||||
, newPackage = this.appPackage + '.selendroid'
|
||||
, srcManifest = path.resolve(__dirname, '..', 'build', 'selendroid',
|
||||
'AndroidManifest.xml')
|
||||
, dstDir = path.resolve(getTempPath(), this.appPackage)
|
||||
@@ -197,11 +195,11 @@ ADB.prototype.insertSelendroidManifest = function(serverPath, cb) {
|
||||
}
|
||||
fs.writeFileSync(dstManifest, fs.readFileSync(srcManifest, "utf8"), "utf8");
|
||||
async.series([
|
||||
function(cb) { mkdirp(dstDir, cb); },
|
||||
function(cb) { me.checkSdkBinaryPresent("aapt", cb); },
|
||||
function(cb) { me.compileManifest(dstManifest, newPackage, me.appPackage, cb); },
|
||||
function(cb) { me.insertManifest(dstManifest, serverPath, newServerPath,
|
||||
cb); }
|
||||
function(cb) { mkdirp(dstDir, cb); }.bind(this),
|
||||
function(cb) { this.checkSdkBinaryPresent("aapt", cb); }.bind(this),
|
||||
function(cb) { this.compileManifest(dstManifest, newPackage, this.appPackage, cb); }.bind(this),
|
||||
function(cb) { this.insertManifest(dstManifest, serverPath, newServerPath,
|
||||
cb); }.bind(this)
|
||||
], cb);
|
||||
};
|
||||
|
||||
@@ -327,7 +325,6 @@ ADB.prototype.signDefault = function(apks, cb) {
|
||||
|
||||
// apk is a single apk path
|
||||
ADB.prototype.signCustom = function(apk, cb) {
|
||||
var me = this;
|
||||
var jarsigner = path.resolve(process.env.JAVA_HOME, 'bin', 'jarsigner');
|
||||
jarsigner = isWindows ? '"' + jarsigner + '.exe"' : '"' + jarsigner + '"';
|
||||
var java = path.resolve(process.env.JAVA_HOME, 'bin', 'java');
|
||||
@@ -337,13 +334,13 @@ ADB.prototype.signCustom = function(apk, cb) {
|
||||
// "jarsigner" "blank.apk" -sigalg MD5withRSA -digestalg SHA1
|
||||
// -keystore "./key.keystore" -storepass "android"
|
||||
// -keypass "android" "androiddebugkey"
|
||||
if (!fs.existsSync(me.keystorePath)) {
|
||||
return cb(new Error("Keystore doesn't exist. " + me.keystorePath));
|
||||
if (!fs.existsSync(this.keystorePath)) {
|
||||
return cb(new Error("Keystore doesn't exist. " + this.keystorePath));
|
||||
}
|
||||
|
||||
var sign = [jarsigner, '"' + apk + '"', '-sigalg MD5withRSA', '-digestalg SHA1',
|
||||
'-keystore "' + me.keystorePath + '"', '-storepass "' + me.keystorePassword + '"',
|
||||
'-keypass "' + me.keyPassword + '"', '"' + me.keyAlias + '"'].join(' ');
|
||||
'-keystore "' + this.keystorePath + '"', '-storepass "' + this.keystorePassword + '"',
|
||||
'-keypass "' + this.keyPassword + '"', '"' + this.keyAlias + '"'].join(' ');
|
||||
logger.debug("Unsigning apk with: " + unsign);
|
||||
exec(unsign, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
||||
if (stderr) {
|
||||
@@ -367,13 +364,12 @@ ADB.prototype.signCustom = function(apk, cb) {
|
||||
|
||||
// apks is an array of strings.
|
||||
ADB.prototype.sign = function(apks, cb) {
|
||||
var me = this;
|
||||
if (me.useKeystore) {
|
||||
async.each(apks, me.signCustom.bind(me), function(err) {
|
||||
if (this.useKeystore) {
|
||||
async.each(apks, this.signCustom.bind(this), function(err) {
|
||||
cb(err);
|
||||
});
|
||||
} else {
|
||||
me.signDefault(apks, cb);
|
||||
this.signDefault(apks, cb);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -391,13 +387,12 @@ ADB.prototype.checkApkCert = function(apk, cb) {
|
||||
var keytool = path.resolve(process.env.JAVA_HOME, 'bin', 'keytool');
|
||||
keytool = isWindows ? '"' + keytool + '.exe"' : '"' + keytool + '"';
|
||||
var keystoreHash = null;
|
||||
var me = this;
|
||||
|
||||
var checkKeystoreMD5 = function(innerCb) {
|
||||
logger.debug("checkKeystoreMD5");
|
||||
// get keystore md5
|
||||
var keystore = [keytool, '-v', '-list', '-alias "' + me.keyAlias + '"',
|
||||
'-keystore "' + me.keystorePath + '"', '-storepass "' + me.keystorePassword + '"'].join(' ');
|
||||
var keystore = [keytool, '-v', '-list', '-alias "' + this.keyAlias + '"',
|
||||
'-keystore "' + this.keystorePath + '"', '-storepass "' + this.keystorePassword + '"'].join(' ');
|
||||
logger.debug("Printing keystore md5: " + keystore);
|
||||
exec(keystore, { maxBuffer: 524288 }, function(err, stdout) {
|
||||
keystoreHash = md5.exec(stdout);
|
||||
@@ -420,7 +415,7 @@ ADB.prototype.checkApkCert = function(apk, cb) {
|
||||
entry = entry.entryName;
|
||||
if (!rsa.test(entry)) return next();
|
||||
logger.debug("Entry: " + entry);
|
||||
var entryPath = path.join(getTempPath(), me.appPackage, 'cert');
|
||||
var entryPath = path.join(getTempPath(), this.appPackage, 'cert');
|
||||
logger.debug("entryPath: " + entryPath);
|
||||
var entryFile = path.join(entryPath, entry);
|
||||
logger.debug("entryFile: " + entryFile);
|
||||
@@ -446,7 +441,7 @@ ADB.prototype.checkApkCert = function(apk, cb) {
|
||||
next();
|
||||
}
|
||||
});
|
||||
};
|
||||
}.bind(this);
|
||||
next();
|
||||
};
|
||||
|
||||
@@ -490,70 +485,66 @@ ADB.prototype.checkFastReset = function(cb) {
|
||||
|
||||
if (!this.appPackage) return cb(new Error("appPackage must be set."));
|
||||
|
||||
var me = this;
|
||||
me.checkApkCert(me.cleanAPK, function(cleanSigned){
|
||||
me.checkApkCert(me.apkPath, function(appSigned){
|
||||
logger.debug("App signed? " + appSigned + " " + me.apkPath);
|
||||
this.checkApkCert(this.cleanAPK, function(cleanSigned){
|
||||
this.checkApkCert(this.apkPath, function(appSigned){
|
||||
logger.debug("App signed? " + appSigned + " " + this.apkPath);
|
||||
// Only build & resign clean.apk if it doesn't exist or isn't signed.
|
||||
if (!fs.existsSync(me.cleanAPK) || !cleanSigned) {
|
||||
me.buildFastReset(appSigned, function(err){ if (err) return cb(err); cb(null); });
|
||||
if (!fs.existsSync(this.cleanAPK) || !cleanSigned) {
|
||||
this.buildFastReset(appSigned, function(err){ if (err) return cb(err); cb(null); });
|
||||
} else {
|
||||
if (!appSigned) {
|
||||
// Resign app apk because it's not signed.
|
||||
me.sign([me.apkPath], cb);
|
||||
this.sign([this.apkPath], cb);
|
||||
} else {
|
||||
// App and clean are already existing and signed.
|
||||
cb(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.getDeviceWithRetry = function(cb) {
|
||||
logger.info("Trying to find a connected android device");
|
||||
var me = this;
|
||||
var getDevices = function(innerCb) {
|
||||
me.getConnectedDevices(function(err, devices) {
|
||||
this.getConnectedDevices(function(err, devices) {
|
||||
if (typeof devices === "undefined" || devices.length === 0 || err) {
|
||||
return innerCb(new Error("Could not find a connected Android device."));
|
||||
}
|
||||
innerCb(null);
|
||||
});
|
||||
};
|
||||
}.bind(this);
|
||||
getDevices(function(err) {
|
||||
if (err) {
|
||||
logger.info("Could not find devices, restarting adb server...");
|
||||
me.restartAdb(function() {
|
||||
this.restartAdb(function() {
|
||||
getDevices(cb);
|
||||
});
|
||||
} else {
|
||||
logger.info("Found device, no need to retry");
|
||||
cb(null);
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.prepareDevice = function(onReady) {
|
||||
logger.info("Preparing device for session");
|
||||
var me = this;
|
||||
async.series([
|
||||
function(cb) { me.checkAppPresent(cb); },
|
||||
function(cb) { me.checkAdbPresent(cb); },
|
||||
function(cb) { me.prepareEmulator(cb); },
|
||||
function(cb) { me.getDeviceWithRetry(cb);},
|
||||
function(cb) { me.waitForDevice(cb); },
|
||||
function(cb) { me.checkFastReset(cb); }
|
||||
function(cb) { this.checkAppPresent(cb); }.bind(this),
|
||||
function(cb) { this.checkAdbPresent(cb); }.bind(this),
|
||||
function(cb) { this.prepareEmulator(cb); }.bind(this),
|
||||
function(cb) { this.getDeviceWithRetry(cb);}.bind(this),
|
||||
function(cb) { this.waitForDevice(cb); }.bind(this),
|
||||
function(cb) { this.checkFastReset(cb); }.bind(this)
|
||||
], onReady);
|
||||
};
|
||||
|
||||
ADB.prototype.pushStrings = function(cb) {
|
||||
var me = this;
|
||||
var remotePath = '/data/local/tmp';
|
||||
var stringsJson = 'strings.json';
|
||||
if (!fs.existsSync(me.apkPath)) {
|
||||
if (!fs.existsSync(this.apkPath)) {
|
||||
// apk doesn't exist locally so remove old strings.json
|
||||
var pushCmd = me.adbCmd + ' shell rm ' + remotePath + '/' + stringsJson;
|
||||
var pushCmd = this.adbCmd + ' shell rm ' + remotePath + '/' + stringsJson;
|
||||
logger.debug("Apk doesn't exist. Removing old strings.json " + pushCmd);
|
||||
exec(pushCmd, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
||||
cb(null);
|
||||
@@ -561,9 +552,9 @@ ADB.prototype.pushStrings = function(cb) {
|
||||
} else {
|
||||
var stringsFromApkJarPath = path.resolve(__dirname, '..', 'app', 'android',
|
||||
'strings_from_apk.jar');
|
||||
var outputPath = path.resolve(getTempPath(), me.appPackage);
|
||||
var outputPath = path.resolve(getTempPath(), this.appPackage);
|
||||
var makeStrings = ['java -jar "', stringsFromApkJarPath,
|
||||
'" "', me.apkPath, '" "', outputPath, '"'].join('');
|
||||
'" "', this.apkPath, '" "', outputPath, '"'].join('');
|
||||
logger.debug(makeStrings);
|
||||
exec(makeStrings, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
||||
if (err) {
|
||||
@@ -572,32 +563,31 @@ ADB.prototype.pushStrings = function(cb) {
|
||||
}
|
||||
var jsonFile = path.resolve(outputPath, stringsJson);
|
||||
|
||||
var pushCmd = me.adbCmd + ' push "' + jsonFile + '" ' + remotePath;
|
||||
var pushCmd = this.adbCmd + ' push "' + jsonFile + '" ' + remotePath;
|
||||
exec(pushCmd, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
||||
cb(null);
|
||||
});
|
||||
});
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
ADB.prototype.startAppium = function(onReady, onExit) {
|
||||
logger.info("Starting android appium");
|
||||
var me = this;
|
||||
this.onExit = onExit;
|
||||
|
||||
logger.debug("Using fast reset? " + this.fastReset);
|
||||
|
||||
async.series([
|
||||
function(cb) { me.prepareDevice(cb); },
|
||||
function(cb) { me.pushStrings(cb); },
|
||||
function(cb) { me.uninstallApp(cb); },
|
||||
function(cb) { me.installApp(cb); },
|
||||
function(cb) { me.forwardPort(cb); },
|
||||
function(cb) { me.pushAppium(cb); },
|
||||
function(cb) { me.runBootstrap(cb, onExit); },
|
||||
function(cb) { me.wakeUp(cb); },
|
||||
function(cb) { me.unlockScreen(cb); },
|
||||
function(cb) { me.startApp(cb); }
|
||||
function(cb) { this.prepareDevice(cb); }.bind(this),
|
||||
function(cb) { this.pushStrings(cb); }.bind(this),
|
||||
function(cb) { this.uninstallApp(cb); }.bind(this),
|
||||
function(cb) { this.installApp(cb); }.bind(this),
|
||||
function(cb) { this.forwardPort(cb); }.bind(this),
|
||||
function(cb) { this.pushAppium(cb); }.bind(this),
|
||||
function(cb) { this.runBootstrap(cb, onExit); }.bind(this),
|
||||
function(cb) { this.wakeUp(cb); }.bind(this),
|
||||
function(cb) { this.unlockScreen(cb); }.bind(this),
|
||||
function(cb) { this.startApp(cb); }.bind(this)
|
||||
], function(err, seriesInfo) {
|
||||
onReady(err);
|
||||
});
|
||||
@@ -605,30 +595,28 @@ ADB.prototype.startAppium = function(onReady, onExit) {
|
||||
|
||||
ADB.prototype.startChrome = function(onReady) {
|
||||
logger.info("Starting Chrome");
|
||||
var me = this;
|
||||
logger.debug("Using fast reset? " + this.fastReset);
|
||||
|
||||
async.series([
|
||||
function(cb) { me.prepareDevice(cb); },
|
||||
function(cb) { me.installApp(cb); },
|
||||
function(cb) { me.startApp(cb); }
|
||||
function(cb) { this.prepareDevice(cb); }.bind(this),
|
||||
function(cb) { this.installApp(cb); }.bind(this),
|
||||
function(cb) { this.startApp(cb); }.bind(this)
|
||||
], onReady);
|
||||
};
|
||||
|
||||
ADB.prototype.startSelendroid = function(serverPath, onReady) {
|
||||
logger.info("Starting selendroid");
|
||||
var me = this
|
||||
, modServerExists = false
|
||||
var modServerExists = false
|
||||
, modAppPkg = this.appPackage + '.selendroid';
|
||||
this.selendroidServerPath = path.resolve(getTempPath(),
|
||||
'selendroid.' + this.appPackage + '.apk');
|
||||
|
||||
var checkModServerExists = function(cb) {
|
||||
fs.stat(me.selendroidServerPath, function(err) {
|
||||
fs.stat(this.selendroidServerPath, function(err) {
|
||||
modServerExists = !err;
|
||||
cb();
|
||||
});
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
var conditionalUninstallSelendroid = function(cb) {
|
||||
if (!modServerExists) {
|
||||
@@ -636,48 +624,48 @@ ADB.prototype.startSelendroid = function(serverPath, onReady) {
|
||||
// need to uninstall it if it's already on device
|
||||
logger.info("Rebuilt selendroid apk does not exist, uninstalling " +
|
||||
"any instances of it on device to make way for new one");
|
||||
me.uninstallApk(modAppPkg, cb);
|
||||
this.uninstallApk(modAppPkg, cb);
|
||||
} else {
|
||||
logger.info("Rebuilt selendroid apk exists, doing nothing");
|
||||
cb();
|
||||
}
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
var conditionalInsertManifest = function(cb) {
|
||||
if (!modServerExists) {
|
||||
logger.info("Rebuilt selendroid server does not exist, inserting " +
|
||||
"modified manifest");
|
||||
me.insertSelendroidManifest(serverPath, cb);
|
||||
this.insertSelendroidManifest(serverPath, cb);
|
||||
} else {
|
||||
logger.info("Rebuilt selendroid server already exists, no need to " +
|
||||
"rebuild it with a new manifest");
|
||||
cb();
|
||||
}
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
var conditionalInstallSelendroid = function(cb) {
|
||||
me.checkAppInstallStatus(modAppPkg, function(e, installed) {
|
||||
this.checkAppInstallStatus(modAppPkg, function(e, installed) {
|
||||
if (!installed) {
|
||||
logger.info("Rebuilt selendroid is not installed, installing it");
|
||||
me.installApk(me.selendroidServerPath, cb);
|
||||
this.installApk(this.selendroidServerPath, cb);
|
||||
} else {
|
||||
logger.info("Rebuilt selendroid is already installed");
|
||||
cb();
|
||||
}
|
||||
});
|
||||
};
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
|
||||
async.series([
|
||||
function(cb) { me.prepareDevice(cb); },
|
||||
function(cb) { this.prepareDevice(cb); }.bind(this),
|
||||
function(cb) { checkModServerExists(cb); },
|
||||
function(cb) { conditionalUninstallSelendroid(cb); },
|
||||
function(cb) { conditionalInsertManifest(cb); },
|
||||
function(cb) { me.checkSelendroidCerts(me.selendroidServerPath, cb); },
|
||||
function(cb) { this.checkSelendroidCerts(this.selendroidServerPath, cb); }.bind(this),
|
||||
function(cb) { conditionalInstallSelendroid(cb); },
|
||||
function(cb) { me.installApp(cb); },
|
||||
function(cb) { me.forwardPort(cb); },
|
||||
function(cb) { me.unlockScreen(cb); },
|
||||
function(cb) { me.pushSelendroid(cb); },
|
||||
function(cb) { this.installApp(cb); }.bind(this),
|
||||
function(cb) { this.forwardPort(cb); }.bind(this),
|
||||
function(cb) { this.unlockScreen(cb); }.bind(this),
|
||||
function(cb) { this.pushSelendroid(cb); }.bind(this),
|
||||
function(cb) { logger.info("Selendroid server is launching"); cb(); }
|
||||
], onReady);
|
||||
};
|
||||
@@ -705,8 +693,7 @@ ADB.prototype.pushSelendroid = function(cb) {
|
||||
};
|
||||
|
||||
ADB.prototype.checkSelendroidCerts = function(serverPath, cb) {
|
||||
var me = this
|
||||
, alreadyReturned = false
|
||||
var alreadyReturned = false
|
||||
, checks = 0;
|
||||
|
||||
var onDoneSigning = function() {
|
||||
@@ -720,22 +707,21 @@ ADB.prototype.checkSelendroidCerts = function(serverPath, cb) {
|
||||
var apks = [serverPath, this.apkPath];
|
||||
_.each(apks, function(apk) {
|
||||
logger.info("Checking signed status of " + apk);
|
||||
me.checkApkCert(apk, function(isSigned) {
|
||||
this.checkApkCert(apk, function(isSigned) {
|
||||
if (isSigned) return onDoneSigning();
|
||||
me.sign([apk], function(err) {
|
||||
this.sign([apk], function(err) {
|
||||
if (err && !alreadyReturned) {
|
||||
alreadyReturned = true;
|
||||
return cb(err);
|
||||
}
|
||||
onDoneSigning();
|
||||
});
|
||||
});
|
||||
});
|
||||
}.bind(this));
|
||||
}, this);
|
||||
};
|
||||
|
||||
ADB.prototype.getEmulatorPort = function(cb) {
|
||||
logger.info("Getting running emulator port");
|
||||
var me = this;
|
||||
if (this.emulatorPort !== null) {
|
||||
return cb(null, this.emulatorPort);
|
||||
}
|
||||
@@ -744,14 +730,14 @@ ADB.prototype.getEmulatorPort = function(cb) {
|
||||
cb(new Error("No devices connected"));
|
||||
} else {
|
||||
// pick first device
|
||||
var port = me.getPortFromEmulatorString(devices[0]);
|
||||
var port = this.getPortFromEmulatorString(devices[0]);
|
||||
if (port) {
|
||||
cb(null, port);
|
||||
} else {
|
||||
cb(new Error("Emulator port not found"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.getPortFromEmulatorString = function(emStr) {
|
||||
@@ -769,18 +755,18 @@ ADB.prototype.getRunningAVDName = function(cb) {
|
||||
|
||||
ADB.prototype.prepareEmulator = function(cb) {
|
||||
if (this.avdName !== null) {
|
||||
this.getRunningAVDName(_.bind(function(err, runningAVDName) {
|
||||
this.getRunningAVDName(function(err, runningAVDName) {
|
||||
if (!err && this.avdName.replace('@','') === runningAVDName) {
|
||||
logger.info("Did not launch AVD because it was already running.");
|
||||
cb(null);
|
||||
} else {
|
||||
logger.info("Launching Emulator with AVD " + this.avdName);
|
||||
var killallCmd = isWindows ? "TASKKILL /IM emulator.exe" : "/usr/bin/killall -m emulator*";
|
||||
exec(killallCmd, { maxBuffer: 524288 }, _.bind(function(err, stdout) {
|
||||
exec(killallCmd, { maxBuffer: 524288 }, function(err, stdout) {
|
||||
if (err) {
|
||||
logger.info("Could not kill emulator. It was probably not running. : " + err.message);
|
||||
}
|
||||
this.checkSdkBinaryPresent("emulator",_.bind(function(err, emulatorBinaryPath) {
|
||||
this.checkSdkBinaryPresent("emulator", function(err, emulatorBinaryPath) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
@@ -790,9 +776,9 @@ ADB.prototype.prepareEmulator = function(cb) {
|
||||
var emulatorProc = spawn(emulatorBinaryPath, [this.avdName]);
|
||||
var timeoutMs = 120000;
|
||||
var now = Date.now();
|
||||
var checkEmulatorAlive = _.bind(function() {
|
||||
this.restartAdb(_.bind(function() {
|
||||
this.getConnectedDevices(_.bind(function(err, devices) {
|
||||
var checkEmulatorAlive = function() {
|
||||
this.restartAdb(function() {
|
||||
this.getConnectedDevices(function(err, devices) {
|
||||
if (!err && devices.length) {
|
||||
cb(null, true);
|
||||
} else if (Date.now() < (now + timeoutMs)) {
|
||||
@@ -800,14 +786,14 @@ ADB.prototype.prepareEmulator = function(cb) {
|
||||
} else {
|
||||
cb(new Error("Emulator didn't come up in " + timeoutMs + "ms"));
|
||||
}
|
||||
}, this));
|
||||
}, this));
|
||||
}, this);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
checkEmulatorAlive();
|
||||
}, this));
|
||||
}, this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
@@ -816,7 +802,7 @@ ADB.prototype.prepareEmulator = function(cb) {
|
||||
|
||||
ADB.prototype.getConnectedDevices = function(cb) {
|
||||
this.debug("Getting connected devices...");
|
||||
exec(this.adb + " devices", { maxBuffer: 524288 }, _.bind(function(err, stdout) {
|
||||
exec(this.adb + " devices", { maxBuffer: 524288 }, function(err, stdout) {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
cb(err);
|
||||
@@ -842,7 +828,7 @@ ADB.prototype.getConnectedDevices = function(cb) {
|
||||
}
|
||||
cb(null, devices);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.forwardPort = function(cb) {
|
||||
@@ -850,7 +836,7 @@ ADB.prototype.forwardPort = function(cb) {
|
||||
this.debug("Forwarding system:" + this.systemPort + " to device:" +
|
||||
this.devicePort);
|
||||
var arg = "tcp:" + this.systemPort + " tcp:" + this.devicePort;
|
||||
exec(this.adbCmd + " forward " + arg, { maxBuffer: 524288 }, _.bind(function(err) {
|
||||
exec(this.adbCmd + " forward " + arg, { maxBuffer: 524288 }, function(err) {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
cb(err);
|
||||
@@ -858,7 +844,7 @@ ADB.prototype.forwardPort = function(cb) {
|
||||
this.portForwarded = true;
|
||||
cb(null);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.runBootstrap = function(readyCb, exitCb) {
|
||||
@@ -870,16 +856,15 @@ ADB.prototype.runBootstrap = function(readyCb, exitCb) {
|
||||
this.proc = spawn(this.adb.substr(1, this.adb.length - 2), args);
|
||||
this.onSocketReady = readyCb;
|
||||
|
||||
this.proc.stdout.on('data', _.bind(function(data) {
|
||||
this.proc.stdout.on('data', function(data) {
|
||||
this.outputStreamHandler(data);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
|
||||
this.proc.stderr.on('data', _.bind(function(data) {
|
||||
this.proc.stderr.on('data', function(data) {
|
||||
this.errorStreamHandler(data);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
|
||||
var me = this;
|
||||
this.proc.on('exit', _.bind(function(code) {
|
||||
this.proc.on('exit', function(code) {
|
||||
this.cmdCb = null;
|
||||
if (this.socketClient) {
|
||||
this.socketClient.end();
|
||||
@@ -890,11 +875,11 @@ ADB.prototype.runBootstrap = function(readyCb, exitCb) {
|
||||
if (this.restartBootstrap === true) {
|
||||
// The bootstrap jar has crashed so it must be restarted.
|
||||
this.restartBootstrap = false;
|
||||
me.runBootstrap(function() {
|
||||
this.runBootstrap(function() {
|
||||
// Resend last command because the client is still waiting for the
|
||||
// response.
|
||||
me.resendLastCommand();
|
||||
}, exitCb);
|
||||
this.resendLastCommand();
|
||||
}.bind(this), exitCb);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -902,7 +887,7 @@ ADB.prototype.runBootstrap = function(readyCb, exitCb) {
|
||||
this.alreadyExited = true;
|
||||
exitCb(code);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
|
||||
|
||||
};
|
||||
@@ -911,13 +896,13 @@ ADB.prototype.checkForSocketReady = function(output) {
|
||||
if (/Appium Socket Server Ready/.test(output)) {
|
||||
this.requirePortForwarded();
|
||||
this.debug("Connecting to server on device...");
|
||||
this.socketClient = net.connect(this.systemPort, _.bind(function() {
|
||||
this.socketClient = net.connect(this.systemPort, function() {
|
||||
this.debug("Connected!");
|
||||
this.onSocketReady(null);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
this.socketClient.setEncoding('utf8');
|
||||
var oldData = '';
|
||||
this.socketClient.on('data', _.bind(function(data) {
|
||||
this.socketClient.on('data', function(data) {
|
||||
this.debug("Received command result from bootstrap");
|
||||
try {
|
||||
data = JSON.parse(oldData + data);
|
||||
@@ -935,7 +920,7 @@ ADB.prototype.checkForSocketReady = function(output) {
|
||||
this.debug("Got data when we weren't expecting it, ignoring:");
|
||||
this.debug(JSON.stringify(data));
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -952,25 +937,24 @@ ADB.prototype.sendCommand = function(type, extra, cb) {
|
||||
if (this.cmdCb !== null) {
|
||||
logger.warn("Trying to run a command when one is already in progress. " +
|
||||
"Will spin a bit and try again");
|
||||
var me = this;
|
||||
var start = Date.now();
|
||||
var timeoutMs = 10000;
|
||||
var intMs = 200;
|
||||
var waitForCmdCbNull = function() {
|
||||
if (me.cmdCb === null) {
|
||||
me.sendCommand(type, extra, cb);
|
||||
if (this.cmdCb === null) {
|
||||
this.sendCommand(type, extra, cb);
|
||||
} else if ((Date.now() - start) < timeoutMs) {
|
||||
setTimeout(waitForCmdCbNull, intMs);
|
||||
} else {
|
||||
cb(new Error("Never became able to push strings since a command " +
|
||||
"was in process"));
|
||||
}
|
||||
};
|
||||
}.bind(this);
|
||||
waitForCmdCbNull();
|
||||
} else if (this.socketClient) {
|
||||
this.resendLastCommand = _.bind(function() {
|
||||
this.resendLastCommand = function() {
|
||||
this.sendCommand(type, extra, cb);
|
||||
}, this);
|
||||
}.bind(this);
|
||||
if (typeof extra === "undefined" || extra === null) {
|
||||
extra = {};
|
||||
}
|
||||
@@ -994,13 +978,13 @@ ADB.prototype.sendCommand = function(type, extra, cb) {
|
||||
};
|
||||
|
||||
ADB.prototype.sendShutdownCommand = function(cb) {
|
||||
setTimeout(_.bind(function() {
|
||||
setTimeout(function() {
|
||||
if (!this.alreadyExited) {
|
||||
logger.warn("Android did not shut down fast enough, calling it gone");
|
||||
this.alreadyExited = true;
|
||||
this.onExit(1);
|
||||
}
|
||||
}, this), 7000);
|
||||
}.bind(this), 7000);
|
||||
this.sendCommand('shutdown', null, cb);
|
||||
};
|
||||
|
||||
@@ -1017,7 +1001,6 @@ ADB.prototype.handleBootstrapOutput = function(output) {
|
||||
var lines = output.split("\n");
|
||||
var re = /^\[APPIUM-UIAUTO\] (.+)\[\/APPIUM-UIAUTO\]$/;
|
||||
var match;
|
||||
var me = this;
|
||||
_.each(lines, function(line) {
|
||||
line = line.trim();
|
||||
if (line !== '') {
|
||||
@@ -1028,18 +1011,18 @@ ADB.prototype.handleBootstrapOutput = function(output) {
|
||||
var alertRe = /Emitting system alert message/;
|
||||
if (alertRe.test(line)) {
|
||||
logger.info("Emiting alert message...");
|
||||
me.webSocket.sockets.emit('alert', {message: line});
|
||||
this.webSocket.sockets.emit('alert', {message: line});
|
||||
}
|
||||
} else {
|
||||
// The dump command will always disconnect UiAutomation.
|
||||
// Detect the crash then restart UiAutomation.
|
||||
if (line.indexOf("UiAutomationService not connected") !== -1) {
|
||||
me.restartBootstrap = true;
|
||||
this.restartBootstrap = true;
|
||||
}
|
||||
logger.info(("[ADB STDOUT] " + line).grey);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, this);
|
||||
};
|
||||
|
||||
ADB.prototype.errorStreamHandler = function(output) {
|
||||
@@ -1098,7 +1081,7 @@ ADB.prototype.requireApk = function() {
|
||||
};
|
||||
|
||||
ADB.prototype.waitForDevice = function(cb) {
|
||||
var doWait = _.bind(function(innerCb) {
|
||||
var doWait = function(innerCb) {
|
||||
this.debug("Waiting for device " + this.curDeviceId + " to be ready " +
|
||||
"and to respond to shell commands (timeout = " +
|
||||
this.appDeviceReadyTimeout + ")");
|
||||
@@ -1106,22 +1089,22 @@ ADB.prototype.waitForDevice = function(cb) {
|
||||
, cmd = this.adbCmd + " wait-for-device"
|
||||
, timeoutSecs = parseInt(this.appDeviceReadyTimeout, 10);
|
||||
|
||||
setTimeout(_.bind(function() {
|
||||
setTimeout(function() {
|
||||
if (!movedOn) {
|
||||
movedOn = true;
|
||||
innerCb("Device did not become ready in " + timeoutSecs + " secs; " +
|
||||
"are you sure it's powered on?");
|
||||
}
|
||||
}, this), timeoutSecs * 1000);
|
||||
}.bind(this), timeoutSecs * 1000);
|
||||
|
||||
exec(cmd, { maxBuffer: 524288 }, _.bind(function(err) {
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err) {
|
||||
if (!movedOn) {
|
||||
if (err) {
|
||||
logger.error("Error running wait-for-device");
|
||||
movedOn = true;
|
||||
innerCb(err);
|
||||
} else {
|
||||
exec(this.adbCmd + " shell echo 'ready'", _.bind(function(err) {
|
||||
exec(this.adbCmd + " shell echo 'ready'", function(err) {
|
||||
if (!movedOn) {
|
||||
movedOn = true;
|
||||
if (err) {
|
||||
@@ -1131,23 +1114,23 @@ ADB.prototype.waitForDevice = function(cb) {
|
||||
innerCb(null);
|
||||
}
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
}, this));
|
||||
}, this);
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
|
||||
doWait(_.bind(function(err) {
|
||||
doWait(function(err) {
|
||||
if (err) {
|
||||
this.restartAdb(_.bind(function() {
|
||||
this.restartAdb(function() {
|
||||
this.getConnectedDevices(function() {
|
||||
doWait(cb);
|
||||
});
|
||||
}, this));
|
||||
}.bind(this));
|
||||
} else {
|
||||
cb(null);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.restartAdb = function(cb) {
|
||||
@@ -1165,23 +1148,23 @@ ADB.prototype.restartAdb = function(cb) {
|
||||
ADB.prototype.pushAppium = function(cb) {
|
||||
this.debug("Pushing appium bootstrap to device...");
|
||||
var binPath = path.resolve(__dirname, "..", "build", "android_bootstrap", "AppiumBootstrap.jar");
|
||||
fs.stat(binPath, _.bind(function(err) {
|
||||
fs.stat(binPath, function(err) {
|
||||
if (err) {
|
||||
cb("Could not find AppiumBootstrap.jar; please run " +
|
||||
"'grunt buildAndroidBootstrap'");
|
||||
} else {
|
||||
var remotePath = "/data/local/tmp";
|
||||
var cmd = this.adbCmd + ' push "' + binPath + '" ' + remotePath;
|
||||
exec(cmd, { maxBuffer: 524288 }, _.bind(function(err) {
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err) {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
cb(err);
|
||||
} else {
|
||||
cb(null);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.startApp = function(cb) {
|
||||
@@ -1192,7 +1175,7 @@ ADB.prototype.startApp = function(cb) {
|
||||
var cmd = this.adbCmd + " shell am start -n " + this.appPackage + "/" +
|
||||
activityString;
|
||||
this.debug("Starting app\n" + cmd);
|
||||
exec(cmd, { maxBuffer: 524288 }, _.bind(function(err, stdout) {
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err, stdout) {
|
||||
if(err) {
|
||||
logger.error(err);
|
||||
cb(err);
|
||||
@@ -1214,7 +1197,7 @@ ADB.prototype.startApp = function(cb) {
|
||||
|
||||
this.waitForActivity(cb);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.stopApp = function(cb) {
|
||||
@@ -1237,7 +1220,7 @@ ADB.prototype.getFocusedPackageAndActivity = function(cb) {
|
||||
var cmd = this.adbCmd + " shell dumpsys window windows"
|
||||
, searchRe = new RegExp(/mFocusedApp.+ ([a-zA-Z0-9\.]+)\/(\.?[^\}]+)\}/);
|
||||
|
||||
exec(cmd, { maxBuffer: 524288 }, _.bind(function(err, stdout) {
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err, stdout) {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
cb(err);
|
||||
@@ -1255,7 +1238,7 @@ ADB.prototype.getFocusedPackageAndActivity = function(cb) {
|
||||
cb(new Error("Could not parse activity from dumpsys"));
|
||||
}
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.waitForNotActivity = function(cb) {
|
||||
@@ -1268,8 +1251,8 @@ ADB.prototype.waitForNotActivity = function(cb) {
|
||||
if (targetActivity.indexOf(this.appPackage) === 0) {
|
||||
targetActivity = targetActivity.substring(this.appPackage.length);
|
||||
}
|
||||
var getFocusedApp = _.bind(function() {
|
||||
this.getFocusedPackageAndActivity(_.bind(function(err, foundPackage,
|
||||
var getFocusedApp = function() {
|
||||
this.getFocusedPackageAndActivity(function(err, foundPackage,
|
||||
foundActivity) {
|
||||
var notFoundAct = true;
|
||||
_.each(targetActivity.split(','), function(act) {
|
||||
@@ -1290,8 +1273,8 @@ ADB.prototype.waitForNotActivity = function(cb) {
|
||||
cb(new Error(msg));
|
||||
}
|
||||
|
||||
}, this));
|
||||
}, this);
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
getFocusedApp();
|
||||
};
|
||||
|
||||
@@ -1305,8 +1288,8 @@ ADB.prototype.waitForActivity = function(cb, waitMsOverride) {
|
||||
if (targetActivity.indexOf(this.appPackage) === 0) {
|
||||
targetActivity = targetActivity.substring(this.appPackage.length);
|
||||
}
|
||||
var getFocusedApp = _.bind(function() {
|
||||
this.getFocusedPackageAndActivity(_.bind(function(err, foundPackage,
|
||||
var getFocusedApp = function() {
|
||||
this.getFocusedPackageAndActivity(function(err, foundPackage,
|
||||
foundActivity) {
|
||||
var foundAct = false;
|
||||
_.each(targetActivity.split(','), function(act) {
|
||||
@@ -1327,8 +1310,8 @@ ADB.prototype.waitForActivity = function(cb, waitMsOverride) {
|
||||
cb(new Error(msg));
|
||||
}
|
||||
|
||||
}, this));
|
||||
}, this);
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
getFocusedApp();
|
||||
};
|
||||
|
||||
@@ -1367,17 +1350,16 @@ ADB.prototype.installApk = function(apk, cb) {
|
||||
};
|
||||
|
||||
ADB.prototype.uninstallApp = function(cb) {
|
||||
var me = this;
|
||||
var next = function() {
|
||||
me.requireDeviceId();
|
||||
me.requireApp();
|
||||
me.debug("Uninstalling app " + me.appPackage);
|
||||
this.requireDeviceId();
|
||||
this.requireApp();
|
||||
this.debug("Uninstalling app " + this.appPackage);
|
||||
|
||||
me.uninstallApk(me.appPackage, function(err) {
|
||||
if (me.fastReset) {
|
||||
var cleanPkg = me.appPackage + '.clean';
|
||||
me.debug("Uninstalling app " + cleanPkg);
|
||||
me.uninstallApk(cleanPkg, function(err) {
|
||||
this.uninstallApk(this.appPackage, function(err) {
|
||||
if (this.fastReset) {
|
||||
var cleanPkg = this.appPackage + '.clean';
|
||||
this.debug("Uninstalling app " + cleanPkg);
|
||||
this.uninstallApk(cleanPkg, function(err) {
|
||||
if (err) return cb(err);
|
||||
cb(null);
|
||||
});
|
||||
@@ -1385,11 +1367,11 @@ ADB.prototype.uninstallApp = function(cb) {
|
||||
if (err) return cb(err);
|
||||
cb(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
|
||||
if (me.skipUninstall) {
|
||||
me.debug("Not uninstalling app since server started with --full-reset " +
|
||||
if (this.skipUninstall) {
|
||||
this.debug("Not uninstalling app since server started with --full-reset " +
|
||||
"or --no-reset");
|
||||
cb();
|
||||
} else {
|
||||
@@ -1400,8 +1382,7 @@ ADB.prototype.uninstallApp = function(cb) {
|
||||
ADB.prototype.runFastReset = function(cb) {
|
||||
// list instruments with: adb shell pm list instrumentation
|
||||
// targetPackage + '.clean' / clean.apk.Clear
|
||||
var me = this;
|
||||
var clearCmd = me.adbCmd + ' shell am instrument ' + me.appPackage + '.clean/clean.apk.Clean';
|
||||
var clearCmd = this.adbCmd + ' shell am instrument ' + this.appPackage + '.clean/clean.apk.Clean';
|
||||
logger.debug("Running fast reset clean: " + clearCmd);
|
||||
exec(clearCmd, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
||||
if (err) {
|
||||
@@ -1432,10 +1413,9 @@ ADB.prototype.checkAppInstallStatus = function(pkg, cb) {
|
||||
};
|
||||
|
||||
ADB.prototype.installApp = function(cb) {
|
||||
var me = this
|
||||
, installApp = false
|
||||
var installApp = false
|
||||
, installClean = false;
|
||||
me.requireDeviceId();
|
||||
this.requireDeviceId();
|
||||
|
||||
if (this.apkPath === null) {
|
||||
logger.info("Not installing app since we launched with a package instead " +
|
||||
@@ -1443,37 +1423,37 @@ ADB.prototype.installApp = function(cb) {
|
||||
return cb(null);
|
||||
}
|
||||
|
||||
me.requireApk();
|
||||
this.requireApk();
|
||||
|
||||
var determineInstallAndCleanStatus = function(cb) {
|
||||
logger.info("Determining app install/clean status");
|
||||
me.checkAppInstallStatus(me.appPackage, function(err, installed, cleaned) {
|
||||
this.checkAppInstallStatus(this.appPackage, function(err, installed, cleaned) {
|
||||
installApp = !installed;
|
||||
installClean = !cleaned;
|
||||
cb();
|
||||
});
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
var doInstall = function(cb) {
|
||||
if (installApp) {
|
||||
me.debug("Installing app apk");
|
||||
me.installApk(me.apkPath, cb);
|
||||
this.debug("Installing app apk");
|
||||
this.installApk(this.apkPath, cb);
|
||||
} else { cb(null); }
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
var doClean = function(cb) {
|
||||
if (installClean && me.cleanApp) {
|
||||
me.debug("Installing clean apk");
|
||||
me.installApk(me.cleanAPK, cb);
|
||||
if (installClean && this.cleanApp) {
|
||||
this.debug("Installing clean apk");
|
||||
this.installApk(this.cleanAPK, cb);
|
||||
} else { cb(null); }
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
var doFastReset = function(cb) {
|
||||
// App is already installed so reset it.
|
||||
if (!installApp && me.fastReset) {
|
||||
me.runFastReset(cb);
|
||||
if (!installApp && this.fastReset) {
|
||||
this.runFastReset(cb);
|
||||
} else { cb(null); }
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
async.series([
|
||||
function(cb) { determineInstallAndCleanStatus(cb); },
|
||||
|
||||
@@ -66,11 +66,10 @@ Android.prototype.inWebView = function() {
|
||||
|
||||
// Clear data, close app, then start app.
|
||||
Android.prototype.fastReset = function(cb) {
|
||||
var me = this;
|
||||
async.series([
|
||||
function(cb) { me.adb.runFastReset(cb); },
|
||||
function(cb) { me.adb.waitForNotActivity(cb); },
|
||||
function(cb) { me.adb.startApp(cb); },
|
||||
function(cb) { this.adb.runFastReset(cb); }.bind(this),
|
||||
function(cb) { this.adb.waitForNotActivity(cb); }.bind(this),
|
||||
function(cb) { this.adb.startApp(cb); }.bind(this),
|
||||
], cb);
|
||||
};
|
||||
|
||||
@@ -84,7 +83,7 @@ Android.prototype.start = function(cb, onDie) {
|
||||
}
|
||||
var didLaunch = false;
|
||||
|
||||
var onLaunch = _.bind(function(err, launchCb) {
|
||||
var onLaunch = function(err, launchCb) {
|
||||
if (typeof launchCb === "undefined" || launchCb === null) {
|
||||
launchCb = cb;
|
||||
}
|
||||
@@ -126,9 +125,9 @@ Android.prototype.start = function(cb, onDie) {
|
||||
didLaunch = true;
|
||||
launchCb(null);
|
||||
}
|
||||
}, this);
|
||||
}.bind(this);
|
||||
|
||||
var onExit = _.bind(function(code) {
|
||||
var onExit = function(code) {
|
||||
if (!didLaunch) {
|
||||
logger.error("ADB quit before it successfully launched");
|
||||
cb("ADB quit unexpectedly before successfully launching");
|
||||
@@ -141,16 +140,16 @@ Android.prototype.start = function(cb, onDie) {
|
||||
}
|
||||
|
||||
if (this.adb) {
|
||||
this.adb.uninstallApp(_.bind(function() {
|
||||
this.adb.uninstallApp(function() {
|
||||
this.adb = null;
|
||||
this.shuttingDown = false;
|
||||
this.onStop(code);
|
||||
this.onStop = null;
|
||||
}, this));
|
||||
}.bind(this));
|
||||
} else {
|
||||
logger.info("We're in android's exit callback but adb is gone already");
|
||||
}
|
||||
}, this);
|
||||
}.bind(this);
|
||||
|
||||
if (this.adb === null) {
|
||||
// Pass Android opts and Android ref to adb.
|
||||
@@ -185,25 +184,25 @@ Android.prototype.stop = function(cb) {
|
||||
this.onStop = cb;
|
||||
}
|
||||
this.shuttingDown = true;
|
||||
this.adb.goToHome(_.bind(function() {
|
||||
this.adb.goToHome(function() {
|
||||
this.shutdown();
|
||||
}, this));
|
||||
}.bind(this));
|
||||
this.queue = [];
|
||||
this.progress = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Android.prototype.shutdown = function() {
|
||||
this.adb.sendShutdownCommand(_.bind(function() {
|
||||
this.adb.sendShutdownCommand(function() {
|
||||
logger.info("Sent shutdown command, waiting for ADB to stop...");
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Android.prototype.resetTimeout = function() {
|
||||
if (this.commandTimeout) {
|
||||
clearTimeout(this.commandTimeout);
|
||||
}
|
||||
this.commandTimeout = setTimeout(_.bind(this.timeoutWaitingForCommand, this),
|
||||
this.commandTimeout = setTimeout(this.timeoutWaitingForCommand.bind(this),
|
||||
this.commandTimeoutMs);
|
||||
};
|
||||
|
||||
@@ -215,7 +214,7 @@ Android.prototype.push = function(elem) {
|
||||
|
||||
this.queue.push(elem);
|
||||
|
||||
var next = _.bind(function() {
|
||||
var next = function() {
|
||||
if (this.queue.length <= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -240,7 +239,7 @@ Android.prototype.push = function(elem) {
|
||||
this.progress++;
|
||||
|
||||
if (this.adb && !this.shuttingDown) {
|
||||
this.adb.sendAutomatorCommand(action, params, _.bind(function(response) {
|
||||
this.adb.sendAutomatorCommand(action, params, function(response) {
|
||||
this.cbForCurrentCmd = null;
|
||||
if (typeof cb === 'function') {
|
||||
this.respond(response, cb);
|
||||
@@ -249,7 +248,7 @@ Android.prototype.push = function(elem) {
|
||||
// maybe there's moar work to do
|
||||
this.progress--;
|
||||
next();
|
||||
}, this));
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.cbForCurrentCmd = null;
|
||||
var msg = "Tried to send command to non-existent Android device, " +
|
||||
@@ -266,7 +265,7 @@ Android.prototype.push = function(elem) {
|
||||
this.progress--;
|
||||
next();
|
||||
}
|
||||
}, this);
|
||||
}.bind(this);
|
||||
|
||||
next();
|
||||
};
|
||||
@@ -277,14 +276,14 @@ Android.prototype.executeAtom = function(atom, args, cb, alwaysDefaultFrame) {
|
||||
var frames = alwaysDefaultFrame === true ? [] : this.curWebFrames;
|
||||
this.returnedFromExecuteAtom = false;
|
||||
this.processingRemoteCmd = true;
|
||||
this.remote.executeAtom(atom, args, frames, _.bind(function(err, res) {
|
||||
this.remote.executeAtom(atom, args, frames, function(err, res) {
|
||||
this.processingRemoteCmd = false;
|
||||
if (!this.returnedFromExecuteAtom) {
|
||||
this.returnedFromExecuteAtom = true;
|
||||
res = this.parseExecuteResponse(res);
|
||||
cb(err, res);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Android.prototype.parseExecuteResponse = deviceCommon.parseExecuteResponse;
|
||||
@@ -338,12 +337,11 @@ Android.prototype.findElements = function(strategy, selector, cb) {
|
||||
Android.prototype.findWebElementOrElements = function(strategy, selector, many, ctx, cb) {
|
||||
var ext = many ? 's' : '';
|
||||
var atomsElement = this.getAtomsElement(ctx);
|
||||
var me = this;
|
||||
var doFind = function(findCb) {
|
||||
me.executeAtom('find_element' + ext, [strategy, selector, atomsElement], function(err, res) {
|
||||
me.handleFindCb(err, res, many, findCb);
|
||||
});
|
||||
};
|
||||
this.executeAtom('find_element' + ext, [strategy, selector, atomsElement], function(err, res) {
|
||||
this.handleFindCb(err, res, many, findCb);
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
this.waitForCondition(this.implicitWaitMs, doFind, cb);
|
||||
};
|
||||
|
||||
@@ -372,11 +370,11 @@ Android.prototype.findUIElementOrElements = function(strategy, selector, many, c
|
||||
params = _.extend(params, xpathParams);
|
||||
}
|
||||
}
|
||||
var doFind = _.bind(function(findCb) {
|
||||
this.proxy(["find", params], _.bind(function(err, res) {
|
||||
var doFind = function(findCb) {
|
||||
this.proxy(["find", params], function(err, res) {
|
||||
this.handleFindCb(err, res, many, findCb);
|
||||
}, this));
|
||||
}, this);
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
if (!xpathError) {
|
||||
this.waitForCondition(this.implicitWaitMs, doFind, cb);
|
||||
} else {
|
||||
@@ -431,9 +429,9 @@ Android.prototype.setValue = function(elementId, value, cb) {
|
||||
|
||||
Android.prototype.click = function(elementId, cb) {
|
||||
if (this.inWebView()) {
|
||||
this.useAtomsElement(elementId, cb, _.bind(function(atomsElement) {
|
||||
this.useAtomsElement(elementId, cb, function(atomsElement) {
|
||||
this.executeAtom('tap', [atomsElement], cb);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.proxy(["element:click", {elementId: elementId}], cb);
|
||||
}
|
||||
@@ -549,7 +547,6 @@ Android.prototype.getCssProperty = function(elementId, propertyName, cb) {
|
||||
};
|
||||
|
||||
Android.prototype.getPageSource = function(cb) {
|
||||
var me = this;
|
||||
var xmlFile = temp.path({suffix: '.xml'});
|
||||
var jsonFile = xmlFile + '.json';
|
||||
var json = '';
|
||||
@@ -557,10 +554,10 @@ Android.prototype.getPageSource = function(cb) {
|
||||
[
|
||||
function(cb) {
|
||||
// /data/local/tmp/dump.xml
|
||||
me.proxy(["dumpWindowHierarchy"], cb);
|
||||
},
|
||||
this.proxy(["dumpWindowHierarchy"], cb);
|
||||
}.bind(this),
|
||||
function(cb) {
|
||||
var cmd = me.adb.adbCmd + ' pull /data/local/tmp/dump.xml "' + xmlFile + '"';
|
||||
var cmd = this.adb.adbCmd + ' pull /data/local/tmp/dump.xml "' + xmlFile + '"';
|
||||
logger.debug('transferPageSource command: ' + cmd);
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
||||
if (err) {
|
||||
@@ -569,7 +566,7 @@ Android.prototype.getPageSource = function(cb) {
|
||||
}
|
||||
cb(null);
|
||||
});
|
||||
},
|
||||
}.bind(this),
|
||||
function(cb) {
|
||||
var jar = path.resolve(__dirname, '../app/android/dump2json.jar');
|
||||
var cmd = 'java -jar "' + jar + '" "' + xmlFile + '"';
|
||||
@@ -599,12 +596,11 @@ Android.prototype.getPageSource = function(cb) {
|
||||
};
|
||||
|
||||
Android.prototype.getPageSourceXML = function(cb) {
|
||||
var me = this;
|
||||
var xmlFile = temp.path({suffix: '.xml'});
|
||||
async.series(
|
||||
[
|
||||
function(cb) {
|
||||
var cmd = me.adb.adbCmd + ' shell uiautomator dump /data/local/tmp/dump.xml';
|
||||
var cmd = this.adb.adbCmd + ' shell uiautomator dump /data/local/tmp/dump.xml';
|
||||
logger.debug('getPageSourceXML command: ' + cmd);
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
||||
if (err) {
|
||||
@@ -613,9 +609,9 @@ Android.prototype.getPageSourceXML = function(cb) {
|
||||
}
|
||||
cb(null);
|
||||
});
|
||||
},
|
||||
}.bind(this),
|
||||
function(cb) {
|
||||
var cmd = me.adb.adbCmd + ' pull /data/local/tmp/dump.xml "' + xmlFile + '"';
|
||||
var cmd = this.adb.adbCmd + ' pull /data/local/tmp/dump.xml "' + xmlFile + '"';
|
||||
logger.debug('transferPageSourceXML command: ' + cmd);
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
||||
if (err) {
|
||||
@@ -624,7 +620,7 @@ Android.prototype.getPageSourceXML = function(cb) {
|
||||
}
|
||||
cb(null);
|
||||
});
|
||||
}
|
||||
}.bind(this)
|
||||
|
||||
],
|
||||
// Top level cb
|
||||
@@ -675,17 +671,16 @@ Android.prototype.setOrientation = function(orientation, cb) {
|
||||
};
|
||||
|
||||
Android.prototype.getScreenshot = function(cb) {
|
||||
var me = this;
|
||||
me.adb.requireDeviceId();
|
||||
this.adb.requireDeviceId();
|
||||
var localfile = temp.path({prefix: 'appium', suffix: '.png'});
|
||||
var b64data = "";
|
||||
|
||||
async.series([
|
||||
function(cb) {
|
||||
me.proxy(["takeScreenshot"], cb);
|
||||
},
|
||||
this.proxy(["takeScreenshot"], cb);
|
||||
}.bind(this),
|
||||
function(cb) {
|
||||
var cmd = me.adb.adbCmd + ' pull /data/local/tmp/screenshot.png "' + localfile + '"';
|
||||
var cmd = this.adb.adbCmd + ' pull /data/local/tmp/screenshot.png "' + localfile + '"';
|
||||
logger.debug("screenshot cmd: " + cmd);
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err, stdout, stderr) {
|
||||
if (err) {
|
||||
@@ -694,7 +689,7 @@ Android.prototype.getScreenshot = function(cb) {
|
||||
}
|
||||
cb(null);
|
||||
});
|
||||
},
|
||||
}.bind(this),
|
||||
function(cb) {
|
||||
fs.readFile(localfile, function read(err, data) {
|
||||
if (err) {
|
||||
@@ -858,11 +853,10 @@ Android.prototype.clearWebView = function(cb) {
|
||||
|
||||
Android.prototype.execute = function(script, args, cb) {
|
||||
if (this.inWebView()) {
|
||||
var me = this;
|
||||
this.convertElementForAtoms(args, function(err, res) {
|
||||
if (err) return cb(null, res);
|
||||
me.executeAtom('execute_script', [script, res], cb);
|
||||
});
|
||||
this.executeAtom('execute_script', [script, res], cb);
|
||||
}.bind(this));
|
||||
} else {
|
||||
cb(new NotYetImplementedError(), null);
|
||||
}
|
||||
|
||||
117
app/appium.js
117
app/appium.js
@@ -88,20 +88,20 @@ Appium.prototype.registerConfig = function(configObj) {
|
||||
Appium.prototype.preLaunch = function(cb) {
|
||||
logger.info("Pre-launching app");
|
||||
var caps = {};
|
||||
this.start(caps, _.bind(function(err) {
|
||||
this.start(caps, function(err) {
|
||||
if (err) {
|
||||
cb(err, null);
|
||||
} else {
|
||||
this.preLaunched = true;
|
||||
cb(null, this);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Appium.prototype.start = function(desiredCaps, cb) {
|
||||
this.origApp = this.args.app;
|
||||
this.desiredCapabilities = desiredCaps;
|
||||
this.configure(desiredCaps, _.bind(function(err) {
|
||||
this.configure(desiredCaps, function(err) {
|
||||
if (err) {
|
||||
logger.info("Got configuration error, not starting session");
|
||||
cb(err, null);
|
||||
@@ -109,7 +109,7 @@ Appium.prototype.start = function(desiredCaps, cb) {
|
||||
this.sessions[++this.counter] = { sessionId: '', callback: cb };
|
||||
this.clearPreviousSession();
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Appium.prototype.getDeviceType = function(desiredCaps) {
|
||||
@@ -204,9 +204,9 @@ Appium.prototype.getAppExt = function() {
|
||||
};
|
||||
|
||||
Appium.prototype.setAndroidArgs = function(desiredCaps) {
|
||||
var setArgFromCaps = _.bind(function(arg, cap) {
|
||||
var setArgFromCaps = function(arg, cap) {
|
||||
this.args[arg] = desiredCaps[cap] || this.args[arg];
|
||||
}, this);
|
||||
}.bind(this);
|
||||
setArgFromCaps("androidPackage", "app-package");
|
||||
setArgFromCaps("androidActivity", "app-activity");
|
||||
setArgFromCaps("androidWaitActivity", "app-wait-activity");
|
||||
@@ -308,12 +308,12 @@ Appium.prototype.configureLocalApp = function(appPath, origin, cb) {
|
||||
});
|
||||
} else if (ext === ".zip") {
|
||||
logger.info("Using local zip from " + origin + ": " + appPath);
|
||||
this.unzipLocalApp(appPath, _.bind(function(zipErr, newAppPath) {
|
||||
this.unzipLocalApp(appPath, function(zipErr, newAppPath) {
|
||||
if (zipErr) return cb(zipErr);
|
||||
this.args.app = newAppPath;
|
||||
logger.info("Using locally extracted app: " + this.args.app);
|
||||
cb(null);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
} else {
|
||||
var dExt = this.getAppExt();
|
||||
logger.error("Using local app, but didn't end in .zip or " + dExt);
|
||||
@@ -336,7 +336,7 @@ Appium.prototype.configureDownloadedApp = function(appPath, origin, cb) {
|
||||
}
|
||||
} else if (appUrl.substring(appUrl.length - 4) === ".zip") {
|
||||
try {
|
||||
this.downloadAndUnzipApp(appUrl, _.bind(function(zipErr, appPath) {
|
||||
this.downloadAndUnzipApp(appUrl, function(zipErr, appPath) {
|
||||
if (zipErr) {
|
||||
cb(zipErr);
|
||||
} else {
|
||||
@@ -344,7 +344,7 @@ Appium.prototype.configureDownloadedApp = function(appPath, origin, cb) {
|
||||
logger.info("Using extracted app: " + this.args.app);
|
||||
cb(null);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
logger.info("Using downloadable app from " + origin + ": " + appUrl);
|
||||
} catch (e) {
|
||||
var err = e.toString();
|
||||
@@ -365,11 +365,11 @@ Appium.prototype.configureSafari = function(desiredCaps, cb) {
|
||||
usingDefaultVer = false;
|
||||
}
|
||||
logger.info("Trying to use mobile safari, version " + safariVer);
|
||||
var checkSuccess = _.bind(function(attemptedApp) {
|
||||
var checkSuccess = function(attemptedApp) {
|
||||
logger.info("Using mobile safari app at " + attemptedApp);
|
||||
this.args.app = attemptedApp;
|
||||
cleanSafariNext();
|
||||
}, this);
|
||||
}.bind(this);
|
||||
var cleanSafariNext = function() {
|
||||
logger.info("Cleaning mobile safari data files");
|
||||
cleanSafari(safariVer, function(err) {
|
||||
@@ -382,28 +382,28 @@ Appium.prototype.configureSafari = function(desiredCaps, cb) {
|
||||
});
|
||||
};
|
||||
|
||||
checkSafari(safariVer, _.bind(function(err, attemptedApp) {
|
||||
checkSafari(safariVer, function(err, attemptedApp) {
|
||||
if (err) {
|
||||
logger.warn("Could not find mobile safari with version '" + safariVer +
|
||||
"': " + err);
|
||||
if (usingDefaultVer) {
|
||||
safariVer = "6.1";
|
||||
logger.info("Retrying with safari ver " + safariVer);
|
||||
checkSafari(safariVer, _.bind(function(err, attemptedApp) {
|
||||
checkSafari(safariVer, function(err, attemptedApp) {
|
||||
if (err) {
|
||||
logger.warn("Could not find this one either: " + err);
|
||||
cb(err);
|
||||
} else {
|
||||
checkSuccess(attemptedApp);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
} else {
|
||||
cb(err);
|
||||
}
|
||||
} else {
|
||||
checkSuccess(attemptedApp);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Appium.prototype.configureChromeAndroid = function() {
|
||||
@@ -414,19 +414,17 @@ Appium.prototype.configureChromeAndroid = function() {
|
||||
};
|
||||
|
||||
Appium.prototype.downloadAndUnzipApp = function(appUrl, cb) {
|
||||
var me = this;
|
||||
downloadFile(appUrl, function(zipPath) {
|
||||
me.unzipApp(zipPath, cb);
|
||||
});
|
||||
this.unzipApp(zipPath, cb);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Appium.prototype.unzipLocalApp = function(localZipPath, cb) {
|
||||
var me = this;
|
||||
try {
|
||||
copyLocalZip(localZipPath, function(err, zipPath) {
|
||||
if (err) return cb(err);
|
||||
me.unzipApp(zipPath, cb);
|
||||
});
|
||||
this.unzipApp(zipPath, cb);
|
||||
}.bind(this));
|
||||
} catch (e) {
|
||||
logger.error("Failed copying and unzipping local app: " + localZipPath);
|
||||
cb(e);
|
||||
@@ -435,35 +433,31 @@ Appium.prototype.unzipLocalApp = function(localZipPath, cb) {
|
||||
|
||||
Appium.prototype.unzipApp = function(zipPath, cb) {
|
||||
this.tempFiles.push(zipPath);
|
||||
var me = this;
|
||||
unzipApp(zipPath, me.getAppExt(), function(err, appPath) {
|
||||
unzipApp(zipPath, this.getAppExt(), function(err, appPath) {
|
||||
if (err) {
|
||||
cb(err, null);
|
||||
} else {
|
||||
me.tempFiles.push(appPath);
|
||||
this.tempFiles.push(appPath);
|
||||
cb(null, appPath);
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Appium.prototype.clearPreviousSession = function() {
|
||||
var me = this;
|
||||
logger.info("Clearing out any previous sessions");
|
||||
if (me.sessionOverride && me.device) {
|
||||
me.device.stop(function() {
|
||||
me.devices = [];
|
||||
me.device = null;
|
||||
me.sessions[me.progress] = {};
|
||||
me.invoke();
|
||||
});
|
||||
if (this.sessionOverride && this.device) {
|
||||
this.device.stop(function() {
|
||||
this.devices = [];
|
||||
this.device = null;
|
||||
this.sessions[this.progress] = {};
|
||||
this.invoke();
|
||||
}.bind(this));
|
||||
} else {
|
||||
me.invoke();
|
||||
this.invoke();
|
||||
}
|
||||
};
|
||||
|
||||
Appium.prototype.invoke = function() {
|
||||
var me = this;
|
||||
|
||||
if (this.progress >= this.counter) {
|
||||
return;
|
||||
}
|
||||
@@ -565,34 +559,34 @@ Appium.prototype.invoke = function() {
|
||||
|
||||
if (typeof this.desiredCapabilities.launch === "undefined" || this.desiredCapabilities.launch !== false ) {
|
||||
this.device.start(function(err, sessionIdOverride) {
|
||||
me.progress++;
|
||||
this.progress++;
|
||||
// Ensure we don't use an undefined session.
|
||||
if (typeof me.sessions[me.progress] === 'undefined') {
|
||||
me.progress--;
|
||||
if (typeof this.sessions[this.progress] === 'undefined') {
|
||||
this.progress--;
|
||||
return;
|
||||
}
|
||||
if (sessionIdOverride) {
|
||||
me.sessionId = sessionIdOverride;
|
||||
this.sessionId = sessionIdOverride;
|
||||
logger.info("Overriding session id with " + sessionIdOverride);
|
||||
}
|
||||
|
||||
me.sessions[me.progress].sessionId = me.sessionId;
|
||||
me.sessions[me.progress].callback(err, me.device);
|
||||
this.sessions[this.progress].sessionId = this.sessionId;
|
||||
this.sessions[this.progress].callback(err, this.device);
|
||||
if (err) {
|
||||
me.onDeviceDie(1);
|
||||
this.onDeviceDie(1);
|
||||
}
|
||||
}, _.bind(me.onDeviceDie, me));
|
||||
}.bind(this), this.onDeviceDie.bind(this));
|
||||
} else {
|
||||
//required if we dont want to launch the app, but we do want a session.
|
||||
me.progress++;
|
||||
this.progress++;
|
||||
// Ensure we don't use an undefined session.
|
||||
if (typeof me.sessions[me.progress] === 'undefined') {
|
||||
me.progress--;
|
||||
if (typeof this.sessions[this.progress] === 'undefined') {
|
||||
this.progress--;
|
||||
return;
|
||||
}
|
||||
|
||||
me.sessions[me.progress].sessionId = me.sessionId;
|
||||
me.sessions[me.progress].callback(null, me.device);
|
||||
this.sessions[this.progress].sessionId = this.sessionId;
|
||||
this.sessions[this.progress].callback(null, this.device);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -631,32 +625,29 @@ Appium.prototype.stop = function(cb) {
|
||||
return cb();
|
||||
}
|
||||
|
||||
var me = this;
|
||||
|
||||
logger.info('Shutting down appium session...');
|
||||
this.device.stop(function(code) {
|
||||
me.onDeviceDie(code, cb);
|
||||
});
|
||||
this.onDeviceDie(code, cb);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
|
||||
Appium.prototype.reset = function(cb) {
|
||||
logger.info("Resetting app mid-session");
|
||||
if (this.isIos() || !this.fastReset) {
|
||||
var me = this
|
||||
, oldImpWait = this.device.implicitWaitMs
|
||||
, oldId = this.sessionId;
|
||||
var oldImpWait = this.device.implicitWaitMs
|
||||
, oldId = this.sessionId;
|
||||
|
||||
this.resetting = true;
|
||||
this.stop(function() {
|
||||
logger.info("Restarting app");
|
||||
me.start(me.desiredCapabilities, function() {
|
||||
me.resetting = false;
|
||||
me.device.implicitWaitMs = oldImpWait;
|
||||
me.sessionId = oldId;
|
||||
this.start(this.desiredCapabilities, function() {
|
||||
this.resetting = false;
|
||||
this.device.implicitWaitMs = oldImpWait;
|
||||
this.sessionId = oldId;
|
||||
cb(null, {status: status.codes.Success.code, value: null});
|
||||
});
|
||||
});
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
} else { // fast reset
|
||||
logger.info("Android fast reset");
|
||||
this.device.fastReset(function(err){
|
||||
|
||||
@@ -46,19 +46,18 @@ exports.waitForCondition = function(waitMs, condFn, cb, intervalMs) {
|
||||
}
|
||||
var begunAt = Date.now();
|
||||
var endAt = begunAt + waitMs;
|
||||
var me = this;
|
||||
var spin = function() {
|
||||
condFn(function(condMet) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
if (condMet) {
|
||||
cb.apply(me, args.slice(1));
|
||||
cb.apply(this, args.slice(1));
|
||||
} else if (Date.now() < endAt) {
|
||||
setTimeout(spin, intervalMs);
|
||||
} else {
|
||||
cb.apply(me, args.slice(1));
|
||||
cb.apply(this, args.slice(1));
|
||||
}
|
||||
});
|
||||
};
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
spin();
|
||||
};
|
||||
|
||||
|
||||
@@ -45,12 +45,11 @@ var Firefox = function(opts) {
|
||||
};
|
||||
|
||||
Firefox.prototype.start = function(cb, onDie) {
|
||||
var me = this;
|
||||
this.socket = new net.Socket();
|
||||
this.socket.on('close', function() {
|
||||
onDie(0);
|
||||
});
|
||||
this.socket.on('data', _.bind(this.receive, this));
|
||||
this.socket.on('data', this.receive.bind(this));
|
||||
|
||||
this.socket.connect(this.port, 'localhost', function () { });
|
||||
|
||||
@@ -59,40 +58,40 @@ Firefox.prototype.start = function(cb, onDie) {
|
||||
logger.info("Firefox OS socket connected");
|
||||
var mainCb = cb;
|
||||
async.waterfall([
|
||||
function(cb) { me.getMarionetteId(cb); },
|
||||
function(cb) { me.createSession(cb); },
|
||||
function(cb) { me.launchAppByName(cb); },
|
||||
function(frameId, cb) { me.frame(frameId, cb); }
|
||||
], function() { mainCb(null, me.sessionId); });
|
||||
function(cb) { this.getMarionetteId(cb); }.bind(this),
|
||||
function(cb) { this.createSession(cb); }.bind(this),
|
||||
function(cb) { this.launchAppByName(cb); }.bind(this),
|
||||
function(frameId, cb) { this.frame(frameId, cb); }.bind(this)
|
||||
], function() { mainCb(null, this.sessionId); }.bind(this));
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
Firefox.prototype.stop = function(cb) {
|
||||
logger.info("Stopping firefoxOs connection");
|
||||
this.proxy({type: 'deleteSession'}, _.bind(function(err) {
|
||||
this.proxy({type: 'deleteSession'}, function(err) {
|
||||
if (err) return cb(err);
|
||||
this.socket.destroy();
|
||||
cb(0);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Firefox.prototype.getMarionetteId = function(cb) {
|
||||
logger.info("Getting marionette id");
|
||||
this.proxy({type: 'getMarionetteID'}, _.bind(function(err, res) {
|
||||
this.proxy({type: 'getMarionetteID'}, function(err, res) {
|
||||
if (err) return cb(err);
|
||||
this.fromActor = res.id;
|
||||
cb(null);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Firefox.prototype.createSession = function(cb) {
|
||||
logger.info("Creating firefox os session");
|
||||
this.proxy({type: 'newSession'}, _.bind(function(err, res) {
|
||||
this.proxy({type: 'newSession'}, function(err, res) {
|
||||
if (err) return cb(err);
|
||||
this.sessionId = res.value;
|
||||
cb(null);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Firefox.prototype.launchAppByName = function(cb) {
|
||||
@@ -292,13 +291,13 @@ Firefox.prototype.notImplementedCmds = function() {
|
||||
Firefox.prototype.initCommandMap = function() {
|
||||
var nyiCmds = this.notImplementedCmds();
|
||||
// create controller functions dynamically for implemented commands
|
||||
_.each(this.cmdMap(), _.bind(function(cmdInfo, controller) {
|
||||
_.each(this.cmdMap(), function(cmdInfo, controller) {
|
||||
if (_.contains(nyiCmds, controller)) {
|
||||
throw new Error("Controller " + controller + " is listed in both " +
|
||||
"implemented and not-yet-implemented lists. Fix this " +
|
||||
"before moving on!");
|
||||
}
|
||||
this[controller] = _.bind(function() {
|
||||
this[controller] = function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var cb;
|
||||
var outerCb = args[args.length - 1];
|
||||
@@ -319,16 +318,16 @@ Firefox.prototype.initCommandMap = function() {
|
||||
cmd = _.extend(cmd, {value: args[0]});
|
||||
}
|
||||
this.proxy(cmd, cb);
|
||||
}, this);
|
||||
}, this));
|
||||
}.bind(this);
|
||||
}.bind(this));
|
||||
// throw not yet implemented for any command in nyi list
|
||||
_.each(nyiCmds, _.bind(function(controller) {
|
||||
this[controller] = _.bind(function() {
|
||||
_.each(nyiCmds, function(controller) {
|
||||
this[controller] = function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var cb = args[args.length - 1];
|
||||
cb(new NotYetImplementedError(), null);
|
||||
}, this);
|
||||
}, this));
|
||||
}.bind(this);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
module.exports = function(opts) {
|
||||
|
||||
@@ -81,7 +81,7 @@ RemoteDebugger.prototype.connect = function(cb, pageChangeCb) {
|
||||
this.socketGone = true;
|
||||
me.onAppDisconnect();
|
||||
});
|
||||
this.socket.on('data', _.bind(me.receive, this));
|
||||
this.socket.on('data', me.receive.bind(this));
|
||||
|
||||
this.socket.connect(27753, '::1', function () {
|
||||
logger.info("Debugger socket connected to " + me.socket.remoteAddress +
|
||||
@@ -117,10 +117,10 @@ RemoteDebugger.prototype.selectApp = function(appIdKey, cb) {
|
||||
this.appIdKey = appIdKey;
|
||||
var connectToApp = messages.connectToApp(this.connId, this.appIdKey);
|
||||
logger.info("Selecting app");
|
||||
this.send(connectToApp, _.bind(function(pageDict) {
|
||||
this.send(connectToApp, function(pageDict) {
|
||||
cb(this.pageArrayFromDict(pageDict));
|
||||
this.specialCbs['_rpc_forwardGetListing:'] = _.bind(this.onPageChange, this);
|
||||
}, this));
|
||||
this.specialCbs['_rpc_forwardGetListing:'] = this.onPageChange.bind(this);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
RemoteDebugger.prototype.pageArrayFromDict = function(pageDict) {
|
||||
@@ -137,7 +137,6 @@ RemoteDebugger.prototype.pageArrayFromDict = function(pageDict) {
|
||||
};
|
||||
|
||||
RemoteDebugger.prototype.selectPage = function(pageIdKey, cb, skipReadyCheck) {
|
||||
var me = this;
|
||||
if (typeof skipReadyCheck === "undefined") {
|
||||
skipReadyCheck = false;
|
||||
}
|
||||
@@ -148,22 +147,22 @@ RemoteDebugger.prototype.selectPage = function(pageIdKey, cb, skipReadyCheck) {
|
||||
logger.info("Selecting page " + pageIdKey + " and forwarding socket setup");
|
||||
this.send(setSenderKey, function() {
|
||||
logger.info("Set sender key");
|
||||
var enablePage = messages.enablePage(me.appIdKey, me.connId,
|
||||
me.senderId, me.pageIdKey, me.debuggerType);
|
||||
me.send(enablePage, function() {
|
||||
var enablePage = messages.enablePage(this.appIdKey, this.connId,
|
||||
this.senderId, this.pageIdKey, this.debuggerType);
|
||||
this.send(enablePage, function() {
|
||||
logger.info("Enabled activity on page");
|
||||
if (skipReadyCheck) {
|
||||
cb();
|
||||
} else {
|
||||
me.checkPageIsReady(function(err, isReady) {
|
||||
this.checkPageIsReady(function(err, isReady) {
|
||||
if (!isReady) {
|
||||
return me.pageUnload(cb);
|
||||
return this.pageUnload(cb);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}.bind(this));
|
||||
}
|
||||
});
|
||||
});
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
RemoteDebugger.prototype.checkPageIsReady = function(cb) {
|
||||
@@ -312,12 +311,11 @@ RemoteDebugger.prototype.executeAtomAsync = function(atom, args, frames, respons
|
||||
};
|
||||
|
||||
RemoteDebugger.prototype.execute = function(command, cb, override) {
|
||||
var me = this;
|
||||
if (this.pageLoading && !override) {
|
||||
logger.info("Trying to execute but page is not loaded. Waiting for dom");
|
||||
this.waitForDom(function() {
|
||||
me.execute(command, cb);
|
||||
});
|
||||
this.execute(command, cb);
|
||||
}.bind(this));
|
||||
} else {
|
||||
if(this.debuggerType === this.debuggerTypeEnum.webinspector){
|
||||
assert.ok(this.connId); assert.ok(this.appIdKey); assert.ok(this.senderId);
|
||||
@@ -345,31 +343,29 @@ RemoteDebugger.prototype.navToUrl = function(url, cb) {
|
||||
assert.ok(this.pageIdKey);
|
||||
}
|
||||
logger.info("Navigating to new URL: " + url);
|
||||
var me = this;
|
||||
var navToUrl = messages.setUrl(url, this.appIdKey, this.connId,
|
||||
this.senderId, this.pageIdKey, this.debuggerType);
|
||||
this.send(navToUrl, noop);
|
||||
setTimeout(function() {
|
||||
me.waitForFrameNavigated(function() {
|
||||
me.waitForDom(cb);
|
||||
});
|
||||
}, 1000);
|
||||
this.waitForFrameNavigated(function() {
|
||||
this.waitForDom(cb);
|
||||
}.bind(this));
|
||||
}.bind(this), 1000);
|
||||
};
|
||||
|
||||
RemoteDebugger.prototype.pageLoad = function() {
|
||||
clearTimeout(this.loadingTimeout);
|
||||
var me = this
|
||||
, cbs = this.pageLoadedCbs
|
||||
var cbs = this.pageLoadedCbs
|
||||
, waitMs = 60000
|
||||
, intMs = 500
|
||||
, start = Date.now();
|
||||
logger.debug("Page loaded, verifying through readyState");
|
||||
var verify = function() {
|
||||
me.checkPageIsReady(function(err, isReady) {
|
||||
this.checkPageIsReady(function(err, isReady) {
|
||||
if (isReady || (start + waitMs) < Date.now()) {
|
||||
logger.debug("Page is ready, calling onload cbs");
|
||||
me.pageLoadedCbs = [];
|
||||
me.pageLoading = false;
|
||||
this.pageLoadedCbs = [];
|
||||
this.pageLoading = false;
|
||||
_.each(cbs, function(cb) {
|
||||
cb();
|
||||
});
|
||||
@@ -378,7 +374,7 @@ RemoteDebugger.prototype.pageLoad = function() {
|
||||
setTimeout(verify, intMs);
|
||||
}
|
||||
});
|
||||
};
|
||||
}.bind(this);
|
||||
verify();
|
||||
};
|
||||
|
||||
@@ -400,18 +396,17 @@ RemoteDebugger.prototype.pageUnload = function(cb) {
|
||||
};
|
||||
|
||||
RemoteDebugger.prototype.waitForDom = function(cb) {
|
||||
var me = this;
|
||||
logger.debug("Waiting for dom...");
|
||||
if (typeof cb === "function") {
|
||||
this.pageLoadedCbs.push(cb);
|
||||
}
|
||||
me.pageLoad();
|
||||
this.pageLoad();
|
||||
};
|
||||
|
||||
RemoteDebugger.prototype.waitForFrameNavigated = function(cb) {
|
||||
logger.debug("Waiting for frame navigated...");
|
||||
this.frameNavigatedCbs.push(cb);
|
||||
this.navigatingTimeout = setTimeout(_.bind(this.frameNavigated, this), 500);
|
||||
this.navigatingTimeout = setTimeout(this.frameNavigated.bind(this), 500);
|
||||
};
|
||||
|
||||
// ====================================
|
||||
@@ -444,21 +439,20 @@ RemoteDebugger.prototype.handleSpecialMessage = function(specialCb) {
|
||||
};
|
||||
|
||||
RemoteDebugger.prototype.setHandlers = function() {
|
||||
var me = this;
|
||||
this.handlers = {
|
||||
'_rpc_reportSetup:': function (plist) {
|
||||
me.handleSpecialMessage('_rpc_reportIdentifier:',
|
||||
this.handleSpecialMessage('_rpc_reportIdentifier:',
|
||||
plist.__argument.WIRSimulatorNameKey,
|
||||
plist.__argument.WIRSimulatorBuildKey);
|
||||
},
|
||||
}.bind(this),
|
||||
'_rpc_reportConnectedApplicationList:': function (plist) {
|
||||
me.handleSpecialMessage('connect',
|
||||
this.handleSpecialMessage('connect',
|
||||
plist.__argument.WIRApplicationDictionaryKey);
|
||||
},
|
||||
}.bind(this),
|
||||
'_rpc_applicationSentListing:': function (plist) {
|
||||
me.handleSpecialMessage('_rpc_forwardGetListing:',
|
||||
this.handleSpecialMessage('_rpc_forwardGetListing:',
|
||||
plist.__argument.WIRListingKey);
|
||||
},
|
||||
}.bind(this),
|
||||
'_rpc_applicationSentData:': function(plist) {
|
||||
var dataKey = JSON.parse(plist.__argument.WIRMessageDataKey.toString('utf8'))
|
||||
, msgId = dataKey.id
|
||||
@@ -472,20 +466,20 @@ RemoteDebugger.prototype.setHandlers = function() {
|
||||
"do some kind of callback here");
|
||||
//me.onPageChange();
|
||||
} else if (dataKey.method == "Page.frameNavigated") {
|
||||
if (!me.willNavigateWithoutReload && !me.pageLoading) {
|
||||
if (!this.willNavigateWithoutReload && !this.pageLoading) {
|
||||
logger.info("Frame navigated, unloading page");
|
||||
me.frameNavigated();
|
||||
this.frameNavigated();
|
||||
} else {
|
||||
logger.info("Frame navigated but we were warned about it, not " +
|
||||
"considering page state unloaded");
|
||||
me.willNavigateWithoutReload = false;
|
||||
this.willNavigateWithoutReload = false;
|
||||
}
|
||||
} else if (dataKey.method == "Page.loadEventFired") {
|
||||
me.pageLoad();
|
||||
} else if (typeof me.dataCbs[msgId] === "function") {
|
||||
me.dataCbs[msgId](error, result);
|
||||
me.dataCbs[msgId] = null;
|
||||
} else if (me.dataCbs[msgId] === null) {
|
||||
this.pageLoad();
|
||||
} else if (typeof this.dataCbs[msgId] === "function") {
|
||||
this.dataCbs[msgId](error, result);
|
||||
this.dataCbs[msgId] = null;
|
||||
} else if (this.dataCbs[msgId] === null) {
|
||||
logger.error("Debugger returned data for message " + msgId +
|
||||
"but we already ran that callback! WTF??");
|
||||
} else {
|
||||
@@ -498,7 +492,7 @@ RemoteDebugger.prototype.setHandlers = function() {
|
||||
" error: " + error);
|
||||
}
|
||||
}
|
||||
},
|
||||
}.bind(this),
|
||||
'_rpc_applicationDisconnected:': this.onAppDisconnect
|
||||
};
|
||||
};
|
||||
|
||||
@@ -46,7 +46,6 @@ _.extend(WebKitRemoteDebugger.prototype, RemoteDebugger.prototype);
|
||||
// ====================================
|
||||
|
||||
WebKitRemoteDebugger.prototype.connect = function(pageId, cb, pageChangeCb) {
|
||||
var me = this;
|
||||
this.frameNavigatedCbs = [];
|
||||
this.pageChangeCb = pageChangeCb;
|
||||
var url = 'ws://' + this.host + ':' + this.port + '/devtools/page/' + pageId;
|
||||
@@ -58,21 +57,20 @@ WebKitRemoteDebugger.prototype.connect = function(pageId, cb, pageChangeCb) {
|
||||
});
|
||||
this.socket.on('close', function() {
|
||||
logger.info("Disconnecting from remote debugger");
|
||||
me.socket = null;
|
||||
});
|
||||
this.socket = null;
|
||||
}.bind(this));
|
||||
this.socket.on('error', function(exception) {
|
||||
console.log('Debugger web socket error %s', exception);
|
||||
});
|
||||
this.socket.on('message', function(data) {
|
||||
console.log('Debugger web socket received data : %s', data);
|
||||
me.receive(data);
|
||||
});
|
||||
this.receive(data);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
WebKitRemoteDebugger.prototype.disconnect = function() {
|
||||
var me = this;
|
||||
if(this.isConnected()){
|
||||
me.socket.close(1001);
|
||||
this.socket.close(1001);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -136,15 +134,14 @@ WebKitRemoteDebugger.prototype.handleMessage = function(data, method) {
|
||||
};
|
||||
|
||||
WebKitRemoteDebugger.prototype.setHandlers = function() {
|
||||
var me = this;
|
||||
this.handlers = {
|
||||
'Runtime.evaluate': function (data) {
|
||||
var msgId = data.id
|
||||
, result = data.result
|
||||
, error = data.error || null;
|
||||
me.dataCbs[msgId](error, result);
|
||||
me.dataCbs[msgId] = null;
|
||||
} ,
|
||||
this.dataCbs[msgId](error, result);
|
||||
this.dataCbs[msgId] = null;
|
||||
}.bind(this) ,
|
||||
'Profiler.resetProfiles' : function(data) {
|
||||
logger.info("Device is telling us to reset profiles. Should probably " +
|
||||
"do some kind of callback here");
|
||||
@@ -158,7 +155,6 @@ WebKitRemoteDebugger.prototype.setHandlers = function() {
|
||||
// ====================================
|
||||
|
||||
WebKitRemoteDebugger.prototype.send = function (data, cb) {
|
||||
var me = this;
|
||||
//increase the current id
|
||||
this.curMsgId += 1;
|
||||
//set the id in the message
|
||||
|
||||
467
app/ios.js
467
app/ios.js
File diff suppressed because it is too large
Load Diff
@@ -28,16 +28,15 @@ var Selendroid = function(opts) {
|
||||
|
||||
Selendroid.prototype.start = function(cb) {
|
||||
logger.info("Starting selendroid server");
|
||||
var opts = _.clone(this.opts)
|
||||
, me = this;
|
||||
var opts = _.clone(this.opts);
|
||||
opts.devicePort = 8080; // selendroid listens on 8080 on the device
|
||||
this.adb = new adb(opts);
|
||||
|
||||
async.waterfall([
|
||||
function(cb) { me.ensureServerExists(cb); },
|
||||
function(cb) { me.adb.startSelendroid(me.serverApk, cb); },
|
||||
function(res, cb) { me.waitForServer(cb); },
|
||||
function(cb) { me.createSession(cb); },
|
||||
function(cb) { this.ensureServerExists(cb); }.bind(this),
|
||||
function(cb) { this.adb.startSelendroid(this.serverApk, cb); }.bind(this),
|
||||
function(res, cb) { this.waitForServer(cb); }.bind(this),
|
||||
function(cb) { this.createSession(cb); }.bind(this),
|
||||
], cb);
|
||||
};
|
||||
|
||||
@@ -62,7 +61,7 @@ Selendroid.prototype.ensureServerExists = function(cb) {
|
||||
logger.info("Checking whether selendroid is built yet");
|
||||
var selBin = path.resolve(__dirname, "..", "build", "selendroid",
|
||||
"selendroid.apk");
|
||||
fs.stat(selBin, _.bind(function(err) {
|
||||
fs.stat(selBin, function(err) {
|
||||
if (err) {
|
||||
logger.info("Selendroid needs to be built; please run ./reset.sh " +
|
||||
"--selendroid");
|
||||
@@ -71,7 +70,7 @@ Selendroid.prototype.ensureServerExists = function(cb) {
|
||||
logger.info("Selendroid server exists!");
|
||||
this.serverApk = selBin;
|
||||
cb(null);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Selendroid.prototype.waitForServer = function(cb) {
|
||||
@@ -79,7 +78,7 @@ Selendroid.prototype.waitForServer = function(cb) {
|
||||
, intMs = 800
|
||||
, start = Date.now();
|
||||
|
||||
var pingServer = _.bind(function() {
|
||||
var pingServer = function() {
|
||||
this.proxyTo('/wd/hub/status', 'GET', null, function(err, res, body) {
|
||||
if (body === null || typeof body === "undefined" || !body.trim()) {
|
||||
if (Date.now() - start < waitMs) {
|
||||
@@ -93,7 +92,7 @@ Selendroid.prototype.waitForServer = function(cb) {
|
||||
cb(null);
|
||||
}
|
||||
});
|
||||
}, this);
|
||||
}.bind(this);
|
||||
|
||||
pingServer();
|
||||
};
|
||||
@@ -128,11 +127,11 @@ Selendroid.prototype.createSession = function(cb) {
|
||||
|
||||
Selendroid.prototype.deleteSession = function(cb) {
|
||||
var url = '/wd/hub/session/' + this.selendroidSessionId;
|
||||
this.proxyTo(url, 'DELETE', null, _.bind(function(err, res) {
|
||||
this.proxyTo(url, 'DELETE', null, function(err, res) {
|
||||
if (err) return cb(err);
|
||||
if (res.statusCode !== 200) return cb(new Error("Status was not 200"));
|
||||
this.adb.stopApp(cb);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Selendroid.prototype.proxyTo = proxyTo;
|
||||
|
||||
@@ -346,13 +346,12 @@ $.extend(au, {
|
||||
}
|
||||
|
||||
, _returnElems: function(elems) {
|
||||
var results = []
|
||||
, me = this;
|
||||
var results = [];
|
||||
|
||||
elems.each(function(e, el) {
|
||||
var elid = me.getId(el);
|
||||
var elid = this.getId(el);
|
||||
results.push({ELEMENT: elid});
|
||||
});
|
||||
}.bind(this));
|
||||
|
||||
return {
|
||||
status: codes.Success.code,
|
||||
|
||||
@@ -66,9 +66,9 @@ Instruments.prototype.startSocketServer = function(sock) {
|
||||
this.exitHandler(1);
|
||||
};
|
||||
|
||||
var socketConnectTimeout = setTimeout(_.bind(onSocketNeverConnect, this), 300000);
|
||||
var socketConnectTimeout = setTimeout(onSocketNeverConnect.bind(this), 300000);
|
||||
|
||||
this.socketServer = net.createServer({allowHalfOpen: true}, _.bind(function(conn) {
|
||||
this.socketServer = net.createServer({allowHalfOpen: true}, function(conn) {
|
||||
if (!this.hasConnected) {
|
||||
this.hasConnected = true;
|
||||
this.debug("Instruments is ready to receive commands");
|
||||
@@ -78,21 +78,21 @@ Instruments.prototype.startSocketServer = function(sock) {
|
||||
}
|
||||
conn.setEncoding('utf8'); // get strings from sockets rather than buffers
|
||||
|
||||
conn.on('data', _.bind(function(data) {
|
||||
conn.on('data', function(data) {
|
||||
// when data comes in, route it according to the "event" property
|
||||
this.debug("Socket data received (" + data.length + " bytes)");
|
||||
this.bufferedData += data;
|
||||
}, this));
|
||||
}.bind(this));
|
||||
|
||||
this.currentSocket = conn;
|
||||
//this.debug("Socket Connected");
|
||||
|
||||
conn.on('close', _.bind(function() {
|
||||
conn.on('close', function() {
|
||||
//this.debug("Socket Completely closed");
|
||||
this.currentSocket = null;
|
||||
}, this));
|
||||
}.bind(this));
|
||||
|
||||
conn.on('end', _.bind(function() {
|
||||
conn.on('end', function() {
|
||||
//this.debug("Socket closed by other side");
|
||||
var data = this.bufferedData;
|
||||
this.bufferedData = "";
|
||||
@@ -117,25 +117,25 @@ Instruments.prototype.startSocketServer = function(sock) {
|
||||
"' which doesn't exist");
|
||||
} else {
|
||||
this.debug("Socket data being routed for '" + data.event + "' event");
|
||||
_.bind(this.eventRouter[data.event], this)(data, conn);
|
||||
this.eventRouter[data.event].bind(this)(data, conn);
|
||||
}
|
||||
}, this));
|
||||
}.bind(this));
|
||||
|
||||
}, this));
|
||||
}.bind(this));
|
||||
|
||||
this.socketServer.on('close', _.bind(function() {
|
||||
this.socketServer.on('close', function() {
|
||||
this.debug("Instruments socket server closed");
|
||||
}, this));
|
||||
}.bind(this));
|
||||
|
||||
exec('xcrun -find instruments', _.bind(function (error, stdout) {
|
||||
exec('xcrun -find instruments', function (error, stdout) {
|
||||
this.instrumentsPath = stdout.trim();
|
||||
logger.info("instruments is: " + this.instrumentsPath);
|
||||
|
||||
this.socketServer.listen(sock, _.bind(function() {
|
||||
this.socketServer.listen(sock, function() {
|
||||
this.debug("Instruments socket server started at " + sock);
|
||||
this.launch();
|
||||
}, this));
|
||||
}, this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Instruments.prototype.launch = function() {
|
||||
@@ -221,14 +221,14 @@ Instruments.prototype.commandHandler = function(data, c) {
|
||||
this.curCommand = null;
|
||||
}
|
||||
|
||||
this.waitForCommand(_.bind(function() {
|
||||
this.waitForCommand(function() {
|
||||
this.curCommand = this.commandQueue.shift();
|
||||
this.onReceiveCommand = null;
|
||||
this.debug("Sending command to instruments: " + this.curCommand.cmd);
|
||||
c.write(JSON.stringify({nextCommand: this.curCommand.cmd}));
|
||||
c.end();
|
||||
//this.debug("Closing our half of the connection");
|
||||
}, this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Instruments.prototype.waitForCommand = function(cb) {
|
||||
|
||||
@@ -57,16 +57,16 @@ describeWd('calc app', function(h) {
|
||||
};
|
||||
|
||||
it('should fill two fields with numbers', function(done) {
|
||||
populate("elem", h.driver, _.bind(computeAndCheck, this, h.driver, done));
|
||||
populate("elem", h.driver, computeAndCheck.bind(this, h.driver, done));
|
||||
});
|
||||
|
||||
// using sendKeysToActiveElement
|
||||
it('should fill two fields with numbers - sendKeys', function(done) {
|
||||
populate("driver", h.driver, _.bind(computeAndCheck, this, h.driver, done));
|
||||
populate("driver", h.driver, computeAndCheck.bind(this, h.driver, done));
|
||||
});
|
||||
|
||||
it('should fill two fields with numbers - setValue', function(done) {
|
||||
populate("elem-setvalue", h.driver, _.bind(computeAndCheck, this, h.driver, done));
|
||||
populate("elem-setvalue", h.driver, computeAndCheck.bind(this, h.driver, done));
|
||||
});
|
||||
|
||||
it('should confirm that button is displayed', function(done){
|
||||
|
||||
Reference in New Issue
Block a user