1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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();
}
|