diff options
Diffstat (limited to 'src/scenario/Scenario.js')
| -rw-r--r-- | src/scenario/Scenario.js | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/src/scenario/Scenario.js b/src/scenario/Scenario.js new file mode 100644 index 00000000..e93f6b2e --- /dev/null +++ b/src/scenario/Scenario.js @@ -0,0 +1,103 @@ +/** + * Setup file for the Scenario. + * Must be first in the compilation/bootstrap list. + */ + +// Public namespace +angular.scenario = {}; + +// Namespace for the UI +angular.scenario.ui = {}; + +/** + * Defines a new DSL statement. If your factory function returns a Future + * it's returned, otherwise the result is assumed to be a map of functions + * for chaining. Chained functions are subject to the same rules. + * + * Note: All functions on the chain are bound to the chain scope so values + * set on "this" in your statement function are available in the chained + * functions. + * + * @param {String} The name of the statement + * @param {Function} Factory function(application), return a function for + * the statement. + */ +angular.scenario.dsl = function(name, fn) { + angular.scenario.dsl[name] = function() { + function executeStatement(statement, args) { + var result = statement.apply(this, args); + if (angular.isFunction(result) || result instanceof angular.scenario.Future) + return result; + var self = this; + var chain = angular.extend({}, result); + angular.foreach(chain, function(value, name) { + if (angular.isFunction(value)) { + chain[name] = angular.bind(self, function() { + return executeStatement.call(self, value, arguments); + }); + } else { + chain[name] = value; + } + }); + return chain; + } + var statement = fn.apply(this, arguments); + return function() { + return executeStatement.call(this, statement, arguments); + }; + }; +}; + +/** + * Defines a new matcher for use with the expects() statement. The value + * this.actual (like in Jasmine) is available in your matcher to compare + * against. Your function should return a boolean. The future is automatically + * created for you. + * + * @param {String} The name of the matcher + * @param {Function} The matching function(expected). + */ +angular.scenario.matcher = function(name, fn) { + angular.scenario.matcher[name] = function(expected) { + var prefix = 'expect ' + this.future.name + ' '; + if (this.inverse) { + prefix += 'not '; + } + this.addFuture(prefix + name + ' ' + angular.toJson(expected), + angular.bind(this, function(done) { + this.actual = this.future.value; + if ((this.inverse && fn.call(this, expected)) || + (!this.inverse && !fn.call(this, expected))) { + this.error = 'expected ' + angular.toJson(expected) + + ' but was ' + angular.toJson(this.actual); + } + done(this.error); + }) + ); + }; +}; + +/** + * Iterates through list with iterator function that must call the + * continueFunction to continute iterating. + * + * @param {Array} list to iterate over + * @param {Function} Callback function(value, continueFunction) + * @param {Function} Callback function(error, result) called when iteration + * finishes or an error occurs. + */ +function asyncForEach(list, iterator, done) { + var i = 0; + function loop(error) { + if (error || i >= list.length) { + done(error); + } else { + try { + iterator(list[i++], loop); + } catch (e) { + done(e); + } + } + } + loop(); +} |
