aboutsummaryrefslogtreecommitdiffstats
path: root/src/scenario/Scenario.js
diff options
context:
space:
mode:
authorElliott Sprehn2010-10-19 13:17:49 -0700
committerElliott Sprehn2010-10-20 14:38:00 -0700
commit2115db69035c5993533fe7a3825e64cf6e9068ad (patch)
tree796a502b28cd2bda8108a672eac0bf28c8bc21d4 /src/scenario/Scenario.js
parent9c8b1800b90e14b643bab6ada8e96f8f850e84a6 (diff)
downloadangular.js-2115db69035c5993533fe7a3825e64cf6e9068ad.tar.bz2
Lots of stability and performance updates and UI polish too.
Polish the Scenario Runner UI to include: - a scroll pane that steps appear in since the list can be very long - Collapse successful tests - Show the line where the DSL statements were when there's an error (Chrome, Firefox) Also: - Remove lots angular.bind calls to reduce the amount of stack space used. - Use setTimeout(...,0) to schedule the next future to let the browser breathe and have it repaint the steps. Also prevents overflowing the stack when an it() creates many futures. - Run afterEach() handlers even if the it() block fails. - Make navigateTo() take a function as the second argument so you can compute a URL in the future. - Add wait() DSL statement to allow interactive debugging of tests. - Allow custom jQuery selectors with element(...).query(fn) DSL statement. Known Issues: - All afterEach() handlers run even if a beforeEach() handler fails. Only after handlers for the same level as the failure and above should run.
Diffstat (limited to 'src/scenario/Scenario.js')
-rw-r--r--src/scenario/Scenario.js97
1 files changed, 82 insertions, 15 deletions
diff --git a/src/scenario/Scenario.js b/src/scenario/Scenario.js
index 979210e5..b1782cf2 100644
--- a/src/scenario/Scenario.js
+++ b/src/scenario/Scenario.js
@@ -32,9 +32,9 @@ angular.scenario.dsl = angular.scenario.dsl || function(name, fn) {
var chain = angular.extend({}, result);
angular.foreach(chain, function(value, name) {
if (angular.isFunction(value)) {
- chain[name] = angular.bind(self, function() {
+ chain[name] = function() {
return executeStatement.call(self, value, arguments);
- });
+ };
} else {
chain[name] = value;
}
@@ -63,17 +63,18 @@ angular.scenario.matcher = angular.scenario.matcher || function(name, fn) {
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);
+ var self = this;
+ this.addFuture(prefix + name + ' ' + angular.toJson(expected),
+ function(done) {
+ var error;
+ self.actual = self.future.value;
+ if ((self.inverse && fn.call(self, expected)) ||
+ (!self.inverse && !fn.call(self, expected))) {
+ error = 'expected ' + angular.toJson(expected) +
+ ' but was ' + angular.toJson(self.actual);
}
- done(this.error);
- })
- );
+ done(error);
+ });
};
};
@@ -88,7 +89,10 @@ angular.scenario.matcher = angular.scenario.matcher || function(name, fn) {
*/
function asyncForEach(list, iterator, done) {
var i = 0;
- function loop(error) {
+ function loop(error, index) {
+ if (index && index > i) {
+ i = index;
+ }
if (error || i >= list.length) {
done(error);
} else {
@@ -102,7 +106,63 @@ function asyncForEach(list, iterator, done) {
loop();
}
+/**
+ * Formats an exception into a string with the stack trace, but limits
+ * to a specific line length.
+ *
+ * @param {Object} the exception to format, can be anything throwable
+ * @param {Number} Optional. max lines of the stack trace to include
+ * default is 5.
+ */
+function formatException(error, maxStackLines) {
+ maxStackLines = maxStackLines || 5;
+ var message = error.toString();
+ if (error.stack) {
+ var stack = error.stack.split('\n');
+ if (stack[0].indexOf(message) === -1) {
+ maxStackLines++;
+ stack.unshift(error.message);
+ }
+ message = stack.slice(0, maxStackLines).join('\n');
+ }
+ return message;
+}
+/**
+ * Returns a function that gets the file name and line number from a
+ * location in the stack if available based on the call site.
+ *
+ * Note: this returns another function because accessing .stack is very
+ * expensive in Chrome.
+ */
+function callerFile(offset) {
+ var error = new Error();
+
+ return function() {
+ var line = (error.stack || '').split('\n')[offset];
+
+ // Clean up the stack trace line
+ if (line) {
+ if (line.indexOf('@') !== -1) {
+ // Firefox
+ line = line.substring(line.indexOf('@')+1);
+ } else {
+ // Chrome
+ line = line.substring(line.indexOf('(')+1).replace(')', '');
+ }
+ }
+
+ return line || '';
+ };
+}
+
+/**
+ * Triggers a browser event. Attempts to choose the right event if one is
+ * not specified.
+ *
+ * @param {Object} Either a wrapped jQuery/jqLite node or a DOMElement
+ * @param {String} Optional event type.
+ */
function browserTrigger(element, type) {
if (element && !element.nodeName) element = element[0];
if (!element) return;
@@ -136,10 +196,17 @@ function browserTrigger(element, type) {
}
}
+/**
+ * Don't use the jQuery trigger method since it works incorrectly.
+ *
+ * jQuery notifies listeners and then changes the state of a checkbox and
+ * does not create a real browser event. A real click changes the state of
+ * the checkbox and then notifies listeners.
+ *
+ * To work around this we instead use our own handler that fires a real event.
+ */
_jQuery.fn.trigger = function(type) {
return this.each(function(index, node) {
browserTrigger(node, type);
});
};
-
-