diff options
| author | Elliott Sprehn | 2010-10-19 13:17:49 -0700 | 
|---|---|---|
| committer | Elliott Sprehn | 2010-10-20 14:38:00 -0700 | 
| commit | 2115db69035c5993533fe7a3825e64cf6e9068ad (patch) | |
| tree | 796a502b28cd2bda8108a672eac0bf28c8bc21d4 /src/scenario/Scenario.js | |
| parent | 9c8b1800b90e14b643bab6ada8e96f8f850e84a6 (diff) | |
| download | angular.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.js | 97 | 
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);    });  }; - -  | 
