@testim/testim-cli
Version:
Command line interface for running Testing on you CI
220 lines (184 loc) • 6.94 kB
JavaScript
var q = require('q');
/**
* Constructor
*/
function Multibrowser() {
this.instances = {};
this.promiseBucket = [];
var defer = q.defer();
this.qResolve = defer.resolve.toString();
}
Multibrowser.prototype.addInstance = function(browserName, client) {
if(this.instances[browserName]) {
throw new Error('webdriver instance "' + browserName + '" is already defined');
}
this.instances[browserName] = client;
};
/**
* modifier for multibrowser instance
*/
Multibrowser.prototype.getModifier = function() {
var browserNames = Object.keys(this.instances),
multibrowser = this;
return function(client) {
client.next = function() {
var self = this,
promises = [],
args = Array.prototype.slice.apply(arguments),
stack = args.pop(),
fnName = args.pop(),
instance;
/**
* no need for actual function here
*/
args.shift();
/**
* flush promise bucket
*/
multibrowser.promiseBucket = [];
return this.lastPromise.done(function() {
browserNames.forEach(function(browserName) {
instance = multibrowser.instances[browserName];
instance = instance[fnName].apply(instance, args[0]);
promises.push(instance.promise);
});
return q.all(promises).then(function(result) {
/**
* custom event handling since multibrowser instance
* actually never executes any command
*/
var payload = {
fnName: fnName
};
for(var i = 0; i < browserNames.length; ++i) {
payload[browserNames[i]] = result[i];
}
if(fnName.match(/(init|end)/)) {
self.emit(fnName, payload);
}
self.emit('result', payload);
self.defer.resolve(result);
}, function(err) {
self.emit('error', err);
self.defer.reject(err);
});
});
};
var _then = client.then;
client.then = function(onFulfilled, onRejected) {
/**
* curry arguments
* as multibrowser commands return with an array of results for each instance
* respectively (see q.all) we need to curry them (expand arguments) to help
* users to better work with the results
*
* uncurried original version:
* ```js
* matrix.getTitle().then(function(result) {
* expect(result[0]).to.be.equal('title of browser A');
* expect(result[1]).to.be.equal('title of browser B');
* })
* ```
*
* curried version:
* ```js
* matrix.getTitle().then(function(titleBrowserA, titleBrowserB) {
* expect(titleBrowserA).to.be.equal('title of browser A');
* expect(titleBrowserB).to.be.equal('title of browser B');
* })
* ```
*/
var curryArguments = function(args) {
/**
* when returning with a promise within a multibrowser promise like
*
* ```js
* matrix.url(...).getTitle().then(function() {
* return matrix.getSource();
* })
* ```
*
* we will have problems as we are running through curryArguments twice.
* Therefor check if the onFulFilled handler is from the Q library and
* handle that promise as usual here.
* It's an ugly hack but the only way to get around with this and having
* nice curried arguments.
*
*/
if(onFulfilled.toString() === multibrowser.qResolve) {
return onFulfilled.apply(this, arguments);
}
if(arguments.length === 1 && !Array.isArray(args)) {
return onFulfilled.call(this, args);
}
if(arguments.length > 1) {
args = Array.prototype.slice.call(arguments);
}
return onFulfilled.apply(this, args);
};
if(!onFulfilled && !onRejected) {
return this;
}
if(onFulfilled && !onRejected) {
return _then.call(this, curryArguments);
}
if(onFulfilled && onRejected) {
return _then.call(this, curryArguments, onRejected);
}
return _then.call(this, undefined, onRejected);
};
client.select = function(browserName) {
var instance = multibrowser.instances[browserName];
if(!instance) {
throw new Error('browser name "' + browserName + '" was not defined');
}
instance.isMultibrowser = false;
return instance;
};
client.sync = function() {
var bucket = multibrowser.flushPromiseBucket();
return this.call(function() {
return q.all(bucket);
});
};
var _addCommand = client.addCommand;
client.addCommand = function(fnName, fn, forceOverwrite) {
_addCommand.call(this, fnName, fn, forceOverwrite);
browserNames.forEach(function(browserName) {
multibrowser.instances[browserName].addCommand(fnName, fn, forceOverwrite);
});
return this;
};
return client;
};
};
/**
* flush bucket and return current pending promises
*/
Multibrowser.prototype.flushPromiseBucket = function() {
var bucket = this.promiseBucket.filter(function(promise) {
return promise.inspect().state === 'pending';
});
this.promiseBucket = [];
return bucket;
};
/**
* modifier for single webdriverio instances
*/
Multibrowser.prototype.getInstanceModifier = function() {
var multibrowser = this;
return function(client) {
var _next = client.next;
/**
* Overwrite next (bind) method to put each command into a bucket.
* This provides us useful information about all current running
* commands.
*/
client.next = function() {
multibrowser.promiseBucket.push(this.promise);
return _next.apply(this, arguments);
};
return client;
};
};
module.exports = Multibrowser;