UNPKG

@testim/testim-cli

Version:

Command line interface for running Testing on you CI

220 lines (184 loc) 6.94 kB
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;