mirror of
https://github.com/appium/appium.git
synced 2026-02-08 19:00:16 -06:00
339 lines
9.8 KiB
JavaScript
339 lines
9.8 KiB
JavaScript
"use strict";
|
|
|
|
var errors = require('../server/errors.js')
|
|
, request = require('request')
|
|
, _ = require('underscore')
|
|
, exec = require('child_process').exec
|
|
, status = require("../server/status.js")
|
|
, logger = require('../server/logger.js').get('appium')
|
|
, logDeprecationWarning = require('../helpers.js').logDeprecationWarning;
|
|
|
|
var UnknownError = errors.UnknownError
|
|
, ProtocolError = errors.ProtocolError;
|
|
|
|
exports.respond = function (response, cb) {
|
|
if (typeof response === 'undefined') {
|
|
cb(null, '');
|
|
} else {
|
|
if (typeof(response) !== "object") {
|
|
cb(new UnknownError(), response);
|
|
} else if (!('status' in response)) {
|
|
cb(new ProtocolError('Status missing in response from device'), response);
|
|
} else {
|
|
var status = parseInt(response.status, 10);
|
|
if (isNaN(status)) {
|
|
cb(new ProtocolError('Invalid status in response from device'), response);
|
|
} else {
|
|
response.status = status;
|
|
cb(null, response);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
exports.proxy = function (command, cb) {
|
|
// was thinking we should use a queue for commands instead of writing to a file
|
|
logger.info('Pushing command to appium work queue: ' + JSON.stringify(command));
|
|
this.push([command, cb]);
|
|
};
|
|
|
|
exports.proxyWithMinTime = function (command, ms, cb) {
|
|
// was thinking we should use a queue for commands instead of writing to a file
|
|
var start = Date.now();
|
|
logger.info('Pushing command to appium work queue: ' + JSON.stringify(command));
|
|
this.push([command, function () {
|
|
var args = Array.prototype.slice.call(arguments, 0);
|
|
var waitNeeded = ms - (Date.now() - start);
|
|
if (waitNeeded > 0) {
|
|
setTimeout(function () {
|
|
cb.apply(null, args);
|
|
}, waitNeeded);
|
|
} else {
|
|
cb.apply(null, args);
|
|
}
|
|
}]);
|
|
};
|
|
|
|
exports.waitForCondition = function (waitMs, condFn, cb, intervalMs) {
|
|
if (typeof intervalMs === "undefined") {
|
|
intervalMs = 500;
|
|
}
|
|
var begunAt = Date.now();
|
|
var endAt = begunAt + waitMs;
|
|
var spin = function () {
|
|
condFn(function (condMet) {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
if (condMet) {
|
|
cb.apply(this, args.slice(1));
|
|
} else if (Date.now() < endAt) {
|
|
setTimeout(spin, intervalMs);
|
|
} else {
|
|
cb.apply(this, args.slice(1));
|
|
}
|
|
}.bind(this));
|
|
}.bind(this);
|
|
spin();
|
|
};
|
|
|
|
exports.doRequest = function (url, method, body, contentType, cb) {
|
|
if (typeof cb === "undefined" && typeof contentType === "function") {
|
|
cb = contentType;
|
|
contentType = null;
|
|
}
|
|
if (typeof contentType === "undefined" || contentType === null) {
|
|
contentType = "application/json;charset=UTF-8";
|
|
}
|
|
if (!(/^https?:\/\//.exec(url))) {
|
|
url = 'http://' + url;
|
|
}
|
|
var opts = {
|
|
url: url
|
|
, method: method
|
|
};
|
|
if (_.contains(['put', 'post', 'patch'], method.toLowerCase())) {
|
|
if (typeof body === "object") {
|
|
opts.json = body;
|
|
} else {
|
|
opts.body = body || "";
|
|
}
|
|
}
|
|
// explicitly set these headers with correct capitalization to work around
|
|
// an issue in node/requests
|
|
logger.info("Making http request with opts: " + JSON.stringify(opts));
|
|
request(opts, function (err, res, body) {
|
|
cb(err, res, body);
|
|
});
|
|
};
|
|
|
|
exports.isAppInstalled = function (isInstalledCommand, cb) {
|
|
logger.info("Checking app install status using: " + isInstalledCommand);
|
|
exec(isInstalledCommand, function (error, stdout) {
|
|
cb(error, stdout);
|
|
});
|
|
};
|
|
|
|
exports.removeApp = function (removeCommand, udid, bundleId, cb) {
|
|
logger.info("Removing app using cmd: " + removeCommand);
|
|
exec(removeCommand, function (error) {
|
|
if (error !== null) {
|
|
cb(new Error('Unable to un-install [' + bundleId + '] from device with id [' + udid + ']. Error [' + error + ']'));
|
|
} else {
|
|
cb(error, 'Successfully un-installed [' + bundleId + '] from device with id [' + udid + ']');
|
|
}
|
|
});
|
|
};
|
|
|
|
exports.installApp = function (installationCommand, udid, unzippedAppPath, cb) {
|
|
logger.info("Installing app using cmd: " + installationCommand);
|
|
exec(installationCommand, { maxBuffer: 524288 }, function (error) {
|
|
if (error !== null) {
|
|
cb(new Error('Unable to install [' + unzippedAppPath + '] to device with id [' + udid + ']. Error [' + error + ']'));
|
|
} else {
|
|
cb(error, 'Successfully unzipped and installed [' + unzippedAppPath + '] to device with id [' + udid + ']');
|
|
}
|
|
});
|
|
};
|
|
|
|
exports.unpackApp = function (req, packageExtension, cb) {
|
|
var reqAppPath = req.body.appPath;
|
|
if (reqAppPath.toLowerCase().substring(0, 4) === "http") {
|
|
req.appium.downloadAndUnzipApp(reqAppPath, function (err, appPath) {
|
|
cb(appPath);
|
|
});
|
|
} else if (reqAppPath.toLowerCase().substring(reqAppPath.length - 4) === ".zip") {
|
|
req.appium.unzipLocalApp(reqAppPath, function (err, appPath) {
|
|
cb(appPath);
|
|
});
|
|
} else if (reqAppPath.toLowerCase().substring(reqAppPath.length - 4) === packageExtension) {
|
|
cb(reqAppPath.toString());
|
|
} else {
|
|
cb(null);
|
|
}
|
|
};
|
|
|
|
exports.proxyTo = function (endpoint, method, data, cb) {
|
|
if (endpoint[0] !== '/') {
|
|
endpoint = '/' + endpoint;
|
|
}
|
|
var url = 'http://' + this.proxyHost + ':' + this.proxyPort + endpoint;
|
|
exports.doRequest(url, method, data ? data : '', cb);
|
|
};
|
|
|
|
exports.parseExecuteResponse = function (response, cb) {
|
|
if ((response.value !== null) && (typeof response.value !== "undefined")) {
|
|
var wdElement = null;
|
|
if (!_.isArray(response.value)) {
|
|
if (typeof response.value.ELEMENT !== "undefined") {
|
|
wdElement = response.value.ELEMENT;
|
|
wdElement = this.parseElementResponse(response.value);
|
|
if (wdElement === null) {
|
|
cb(null, {
|
|
status: status.codes.UnknownError.code
|
|
, value: "Error converting element ID atom for using in WD: " + response.value.ELEMENT
|
|
});
|
|
}
|
|
response.value = wdElement;
|
|
}
|
|
} else {
|
|
var args = response.value;
|
|
for (var i = 0; i < args.length; i++) {
|
|
wdElement = args[i];
|
|
if ((args[i] !== null) && (typeof args[i].ELEMENT !== "undefined")) {
|
|
wdElement = this.parseElementResponse(args[i]);
|
|
if (wdElement === null) {
|
|
cb(null, {
|
|
status: status.codes.UnknownError.code
|
|
, value: "Error converting element ID atom for using in WD: " + args[i].ELEMENT
|
|
});
|
|
return;
|
|
}
|
|
args[i] = wdElement;
|
|
}
|
|
}
|
|
response.value = args;
|
|
}
|
|
}
|
|
return response;
|
|
};
|
|
|
|
exports.checkValidLocStrat = function (strat, includeWeb, cb) {
|
|
if (typeof includeWeb === "undefined") {
|
|
includeWeb = false;
|
|
}
|
|
var validStrats = [
|
|
'xpath',
|
|
'id',
|
|
'name',
|
|
'dynamic',
|
|
'class name'
|
|
];
|
|
var nativeStrats = [
|
|
'-ios uiautomation',
|
|
'accessibility id',
|
|
'-android uiautomator'
|
|
];
|
|
var webStrats = [
|
|
'link text',
|
|
'css selector',
|
|
'tag name',
|
|
'partial link text'
|
|
];
|
|
var nativeDeprecations = {};
|
|
var webDeprecations = {};
|
|
var deprecations = {};
|
|
|
|
if (includeWeb) {
|
|
validStrats = validStrats.concat(webStrats);
|
|
deprecations = webDeprecations;
|
|
} else {
|
|
validStrats = validStrats.concat(nativeStrats);
|
|
deprecations = nativeDeprecations;
|
|
}
|
|
if (!_.contains(validStrats, strat)) {
|
|
logger.info("Invalid locator strategy: " + strat);
|
|
cb(null, {
|
|
status: status.codes.UnknownCommand.code,
|
|
value: {message: "Invalid locator strategy: " + strat}
|
|
});
|
|
return false;
|
|
} else {
|
|
if (_.has(deprecations, strat)) {
|
|
logDeprecationWarning('locator strategy', strat, deprecations[strat]);
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
exports.parseElementResponse = function (element) {
|
|
var objId = element.ELEMENT
|
|
, clientId = (5000 + this.webElementIds.length).toString();
|
|
this.webElementIds.push(objId);
|
|
return {ELEMENT: clientId};
|
|
};
|
|
|
|
exports.getAtomsElement = function (wdId) {
|
|
var atomsId;
|
|
try {
|
|
atomsId = this.webElementIds[parseInt(wdId, 10) - 5000];
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
if (typeof atomsId === "undefined") {
|
|
return null;
|
|
}
|
|
return {'ELEMENT': atomsId};
|
|
};
|
|
|
|
exports.useAtomsElement = function (elementId, failCb, cb) {
|
|
if (parseInt(elementId, 10) < 5000) {
|
|
logger.info("Element with id " + elementId + " passed in for use with " +
|
|
"atoms, but it's out of our internal scope. Adding 5000");
|
|
elementId = (parseInt(elementId, 10) + 5000).toString();
|
|
}
|
|
var atomsElement = this.getAtomsElement(elementId);
|
|
if (atomsElement === null) {
|
|
failCb(null, {
|
|
status: status.codes.UnknownError.code
|
|
, value: "Error converting element ID for using in WD atoms: " + elementId
|
|
});
|
|
} else {
|
|
cb(atomsElement);
|
|
}
|
|
};
|
|
|
|
exports.convertElementForAtoms = function (args, cb) {
|
|
for (var i = 0; i < args.length; i++) {
|
|
if (args[i] !== null && typeof args[i].ELEMENT !== "undefined") {
|
|
var atomsElement = this.getAtomsElement(args[i].ELEMENT);
|
|
if (atomsElement === null) {
|
|
cb(true, {
|
|
status: status.codes.UnknownError.code
|
|
, value: "Error converting element ID for using in WD atoms: " + args[i].ELEMENT
|
|
});
|
|
return;
|
|
}
|
|
args[i] = atomsElement;
|
|
}
|
|
}
|
|
cb(null, args);
|
|
};
|
|
|
|
exports.jwpError = function (err, code, cb) {
|
|
if (typeof code === "function") {
|
|
cb = code;
|
|
code = null;
|
|
}
|
|
if (code) {
|
|
return cb(null, {
|
|
status: code
|
|
, value: err.message
|
|
});
|
|
}
|
|
return cb(err);
|
|
};
|
|
|
|
exports.jwpSuccess = function (val, cb) {
|
|
if (typeof val === "function") {
|
|
cb = val;
|
|
val = null;
|
|
}
|
|
return cb(null, {
|
|
status: status.codes.Success.code
|
|
, value: val
|
|
});
|
|
};
|
|
|
|
exports.jwpResponse = function (err, val, cb) {
|
|
if (typeof err === "function") {
|
|
return exports.jwpSuccess(err);
|
|
}
|
|
if (typeof val === "function") {
|
|
cb = val;
|
|
val = null;
|
|
}
|
|
if (err) {
|
|
return exports.jwpError(err, status.codes.UnknownError.code, cb);
|
|
}
|
|
return exports.jwpSuccess(val, cb);
|
|
};
|