mirror of
https://github.com/appium/appium.git
synced 2026-02-12 04:50:08 -06:00
336 lines
8.7 KiB
JavaScript
336 lines
8.7 KiB
JavaScript
"use strict";
|
|
|
|
var errors = require('./errors')
|
|
, _ = require('underscore')
|
|
, logger = require('../logger').get('appium')
|
|
, net = require('net')
|
|
, deviceCommon = require('./device')
|
|
, status = require("./uiauto/lib/status")
|
|
, getAtomSrc = require('./hybrid/firefoxos/firefoxos-atoms').get
|
|
, async = require('async')
|
|
, NotYetImplementedError = errors.NotYetImplementedError;
|
|
|
|
var Firefox = function(opts) {
|
|
this.rest = opts.rest;
|
|
this.app = opts.app;
|
|
this.opts = opts;
|
|
this.udid = opts.udid;
|
|
this.verbose = opts.verbose;
|
|
this.port = opts.port || 2828;
|
|
this.queue = [];
|
|
this.progress = 0;
|
|
this.onStop = function() {};
|
|
this.implicitWaitMs = 0;
|
|
this.commandTimeoutMs = 60 * 1000;
|
|
this.origCommandTimeoutMs = this.commandTimeoutMs;
|
|
this.commandTimeout = null;
|
|
this.asyncWaitMs = 0;
|
|
this.onConnect = null;
|
|
this.hasConnected = false;
|
|
this.fromActor = null;
|
|
this.socket = null;
|
|
this.receiveStream = null;
|
|
this.expectedRcvBytes = null;
|
|
this.capabilities = {
|
|
platform: 'LINUX'
|
|
, browserName: 'FirefoxOS'
|
|
, version: '18.0'
|
|
, webStorageEnabled: false
|
|
, takesScreenshots: true
|
|
, javascriptEnabled: true
|
|
, databaseEnabled: false
|
|
};
|
|
this.lastCmd = null;
|
|
this.initCommandMap();
|
|
};
|
|
|
|
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.connect(this.port, 'localhost', function () { });
|
|
|
|
|
|
this.onConnect = function() {
|
|
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); });
|
|
};
|
|
|
|
};
|
|
|
|
Firefox.prototype.stop = function(cb) {
|
|
logger.info("Stopping firefoxOs connection");
|
|
this.proxy({type: 'deleteSession'}, _.bind(function(err) {
|
|
if (err) return cb(err);
|
|
this.socket.destroy();
|
|
cb(0);
|
|
}, this));
|
|
};
|
|
|
|
Firefox.prototype.getMarionetteId = function(cb) {
|
|
logger.info("Getting marionette id");
|
|
this.proxy({type: 'getMarionetteID'}, _.bind(function(err, res) {
|
|
if (err) return cb(err);
|
|
this.fromActor = res.id;
|
|
cb(null);
|
|
}, this));
|
|
};
|
|
|
|
Firefox.prototype.createSession = function(cb) {
|
|
logger.info("Creating firefox os session");
|
|
this.proxy({type: 'newSession'}, _.bind(function(err, res) {
|
|
if (err) return cb(err);
|
|
this.sessionId = res.value;
|
|
cb(null);
|
|
}, this));
|
|
};
|
|
|
|
Firefox.prototype.launchAppByName = function(cb) {
|
|
logger.info("Launching our app by its name");
|
|
var atomSrc = getAtomSrc('gaia_apps');
|
|
var wrappedScript = atomSrc +
|
|
";GaiaApps.launchWithName('" + this.app +"');";
|
|
var cmd = {
|
|
type: 'executeAsyncScript'
|
|
, args: []
|
|
, newSandbox: true
|
|
, specialPowers: false
|
|
, value: wrappedScript
|
|
};
|
|
this.proxy(cmd, function(err, res) {
|
|
if (err) return cb(err);
|
|
cb(null, res.value.frame.ELEMENT);
|
|
});
|
|
};
|
|
|
|
Firefox.prototype.receive = function(data) {
|
|
var parts, bytes, jsonData;
|
|
if (this.receiveStream) {
|
|
this.receiveStream += data.toString();
|
|
logger.info(data.length + " b more data received, adding to stream (" + this.receiveStream.length + " b)");
|
|
try {
|
|
data = JSON.parse(this.receiveStream);
|
|
this.receiveStream = null;
|
|
} catch (e) {
|
|
logger.info("Stream still not complete, waiting");
|
|
return;
|
|
}
|
|
} else {
|
|
parts = data.toString().split(":");
|
|
bytes = parseInt(parts[0], 10);
|
|
logger.info("Data received, looking for " + bytes + " bytes");
|
|
jsonData = parts.slice(1).join(":");
|
|
try {
|
|
data = JSON.parse(jsonData);
|
|
} catch (e) {
|
|
logger.info("Data did not parse, waiting for more");
|
|
this.receiveStream = jsonData;
|
|
return;
|
|
}
|
|
}
|
|
logger.debug(JSON.stringify(data));
|
|
if (!this.hasConnected) {
|
|
this.hasConnected = true;
|
|
this.fromActor = data.from;
|
|
this.onConnect();
|
|
} else if (this.cbForCurrentCmd) {
|
|
var cb = this.cbForCurrentCmd;
|
|
this.progress--;
|
|
this.cbForCurrentCmd = null;
|
|
if (typeof cb === 'function') {
|
|
this.respond(data, cb);
|
|
}
|
|
this.executeNextCommand();
|
|
}
|
|
};
|
|
|
|
Firefox.prototype.proxy = deviceCommon.proxy;
|
|
|
|
Firefox.prototype.push = function(elem) {
|
|
this.queue.push(elem);
|
|
this.executeNextCommand();
|
|
};
|
|
|
|
|
|
Firefox.prototype.respond = function(data, cb) {
|
|
if (typeof data.error !== "undefined") {
|
|
cb(new Error(data.error.message));
|
|
} else if (typeof data.id !== "undefined") {
|
|
cb(null, data);
|
|
} else {
|
|
cb(null, {
|
|
status: status.codes.Success.code
|
|
, value: data.value
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
Firefox.prototype.executeNextCommand = function() {
|
|
if (this.queue.length <= 0 || this.progress > 0) {
|
|
return;
|
|
}
|
|
|
|
var target = this.queue.shift()
|
|
, command = target[0]
|
|
, cb = target[1];
|
|
|
|
this.cbForCurrentCmd = cb;
|
|
|
|
this.progress++;
|
|
command.to = this.fromActor;
|
|
var cmdStr = JSON.stringify(command);
|
|
cmdStr = cmdStr.length + ':' + cmdStr;
|
|
|
|
logger.debug("Sending command to firefoxOs: " + cmdStr);
|
|
this.socket.write(cmdStr);
|
|
};
|
|
|
|
Firefox.prototype.cmdMap = function() {
|
|
var elFn = function(elId) { return {element: elId }; };
|
|
return {
|
|
implicitWait: ['setSearchTimeout']
|
|
, getUrl: ['getUrl']
|
|
, findElement: ['findElement', function(strategy, selector) {
|
|
return {
|
|
using: strategy, value: selector
|
|
};
|
|
}, function(err, res, cb) {
|
|
if (err) return cb(err);
|
|
res.value = {ELEMENT: res.value};
|
|
cb(null, res);
|
|
}]
|
|
, click: ['clickElement', elFn]
|
|
, setValue: ['sendKeysToElement', function(elId, val) {
|
|
return {
|
|
element: elId
|
|
, value: val.split("")
|
|
};
|
|
}]
|
|
, getText: ['getElementText', elFn]
|
|
, getPageSource: ['getPageSource']
|
|
, execute: ['executeScript', function(script, params) {
|
|
return {
|
|
value: script
|
|
, args: params
|
|
};}]
|
|
, frame: ['switchToFrame', elFn]
|
|
};
|
|
};
|
|
|
|
Firefox.prototype.notImplementedCmds = function() {
|
|
return [
|
|
'equalsWebElement'
|
|
, 'findElements'
|
|
, 'findElementFromElement'
|
|
, 'findElementsFromElement'
|
|
, 'fireEvent'
|
|
, 'complexTap'
|
|
, 'flick'
|
|
, 'getPageSourceXML'
|
|
, 'swipe'
|
|
, 'hideKeyboard'
|
|
, 'clear'
|
|
, 'getName'
|
|
, 'getAttribute'
|
|
, 'getCssProperty'
|
|
, 'getLocation'
|
|
, 'getSize'
|
|
, 'getWindowSize'
|
|
, 'getPageIndex'
|
|
, 'keyevent'
|
|
, 'back'
|
|
, 'forward'
|
|
, 'refresh'
|
|
, 'keys'
|
|
, 'leaveWebView'
|
|
, 'elementDisplayed'
|
|
, 'elementEnabled'
|
|
, 'elementSelected'
|
|
, 'getAlertText'
|
|
, 'setAlertText'
|
|
, 'postAcceptAlert'
|
|
, 'postDismissAlert'
|
|
, 'asyncScriptTimeout'
|
|
, 'setOrientation'
|
|
, 'getOrientation'
|
|
, 'moveTo'
|
|
, 'clickCurrent'
|
|
, 'fakeFlickElement'
|
|
, 'executeAsync'
|
|
, 'title'
|
|
, 'submit'
|
|
, 'url'
|
|
, 'active'
|
|
, 'getWindowHandle'
|
|
, 'setWindow'
|
|
, 'closeWindow'
|
|
, 'getWindowHandles'
|
|
, 'getCommandTimeout'
|
|
, 'receiveAsyncResponse'
|
|
, 'setValueImmediate'
|
|
, 'findAndAct'
|
|
, 'getCookies'
|
|
, 'setCookie'
|
|
, 'deleteCookie'
|
|
, 'deleteCookies'
|
|
, 'getCurrentActivity'
|
|
];
|
|
};
|
|
|
|
Firefox.prototype.initCommandMap = function() {
|
|
var nyiCmds = this.notImplementedCmds();
|
|
// create controller functions dynamically for implemented commands
|
|
_.each(this.cmdMap(), _.bind(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() {
|
|
var args = Array.prototype.slice.call(arguments, 0);
|
|
var cb;
|
|
var outerCb = args[args.length - 1];
|
|
if (typeof cmdInfo[2] === 'function') {
|
|
cb = function(err, res) {
|
|
cmdInfo[2](err, res, outerCb);
|
|
};
|
|
} else {
|
|
cb = outerCb;
|
|
}
|
|
args = args.slice(0, args.length - 1);
|
|
var cmd = {
|
|
type: cmdInfo[0]
|
|
};
|
|
if (typeof cmdInfo[1] === 'function') {
|
|
cmd = _.extend(cmd, cmdInfo[1].apply(this, args));
|
|
} else if (typeof cmdInfo[1] === "undefined" && args.length > 0) {
|
|
cmd = _.extend(cmd, {value: args[0]});
|
|
}
|
|
this.proxy(cmd, cb);
|
|
}, this);
|
|
}, this));
|
|
// throw not yet implemented for any command in nyi list
|
|
_.each(nyiCmds, _.bind(function(controller) {
|
|
this[controller] = _.bind(function() {
|
|
var args = Array.prototype.slice.call(arguments, 0);
|
|
var cb = args[args.length - 1];
|
|
cb(new NotYetImplementedError(), null);
|
|
}, this);
|
|
}, this));
|
|
};
|
|
|
|
module.exports = function(opts) {
|
|
return new Firefox(opts);
|
|
};
|