FaC: Das alte Flag von Terminen, die in die Zeiterfassung übernommen worden sind, wird ebenfalls berücksichtigt.
259 lines
8.0 KiB
JavaScript
259 lines
8.0 KiB
JavaScript
var EventEmitter = require('events').EventEmitter,
|
|
_ = require("lodash"),
|
|
util = require( 'util' ),
|
|
url = require('url'),
|
|
http = require('http'),
|
|
https = require('https'),
|
|
__slice = Array.prototype.slice,
|
|
utils = require("./utils"),
|
|
findCallback = utils.findCallback,
|
|
niceArgs = utils.niceArgs,
|
|
niceResp = utils.niceResp,
|
|
strip = utils.strip,
|
|
deprecator = utils.deprecator,
|
|
httpUtils = require('./http-utils'),
|
|
config = require('./config'),
|
|
Element = require('./element'),
|
|
commands = require('./commands'),
|
|
SAUCE_API_HOST = process.env["SAUCE_API_HOST"] || "saucelabs.com";
|
|
|
|
// Webdriver client main class
|
|
// configUrl: url object constructed via url.parse
|
|
var Webdriver = module.exports = function(configUrl) {
|
|
EventEmitter.call( this );
|
|
this.sessionID = null;
|
|
this.httpAgent = null;
|
|
this.configUrl = configUrl;
|
|
this.sauceTestPageRoot = "https://" + SAUCE_API_HOST + "/jobs";
|
|
this.sauceRestRoot = "https://" + SAUCE_API_HOST + "/rest/v1";
|
|
// config url without auth
|
|
this.noAuthConfigUrl = url.parse(url.format(this.configUrl));
|
|
delete this.noAuthConfigUrl.auth;
|
|
|
|
this.defaultCapabilities = {
|
|
browserName: 'firefox'
|
|
, version: ''
|
|
, javascriptEnabled: true
|
|
, platform: 'ANY'
|
|
};
|
|
|
|
this._httpConfig = _.clone(config.httpConfig);
|
|
};
|
|
|
|
//inherit from EventEmitter
|
|
util.inherits( Webdriver, EventEmitter );
|
|
|
|
// creates a new element
|
|
Webdriver.prototype.newElement = function(jsonWireElement) {
|
|
return new Element(jsonWireElement, this);
|
|
};
|
|
|
|
/**
|
|
* attach(sessionID, cb) -> cb(err)
|
|
* Connect to an already-active session.
|
|
*/
|
|
Webdriver.prototype.attach = function(sessionID) {
|
|
var cb = findCallback(arguments);
|
|
this.sessionID = sessionID;
|
|
if(cb) { cb(null); }
|
|
};
|
|
|
|
/**
|
|
* detach(cb) -> cb(err)
|
|
* Detach from the current session.
|
|
*/
|
|
Webdriver.prototype.detach = function() {
|
|
var cb = findCallback(arguments);
|
|
this.sessionID = null;
|
|
if(cb) { cb(null); }
|
|
};
|
|
|
|
commands.chain = function(obj){
|
|
deprecator.warn('chain', 'chain api has been deprecated, use promise chain instead.');
|
|
require("./deprecated-chain").patch(this);
|
|
return this.chain(obj);
|
|
};
|
|
|
|
Webdriver.prototype._init = function() {
|
|
delete this.sessionID;
|
|
var _this = this;
|
|
var fargs = utils.varargs(arguments);
|
|
var cb = fargs.callback,
|
|
desired = fargs.all[0] || {};
|
|
|
|
var _desired = _.clone(desired);
|
|
|
|
if(desired.deviceName || desired.device || desired.wdNoDefaults ||
|
|
desired['wd-no-defaults']) {
|
|
// no default or appium caps, we dont default
|
|
delete _desired.wdNoDefaults;
|
|
delete _desired['wd-no-defaults'];
|
|
} else {
|
|
// using default
|
|
_.defaults(_desired, this.defaultCapabilities);
|
|
}
|
|
|
|
// http options
|
|
this.httpAgent = (this.configUrl.protocol === 'https:')
|
|
? new https.Agent({ keepAlive: false })
|
|
: new http.Agent({ keepAlive: false });
|
|
var httpOpts = httpUtils.newHttpOpts('POST', _this._httpConfig);
|
|
httpOpts.agent = this.httpAgent;
|
|
|
|
var url = httpUtils.buildInitUrl(this.configUrl);
|
|
|
|
var data = {};
|
|
|
|
// building request
|
|
if (_desired.allowW3C || _desired.forceW3C) {
|
|
data.capabilities = {
|
|
alwaysMatch: utils.prefixCapabilities(_desired),
|
|
firstMatch: [{}],
|
|
};
|
|
}
|
|
|
|
if (!_desired.forceW3C) {
|
|
data.desiredCapabilities = _desired;
|
|
}
|
|
httpUtils.emit(this, httpOpts.method, url, data);
|
|
|
|
httpOpts.prepareToSend(url, data);
|
|
|
|
httpUtils.requestWithRetry(httpOpts, this._httpConfig, this.emit.bind(this), function(err, res, data) {
|
|
if(err) { return cb(err); }
|
|
|
|
var resData;
|
|
var jsonData;
|
|
|
|
// retrieving session
|
|
try{
|
|
jsonData = JSON.parse(data);
|
|
_this.sessionID = jsonData.value ? jsonData.value.sessionId : false;
|
|
_this.w3c = false;
|
|
resData = jsonData.value;
|
|
if(!_this.sessionID && jsonData.status === 0 ){
|
|
_this.sessionID = jsonData.sessionId;
|
|
_this.w3c = true;
|
|
}
|
|
} catch(ignore){}
|
|
if(!_this.sessionID){
|
|
// attempting to retrieve the session the OLD old way
|
|
try{
|
|
var locationArr = res.headers.location.replace(/\/$/, '').split('/');
|
|
_this.sessionID = locationArr[locationArr.length - 1];
|
|
} catch(ignore){}
|
|
}
|
|
|
|
if (_this.sessionID) {
|
|
if (/saucelabs\.com/.exec(url.hostname)) {
|
|
_this.emit('status', '\nDriving the web on session: ' + _this.sauceTestPageRoot + '/' + _this.sessionID + '\n');
|
|
} else {
|
|
_this.emit('status', '\nDriving the web on session: ' + _this.sessionID + '\n');
|
|
}
|
|
if (cb) { cb(null, _this.sessionID, resData); }
|
|
} else {
|
|
data = strip(data);
|
|
if (cb) {
|
|
err = new Error('The environment you requested was unavailable.' + ((jsonData && jsonData.value) ? ('\n' + jsonData.value.message) : ''));
|
|
err.data = data;
|
|
return cb(err);
|
|
} else {
|
|
/* eslint-disable no-console */
|
|
console.error('\x1b[31mError\x1b[0m: The environment you requested was unavailable.\n');
|
|
console.error('\x1b[33mReason\x1b[0m:\n');
|
|
console.error(data);
|
|
console.error('\nFor the available values please consult the WebDriver JSONWireProtocol,');
|
|
console.error('located at: \x1b[33mhttp://code.google.com/p/selenium/wiki/JsonWireProtocol#/session\x1b[0m');
|
|
/* eslint-enable no-console */
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
// standard jsonwire call
|
|
Webdriver.prototype._jsonWireCall = function(opts) {
|
|
var _this = this;
|
|
|
|
// http options init
|
|
var httpOpts = httpUtils.newHttpOpts(opts.method, this._httpConfig);
|
|
httpOpts.agent = this.httpAgent;
|
|
|
|
var url = httpUtils.buildJsonCallUrl(this.noAuthConfigUrl, this.sessionID, opts.relPath, opts.absPath);
|
|
|
|
// building callback
|
|
var cb = opts.cb;
|
|
if (opts.emit) {
|
|
// wrapping cb if we need to emit a message
|
|
var _cb = cb;
|
|
cb = function() {
|
|
var args = __slice.call(arguments, 0);
|
|
_this.emit(opts.emit.event, opts.emit.message);
|
|
if (_cb) { _cb.apply(_this,args); }
|
|
};
|
|
}
|
|
|
|
// logging
|
|
httpUtils.emit(this, httpOpts.method, url, opts.data);
|
|
|
|
// writting data
|
|
var data = opts.data || {};
|
|
httpOpts.prepareToSend(url, data);
|
|
// building request
|
|
httpUtils.requestWithRetry(httpOpts, this._httpConfig, this.emit.bind(this), function(err, res, data) {
|
|
if(err) { return cb(err); }
|
|
data = strip(data);
|
|
cb(null, data || "");
|
|
});
|
|
};
|
|
|
|
Webdriver.prototype._sauceJobUpdate = function(jsonData, done) {
|
|
var _this = this;
|
|
if(!this.configUrl || !this.configUrl.auth){
|
|
return done(new Error("Missing auth token."));
|
|
} else if(!this.configUrl.auth.match(/^.+:.+$/)){
|
|
return done(new Error("Invalid auth token."));
|
|
}
|
|
var jobUpdateUrl = url.resolve(
|
|
this.sauceRestRoot.replace(/\/?$/,'/'),
|
|
this.configUrl.auth.replace(/:.*$/,'') + '/jobs/' + this.sessionID);
|
|
|
|
var httpOpts = httpUtils.newHttpOpts('PUT', this._httpConfig);
|
|
httpOpts.auth = {
|
|
user: this.configUrl.auth.split(':')[0],
|
|
pass: this.configUrl.auth.split(':')[1],
|
|
};
|
|
httpOpts.jar = false; // disable cookies: avoids CSRF issues
|
|
httpOpts.prepareToSend(jobUpdateUrl, jsonData);
|
|
|
|
httpUtils.requestWithoutRetry(httpOpts, this.emit.bind(this), function(err, resp) {
|
|
if(err) { return done(err); }
|
|
if(resp.statusCode !== 200) {
|
|
return done(new Error("Sauce job update failed with http status code:" +
|
|
resp.statusCode));
|
|
}
|
|
httpUtils.emit(_this, 'POST', '/rest/v1/:user/jobs/:sessionID', jsonData);
|
|
done();
|
|
});
|
|
};
|
|
|
|
_(commands).each(function(fn, name) {
|
|
Webdriver.prototype[name] = function() {
|
|
var _this = this;
|
|
var fargs = utils.varargs(arguments);
|
|
this.emit('command', "CALL" , name + niceArgs(fargs.all));
|
|
var cb = function(err) {
|
|
if(err) {
|
|
err.message = '[' + name + niceArgs(fargs.all) + "] " + err.message;
|
|
if(fargs.callback) { fargs.callback(err); }
|
|
} else {
|
|
var cbArgs = __slice.call(arguments, 0);
|
|
_this.emit('command', "RESPONSE" , name + niceArgs(fargs.all),
|
|
niceResp(_.drop(cbArgs)));
|
|
if(fargs.callback) { fargs.callback.apply(null, cbArgs); }
|
|
}
|
|
};
|
|
var args = fargs.all.concat([cb]);
|
|
return fn.apply(this, args);
|
|
};
|
|
});
|