mirror of
https://github.com/appium/appium.git
synced 2026-02-13 21:39:49 -06:00
make chrome device proxy to chromedriver instead of using atoms
This commit is contained in:
@@ -2,67 +2,135 @@
|
||||
|
||||
var Android = require('./android').Android
|
||||
, _ = require('underscore')
|
||||
, proxyTo = require('./device').proxyTo
|
||||
, logger = require('../logger').get('appium')
|
||||
, deviceCommon = require('./device')
|
||||
, rdp = require('./hybrid/rdp.js')
|
||||
, exec = require('child_process').exec
|
||||
, status = require("./uiauto/lib/status");
|
||||
, spawn = require('child_process').spawn
|
||||
, async = require('async')
|
||||
, adb = require('../android/adb');
|
||||
|
||||
var ChromeAndroid = function(opts) {
|
||||
this.initialize(opts);
|
||||
this.systemDebuggerPort = 2773;
|
||||
this.opts = opts;
|
||||
this.isProxy = true;
|
||||
this.proxyHost = '127.0.0.1';
|
||||
this.proxyPort = opts.port || 9515;
|
||||
this.chromedriver = null;
|
||||
this.proc = null;
|
||||
this.chromedriverStarted = false;
|
||||
this.chromedriver = "chromedriver";
|
||||
this.adb = null;
|
||||
this.onDie = function() {};
|
||||
};
|
||||
|
||||
_.extend(ChromeAndroid.prototype, Android.prototype);
|
||||
|
||||
ChromeAndroid.prototype._start = ChromeAndroid.prototype.start;
|
||||
|
||||
ChromeAndroid.prototype.loadFirstWebview = function(cb) {
|
||||
var me = this;
|
||||
this.remote = rdp.init(function() {
|
||||
// TODO: fill out with what to do if webview disconnects on us
|
||||
});
|
||||
this.remote.port = this.systemDebuggerPort;
|
||||
this.remote.pageArrayFromJson(function(pageArray) {
|
||||
me.curWindowHandle = pageArray[0].id;
|
||||
me.remote.connect(me.curWindowHandle, cb);
|
||||
});
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.start = function(cb, onDie) {
|
||||
var me = this;
|
||||
this._start(function(err) {
|
||||
if (err) return cb(err);
|
||||
me.forwardDebuggerPort(function(err) {
|
||||
if (err) return cb(err);
|
||||
me.loadFirstWebview(function() {
|
||||
cb();
|
||||
});
|
||||
});
|
||||
}, onDie);
|
||||
this.adb = new adb(this.opts);
|
||||
this.onDie = onDie;
|
||||
// do stuff to launch chromedriver
|
||||
async.waterfall([
|
||||
this.ensureChromedriverExists.bind(this),
|
||||
this.startChromedriver.bind(this),
|
||||
this.createSession.bind(this)
|
||||
], cb);
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.startAppium = function(onLaunch, onExit) {
|
||||
this.adb.startChrome(onLaunch, onExit);
|
||||
ChromeAndroid.prototype.ensureChromedriverExists = function(cb) {
|
||||
logger.info("Ensuring Chromedriver exists");
|
||||
exec('which chromedriver', function(err, stdout) {
|
||||
if (err) return cb(new Error("Could not find chromedriver, is it on PATH?"));
|
||||
this.chromedriver = stdout.trim();
|
||||
cb();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.shutdown = function() {
|
||||
this.remote.disconnect();
|
||||
this.onStop();
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.forwardDebuggerPort = function(cb) {
|
||||
logger.info("Forwarding debugger port");
|
||||
var arg = "tcp:" + this.systemDebuggerPort +
|
||||
" localabstract:chrome_devtools_remote";
|
||||
exec(this.adb.adbCmd + " forward " + arg, _.bind(function(err) {
|
||||
if (err) {
|
||||
logger.error(err); return cb(err);
|
||||
ChromeAndroid.prototype.startChromedriver = function(cb) {
|
||||
logger.info("Spawning chromedriver with: " + this.chromedriver);
|
||||
var alreadyReturned = false;
|
||||
var args = ["--url-base=wd/hub"];
|
||||
this.proc = spawn(this.chromedriver, args);
|
||||
this.proc.stdout.setEncoding('utf8');
|
||||
this.proc.stderr.setEncoding('utf8');
|
||||
this.proc.on('error', function(err) {
|
||||
logger.error('Chromedriver process failed with error: ' + err.message);
|
||||
alreadyReturned = true;
|
||||
this.onDie();
|
||||
}.bind(this));
|
||||
this.proc.stdout.on('data', function(data) {
|
||||
logger.info('[CHROMEDRIVER] ' + data.trim());
|
||||
if (data.indexOf("Started ChromeDriver") !== -1) {
|
||||
this.chromedriverStarted = true;
|
||||
alreadyReturned = true;
|
||||
cb();
|
||||
}
|
||||
cb(null);
|
||||
}, this));
|
||||
}.bind(this));
|
||||
this.proc.stderr.on('data', function(data) {
|
||||
logger.info('[CHROMEDRIVER STDERR] ' + data.trim());
|
||||
});
|
||||
this.proc.on('exit', function(code) {
|
||||
logger.info("Chromedriver exited with code " + code);
|
||||
alreadyReturned = true;
|
||||
if (!this.chromedriverStarted) {
|
||||
return cb(new Error("Chromedriver quit before it was available"));
|
||||
}
|
||||
this.onDie();
|
||||
}.bind(this));
|
||||
setTimeout(function() {
|
||||
if (!alreadyReturned) {
|
||||
logger.info("We didn't hear from chromedriver in a while; let's " +
|
||||
"assume it started OK");
|
||||
cb();
|
||||
}
|
||||
}.bind(this), 1000);
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.createSession = function(cb) {
|
||||
logger.info("Creating Chrome session");
|
||||
var data = {
|
||||
sessionId: null,
|
||||
desiredCapabilities: {
|
||||
chromeOptions: {
|
||||
androidPackage: 'com.android.chrome'
|
||||
}
|
||||
}
|
||||
};
|
||||
this.proxyTo('/wd/hub/session', 'POST', data, function(err, res, body) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (res.statusCode === 303 && res.headers.location) {
|
||||
logger.info("Successfully started chrome session");
|
||||
var loc = res.headers.location;
|
||||
this.chromeSessionId = /\/([^\/]+)$/.exec(loc)[1];
|
||||
cb(null, this.chromeSessionId);
|
||||
} else {
|
||||
logger.error("Chromedriver create session did not work. Status was " +
|
||||
res.statusCode + " and body was " + body);
|
||||
cb(new Error("Did not get session redirect from Chromedriver"));
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.deleteSession = function(cb) {
|
||||
var url = '/wd/hub/session/' + this.chromeSessionId;
|
||||
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"));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.stop = function(cb) {
|
||||
async.series([
|
||||
this.adb.getConnectedDevices.bind(this.adb),
|
||||
this.adb.stopApp.bind(this.adb)
|
||||
], function(err) {
|
||||
if (err) logger.error(err.message);
|
||||
cb(0);
|
||||
});
|
||||
};
|
||||
|
||||
ChromeAndroid.prototype.proxyTo = proxyTo;
|
||||
|
||||
module.exports = function(opts) {
|
||||
return new ChromeAndroid(opts);
|
||||
};
|
||||
|
||||
@@ -62,29 +62,36 @@ exports.waitForCondition = function(waitMs, condFn, cb, intervalMs) {
|
||||
spin();
|
||||
};
|
||||
|
||||
exports.request = function(url, method, body, contentType, cb) {
|
||||
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";
|
||||
contentType = "application/json;charset=UTF-8";
|
||||
}
|
||||
if (!(/^https?:\/\//.exec(url))) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
var host = /^https?:\/\/([^\/]+)/.exec(url)[1];
|
||||
var opts = {
|
||||
url: url
|
||||
, method: method
|
||||
, headers: {'Content-Type': contentType}
|
||||
, jar: false
|
||||
};
|
||||
opts.headers = {};
|
||||
if (_.contains(['put', 'post', 'patch'], method.toLowerCase())) {
|
||||
if (typeof body !== "string") {
|
||||
opts.json = body;
|
||||
if (typeof body === "object") {
|
||||
opts.body = JSON.stringify(body);
|
||||
} else {
|
||||
opts.body = body;
|
||||
}
|
||||
opts.headers['Content-Length'] = opts.body.length;
|
||||
}
|
||||
// explicitly set these headers with correct capitalization to work around
|
||||
// an issue in node/requests
|
||||
opts.headers['Content-Type'] = contentType;
|
||||
opts.headers.Host = host;
|
||||
logger.info("Making http request with opts: " + JSON.stringify(opts));
|
||||
request(opts, cb);
|
||||
};
|
||||
@@ -132,6 +139,14 @@ exports.unpackApp = function(req, packageExtension, cb) {
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
var controller = require('./controller')
|
||||
, respondError = require('./controller').respondError
|
||||
, request = require('./device').request
|
||||
, doRequest = require('./device').doRequest
|
||||
, _ = require('underscore')
|
||||
, _s = require("underscore.string")
|
||||
, logger = require('../logger').get('appium');
|
||||
@@ -48,7 +48,7 @@ module.exports = function(appium) {
|
||||
req.device.proxyPort);
|
||||
var url = 'http://' + req.device.proxyHost + ':' + req.device.proxyPort +
|
||||
req.originalUrl;
|
||||
request(url, req.route.method.toUpperCase(), req.body,
|
||||
doRequest(url, req.route.method.toUpperCase(), req.body,
|
||||
req.headers['content-type'], function(err, response, body) {
|
||||
if (err) return next(err);
|
||||
if (body && body.value) {
|
||||
|
||||
@@ -4,6 +4,7 @@ var errors = require('./errors')
|
||||
, adb = require('../android/adb')
|
||||
, _ = require('underscore')
|
||||
, request = require('./device').request
|
||||
, proxyTo = require('./device').proxyTo
|
||||
, logger = require('../logger').get('appium')
|
||||
, status = require("./uiauto/lib/status")
|
||||
, fs = require('fs')
|
||||
@@ -134,13 +135,7 @@ Selendroid.prototype.deleteSession = function(cb) {
|
||||
}, this));
|
||||
};
|
||||
|
||||
Selendroid.prototype.proxyTo = function(endpoint, method, data, cb) {
|
||||
if (endpoint[0] !== '/') {
|
||||
endpoint = '/' + endpoint;
|
||||
}
|
||||
var url = 'http://' + this.proxyHost + ':' + this.proxyPort + endpoint;
|
||||
request(url, method, data ? data : null, cb);
|
||||
};
|
||||
Selendroid.prototype.proxyTo = proxyTo;
|
||||
|
||||
module.exports = function(opts) {
|
||||
return new Selendroid(opts);
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
"adm-zip" : "~0.4.3",
|
||||
"ws": "0.4.25",
|
||||
"socket.io" : "~0.9.14"
|
||||
},
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt travis"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user