mirror of
https://github.com/appium/appium.git
synced 2026-10-28 04:14:00 +08:00
398 lines
10 KiB
JavaScript
398 lines
10 KiB
JavaScript
"use strict";
|
|
var path = require('path')
|
|
, rimraf = require('rimraf')
|
|
, fs = require('fs')
|
|
, logger = require('../logger').get('appium')
|
|
, sock = '/tmp/instruments_sock'
|
|
, instruments = require('../instruments/instruments')
|
|
, delay = require("./helpers.js").delay
|
|
, uuid = require('uuid-js')
|
|
, status = require("./uiauto/lib/status");
|
|
|
|
var UnknownError = function(message) {
|
|
this.message = message? message : "Invalid response from device";
|
|
this.name = "UnknownError";
|
|
};
|
|
|
|
var ProtocolError = function(message) {
|
|
this.message = message;
|
|
this.name = "ProtocolError";
|
|
};
|
|
|
|
var IOS = function(rest, app, udid, verbose, removeTraceDir) {
|
|
this.rest = rest;
|
|
this.app = app;
|
|
this.udid = udid;
|
|
this.verbose = verbose;
|
|
this.instruments = null;
|
|
this.queue = [];
|
|
this.progress = 0;
|
|
this.removeTraceDir = removeTraceDir;
|
|
this.onStop = function(code, traceDir) {};
|
|
this.capabilities = {
|
|
version: '6.0'
|
|
, webStorageEnabled: false
|
|
, locationContextEnabled: false
|
|
, browserName: 'iOS'
|
|
, platform: 'MAC'
|
|
, javascriptEnabled: true
|
|
, databaseEnabled: false
|
|
, takesScreenshot: true
|
|
};
|
|
};
|
|
|
|
IOS.prototype.start = function(cb, onDie) {
|
|
var me = this;
|
|
var didLaunch = false;
|
|
if (typeof onDie === "function") {
|
|
this.onStop = onDie;
|
|
}
|
|
|
|
var onLaunch = function() {
|
|
didLaunch = true;
|
|
logger.info('Instruments launched. Starting poll loop for new commands.');
|
|
me.instruments.setDebug(true);
|
|
cb(null);
|
|
};
|
|
|
|
var onExit = function(code, traceDir) {
|
|
if (!didLaunch) {
|
|
logger.error("Instruments did not launch successfully, failing session");
|
|
cb("Instruments did not launch successfully--please check your app " +
|
|
"paths or bundle IDs and try again");
|
|
}
|
|
this.instruments = null;
|
|
if (me.removeTraceDir && traceDir) {
|
|
rimraf(traceDir, function() {
|
|
me.onStop(code);
|
|
});
|
|
} else {
|
|
me.onStop(code);
|
|
}
|
|
};
|
|
|
|
if (this.instruments === null) {
|
|
this.instruments = instruments(
|
|
this.app
|
|
, this.udid
|
|
, path.resolve(__dirname, 'uiauto/bootstrap.js')
|
|
, path.resolve(__dirname, 'uiauto/Automation.tracetemplate')
|
|
, sock
|
|
, onLaunch
|
|
, onExit
|
|
);
|
|
}
|
|
|
|
};
|
|
|
|
IOS.prototype.stop = function(cb) {
|
|
var me = this;
|
|
if (cb) {
|
|
this.onStop = cb;
|
|
}
|
|
|
|
this.instruments.shutdown();
|
|
me.queue = [];
|
|
me.progress = 0;
|
|
};
|
|
|
|
IOS.prototype.setCommandTimeout = function(secs, cb) {
|
|
var cmd = "waitForDataTimeout = " + parseInt(secs, 10);
|
|
this.proxy(cmd, cb);
|
|
};
|
|
|
|
IOS.prototype.proxy = function(command, cb) {
|
|
// was thinking we should use a queue for commands instead of writing to a file
|
|
this.push([command, cb]);
|
|
logger.info('Pushed command to appium work queue: ' + command);
|
|
};
|
|
|
|
IOS.prototype.push = function(elem) {
|
|
this.queue.push(elem);
|
|
var me = this;
|
|
|
|
var next = function() {
|
|
if (me.queue.length <= 0 || me.progress > 0) {
|
|
return;
|
|
}
|
|
|
|
var target = me.queue.shift()
|
|
, command = target[0]
|
|
, cb = target[1];
|
|
|
|
me.progress++;
|
|
|
|
me.instruments.sendCommand(command, function(response) {
|
|
if (typeof cb === 'function') {
|
|
if (typeof response === 'undefined') {
|
|
cb(null, 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// maybe there's moar work to do
|
|
me.progress--;
|
|
next();
|
|
});
|
|
};
|
|
|
|
next();
|
|
};
|
|
|
|
IOS.prototype.findElementOrElements = function(selector, ctx, many, cb) {
|
|
var ext = many ? 's' : '';
|
|
if (typeof ctx === "undefined" || !ctx) {
|
|
ctx = 'null';
|
|
}
|
|
|
|
var command = ["au.getElement", ext, "ByType('", selector, "', ", ctx,")"].join('');
|
|
|
|
this.proxy(command, cb);
|
|
};
|
|
|
|
IOS.prototype.findElement = function(strategy, selector, cb) {
|
|
if (strategy === "name") {
|
|
var command = ['au.getElementByName("', selector, '")'].join('');
|
|
this.proxy(command, cb);
|
|
} else {
|
|
this.findElementOrElements(selector, null, false, cb);
|
|
}
|
|
};
|
|
|
|
IOS.prototype.findElements = function(strategy, selector, cb) {
|
|
this.findElementOrElements(selector, null, true, cb);
|
|
};
|
|
|
|
IOS.prototype.findElementFromElement = function(element, strategy, selector, cb) {
|
|
this.findElementOrElements(selector, element, false, cb);
|
|
};
|
|
|
|
IOS.prototype.findElementsFromElement = function(element, strategy, selector, cb) {
|
|
this.findElementOrElements(selector, element, true, cb);
|
|
};
|
|
|
|
IOS.prototype.setValue = function(elementId, value, cb) {
|
|
var command = ["au.getElement('", elementId, "').setValue('", value, "')"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.click = function(elementId, cb) {
|
|
var command = ["au.getElement('", elementId, "').tap()"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.clear = function(elementId, cb) {
|
|
var command = ["au.getElement('", elementId, "').setValue('')"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(null, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.getText = function(elementId, cb) {
|
|
var command = ["au.getElement('", elementId, "').value()"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.getAttribute = function(elementId, attributeName, cb) {
|
|
var command = ["au.getElement('", elementId, "').", attributeName, "()"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(null, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.getLocation = function(elementId, cb) {
|
|
var command = ["au.getElement('", elementId, "').getElementLocation()"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.getSize = function(elementId, cb) {
|
|
var command = ["au.getElement('", elementId, "').getElementSize()"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.keys = function(elementId, keys, cb) {
|
|
var command = ["sendKeysToActiveElement('", keys ,"')"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.frame = function(frame, cb) {
|
|
frame = frame? frame : 'mainWindow';
|
|
var command = ["wd_frame = ", frame].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.implicitWait = function(seconds, cb) {
|
|
var command = ["au.timeout('", seconds, "')"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.elementDisplayed = function(elementId, cb) {
|
|
var command = ["au.getElement('", elementId, "').isDisplayed()"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.elementEnabled = function(elementId, cb) {
|
|
var command = ["au.getElement('", elementId, "').isEnabled()"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.getPageSource = function(cb) {
|
|
this.proxy("wd_frame.getPageSource()", function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.getAlertText = function(cb) {
|
|
this.proxy("getAlertText()", function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.postAcceptAlert = function(cb) {
|
|
this.proxy("acceptAlert()", function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.postDismissAlert = function(cb) {
|
|
this.proxy("dismissAlert()", function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.getOrientation = function(cb) {
|
|
var command = "getScreenOrientation()";
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.setOrientation = function(orientation, cb) {
|
|
var command = ["setScreenOrientation('", orientation ,"')"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.getScreenshot = function(cb) {
|
|
var guid = uuid.create();
|
|
var command = ["au.capture('screenshot", guid ,"')"].join('');
|
|
|
|
var shotPath = ["/tmp/", this.instruments.guid, "/Run 1/screenshot", guid, ".png"].join("");
|
|
this.proxy(command, function(err, response) {
|
|
var delayTimes = 0;
|
|
var onErr = function() {
|
|
delayTimes++;
|
|
delay(0.2);
|
|
if (delayTimes <= 10) {
|
|
read(onErr);
|
|
} else {
|
|
read();
|
|
}
|
|
};
|
|
var read = function(onErr) {
|
|
fs.readFile(shotPath, function read(err, data) {
|
|
if (err) {
|
|
if (onErr) {
|
|
onErr();
|
|
} else {
|
|
response.value = '';
|
|
response.status = status.codes.UnknownError.code;
|
|
cb(err, null);
|
|
}
|
|
} else {
|
|
var b64data = new Buffer(data).toString('base64');
|
|
response.value = b64data;
|
|
cb(null, response);
|
|
}
|
|
});
|
|
};
|
|
read(onErr);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.flick = function(xSpeed, ySpeed, swipe, cb) {
|
|
var command = "";
|
|
if (swipe) {
|
|
command = ["touchSwipeFromSpeed(", xSpeed, ",", ySpeed,")"].join('');
|
|
}
|
|
else {
|
|
command = ["touchFlickFromSpeed(", xSpeed, ",", ySpeed,")"].join('');
|
|
}
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.flickElement = function(elementId, xoffset, yoffset, speed, cb) {
|
|
var command = ["au.getElement('", elementId, "').touchFlick(", xoffset, ",", yoffset, ",", speed, ")"].join('');
|
|
|
|
this.proxy(command, function(err, json) {
|
|
cb(err, json);
|
|
});
|
|
};
|
|
|
|
IOS.prototype.url = function(cb) {
|
|
// in the future, detect whether we have a UIWebView that we can use to
|
|
// make sense of this command. For now, and otherwise, it's a no-op
|
|
cb(null, {status: status.codes.Success.code, value: ''});
|
|
};
|
|
|
|
IOS.prototype.active = function(cb) {
|
|
this.proxy("au.getActiveElement()", function(err, json) {
|
|
cb(null, json);
|
|
});
|
|
};
|
|
|
|
module.exports = function(rest, app, udid, verbose, removeTraceDir) {
|
|
return new IOS(rest, app, udid, verbose, removeTraceDir);
|
|
};
|