Files
2012-05-25 09:03:56 -04:00

317 lines
9.1 KiB
JavaScript

var http = require('http')
, __slice = Array.prototype.slice;
var strip = function strip(str) {
var x = [];
for (var i = 0; i < str.length; i++) {
if (str.charCodeAt(i)) {
x.push(str.charAt(i));
}
}
return x.join('');
};
var newError = function(opts)
{
var err = new Error();
for (var k in opts) {
err[k] = opts[k]
}
// nicer error output
err.inspect = function() {
var res = "";
for (var k in this) {
var _this = this;
(function() {
var v = _this[k];
if (typeof v === 'object') {
if ((v["class"] != null) && v["class"].match(/org.openqa.selenium.remote.Response/)) {
// for selenium classes, hidding long fields or field with
// duplicate information
var vAsStr = JSON.stringify(v, function(key, value) {
if (key === 'screen' || key === 'stackTrace' || key === 'buildInformation' || key === 'localizedMessage') {
return '[hidden]';
} else {
return value;
}
}, " ");
res = res + k + ": " + vAsStr + "\n";
} else {
// for other objects making sure output is not too long
var vAsStr = JSON.stringify(v, undefined, " ");
var maxLength = 1000;
if (vAsStr.length > maxLength) {
vAsStr = vAsStr.substr(0, maxLength) + "\n...";
}
res = res + k + ": " + vAsStr + "\n";
}
} else if (typeof v != 'function')
{
// printing non object types without modif
res = res + k + ": " + v + "\n";
}
})();
};
return res;
};
return err;
};
var isWebDriverException = function(res) {
var _ref;
if ((typeof res !== "undefined" && res !== null ?
(_ref = res["class"]) != null ? _ref.indexOf('WebDriverException') :
void 0 : void 0) > 0) {
return true;
}
return false;
}
// just calls the callback when there is no result
var simpleCallback = function(cb) {
return function(res) {
if(res==null) {
// expected behaviour for quit
if(cb!=null){ return cb();}
}else{
res.setEncoding('utf8');
var data = '';
res.on('data', function(chunk) { data += chunk.toString(); });
res.on('end', function() {
if(data == '') {
// expected behaviour
return cb()
} else {
// something wrong
if(cb!=null){
return cb(new Error(
{message:'Unexpected data in simpleCallback.', data:data}) );
}
}
});
}
};
};
// base for all callback handling data
var callbackWithDataBase = function(cb) {
return function(res) {
res.setEncoding('utf8');
var data = '';
res.on('data', function(chunk) { data += chunk.toString(); });
res.on('end', function() {
var obj;
try {
obj = JSON.parse(strip(data));
} catch (e) {
return cb(newError({message:'Not JSON response', data:data}));
}
if (obj.status > 0) {
cb(newError(
{message:'Error response status.',status:obj.status,cause:obj}));
} else {
cb(null, obj);
}
});
}
};
// retrieves field value from result
var callbackWithData = function(cb) {
return callbackWithDataBase(function(err,obj) {
if(err != null) {return cb(err);}
if(isWebDriverException(obj.value)) {return cb(newError(
{message:obj.value.message,cause:obj.value}));}
cb(null, obj.value);
});
};
// retrieves ONE element
var elementCallback = function(cb) {
return callbackWithDataBase(function(err, obj) {
if(err != null) {return cb(err);}
if(isWebDriverException(obj.value)) {return cb(newError(
{message:obj.value.message,cause:obj.value}));}
if (!obj.value.ELEMENT) {
cb(newError(
{message:"no ELEMENT in response value field.",cause:obj}));
} else {
cb(null, obj.value.ELEMENT);
}
});
};
// retrieves SEVERAL elements
var elementsCallback = function(cb) {
return callbackWithDataBase(function(err, obj) {
if(err != null) {return cb(err);}
if(isWebDriverException(obj.value)) {return cb(newError(
{message:obj.value.message,cause:obj.value}));}
if (!(obj.value instanceof Array)) {return cb(newError(
{message:"Response value field is not an Array.", cause:obj.value}));}
var i, elements = [];
for (i = 0; i < obj.value.length; i++) {
elements.push(obj.value[i].ELEMENT);
}
cb(null, elements);
});
};
var newHttpOpts = function(method) {
var opts = new Object();
opts.method = method;
for (var o in this.options) {
opts[o] = this.options[o];
}
opts.headers = {};
opts.headers['Connection'] = 'keep-alive';
if (opts.method === 'POST' || opts.method === 'GET')
opts.headers['Accept'] = 'application/json';
if (opts.method == 'POST')
opts.headers['Content-Type'] = 'application/json; charset=UTF-8';
return opts;
};
// session initialization
var init = function(desired, cb) {
var _this = this;
//allow desired ovveride to be left out
if (typeof desired == 'function') {
cb = desired;
desired = {};
}
// making copy
var _desired = {};
for (var k in desired) {
_desired[k] = desired[k];
}
// defaulting capabilities when necessary
for (var k in this.defaultCapabilities) {
_desired[k] = _desired[k] || this.defaultCapabilities[k];
}
// http options
var httpOpts = newHttpOpts.apply(this, ['POST']);
// authentication (for saucelabs)
if ((_this.username != null) && (_this.accessKey != null)) {
var authString = _this.username + ':' + _this.accessKey;
var buf = new Buffer(authString);
httpOpts['headers'] = {
'Authorization': 'Basic ' + buf.toString('base64')
};
}
// building request
var req = http.request(httpOpts, function(res) {
var data = '';
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
if (res.headers.location == undefined) {
console.log('\x1b[31mError\x1b[0m: The environment you requested was unavailable.\n');
console.log('\x1b[33mReason\x1b[0m:\n');
console.log(data);
console.log('\nFor the available values please consult the WebDriver JSONWireProtocol,');
console.log('located at: \x1b[33mhttp://code.google.com/p/selenium/wiki/JsonWireProtocol#/session\x1b[0m');
if (cb)
cb({ message: 'The environment you requested was unavailable.' });
return;
}
var locationArr = res.headers.location.split('/');
_this.sessionID = locationArr[locationArr.length - 1];
_this.emit('status', '\nDriving the web on session: ' + _this.sessionID + '\n');
if (cb) { cb(null, _this.sessionID) }
});
});
req.on('error', function(e) { cb(e); });
// writting data
req.write(JSON.stringify({desiredCapabilities: _desired}));
// sending
req.end();
};
// used to build all the methods except init
var methodBuilder = function(builderOpt) {
// by default we call simpleCallBack(cb) assuming cb is the last argument
var defaultCb = function() {
var args, cb, _i;
args = 2 <= arguments.length ? __slice.call(arguments, 0,
_i = arguments.length - 1) : (_i = 0, []), cb = arguments[_i++];
return simpleCallback(cb);
};
return function(cb) {
var _this = this;
// parsing arguments
var args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
// http options init
var httpOpts = newHttpOpts.apply(this, [builderOpt.method]);
// retrieving path information
var relPath = builderOpt.relPath;
if (typeof relPath === 'function') { relPath = relPath.apply(this, args) }
var absPath = builderOpt.absPath;
if (typeof absPath === 'function') { absPath = absPath.apply(this, args) }
// setting path in http options
if (this.sessionID != null) { httpOpts['path'] += '/' + this.sessionID; }
if (relPath) { httpOpts['path'] += relPath; }
if (absPath) { httpOpts['path'] = absPath;}
// building callback
cb = (builderOpt.cb || defaultCb).apply(this, args);
// wrapping cb if we need to emit a message
if (builderOpt.emit != null) {
var _cb = cb;
cb = function(res) {
if (builderOpt.emit != null) {
_this.emit(builderOpt.emit.event, builderOpt.emit.message);
}
if (_cb) { _cb(); }
};
}
// logging
_this.emit('command', httpOpts['method'],
httpOpts['path'].replace(this.sessionID, ':sessionID')
.replace(this.basePath, '')
);
// building request
var req = http.request(httpOpts, cb);
req.on('error', function(e) { cb(e); });
// writting data
var data = '';
if (builderOpt.data != null) {
data = builderOpt.data.apply(this, args);
}
if (typeof data === 'object') {
data = JSON.stringify(data);
}
req.write(data);
//sending
req.end();
};
};
exports.simpleCallback = simpleCallback;
exports.callbackWithData = callbackWithData;
exports.elementCallback = elementCallback;
exports.elementsCallback = elementsCallback;
exports.init = init;
exports.methodBuilder = methodBuilder;