diff options
| author | Misko Hevery | 2011-11-04 12:33:01 -0700 | 
|---|---|---|
| committer | Misko Hevery | 2011-11-14 20:31:14 -0800 | 
| commit | a87f2fb9e4d65ac5d260e914b5e31aa0e0f47b2c (patch) | |
| tree | 93e69475affd24ef5b16c68e47d1476bc37787a7 | |
| parent | c27aba4354c69c4a67fab587a59a8079cc9edc91 (diff) | |
| download | angular.js-a87f2fb9e4d65ac5d260e914b5e31aa0e0f47b2c.tar.bz2 | |
refactor(mock): moved mocks into its own module
| -rw-r--r-- | src/Angular.js | 14 | ||||
| -rw-r--r-- | src/angular-mocks.js | 106 | ||||
| -rw-r--r-- | test/BinderSpec.js | 5 | ||||
| -rw-r--r-- | test/ResourceSpec.js | 4 | ||||
| -rw-r--r-- | test/angular-mocksSpec.js | 54 | ||||
| -rw-r--r-- | test/matchers.js | 17 | ||||
| -rw-r--r-- | test/scenario/RunnerSpec.js | 4 | ||||
| -rw-r--r-- | test/scenario/matchersSpec.js | 6 | ||||
| -rw-r--r-- | test/service/filter/orderBySpec.js | 15 | ||||
| -rw-r--r-- | test/service/logSpec.js | 2 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 131 | 
11 files changed, 219 insertions, 139 deletions
| diff --git a/src/Angular.js b/src/Angular.js index fcb13881..af441e1c 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -426,6 +426,17 @@ function trim(value) {    return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;  } +/** + * @ngdoc function + * @name angular.isElement + * @function + * + * @description + * Determines if a reference is a DOM element (or wrapped jQuery element). + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). + */  function isElement(node) {    return node &&      (node.nodeName  // we are a direct element @@ -1012,7 +1023,7 @@ function assertArg(arg, name, reason) {  function assertArgFn(arg, name) {    assertArg(isFunction(arg), name, 'not a function, got ' + -      (typeof arg == 'object' ? arg.constructor.name : typeof arg)); +      (typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));    return arg;  } @@ -1034,6 +1045,7 @@ function publishExternalAPI(angular){      'isFunction': isFunction,      'isObject': isObject,      'isNumber': isNumber, +    'isElement': isElement,      'isArray': isArray,      'version': version,      'isDate': isDate, diff --git a/src/angular-mocks.js b/src/angular-mocks.js index 497fdc58..970a1b6d 100644 --- a/src/angular-mocks.js +++ b/src/angular-mocks.js @@ -363,16 +363,21 @@ angular.mock.$ExceptionHandlerProvider = function(){    var handler;    this.mode = function(mode){ -    handler = { -     rethrow: function(e) { -      throw e; -     }, -     log: angular.extend(function log(e) { -       log.errors.push(e); -     }, {errors:[]}) -    }[mode]; -    if (!handler) { -      throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); +    switch(mode) { +      case 'rethrow': +        handler = function(e) { +          throw e; +        } +        break; +      case 'log': +        var errors = []; +        handler = function(e) { +          errors.push(e); +        } +        handler.errors = errors; +        break; +      default: +        throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");      }    }; @@ -396,6 +401,12 @@ angular.mock.$ExceptionHandlerProvider = function(){   * See {@link angular.mock} for more info on angular mocks.   */  angular.mock.$LogProvider = function(){ + +  function concat(array1, array2, index) { +    return array1.concat(Array.prototype.slice.call(array2, index)); +  } + +    this.$get = function () {      var $log = {        log: function() { $log.log.logs.push(concat([], arguments, 0)); }, @@ -416,7 +427,7 @@ angular.mock.$LogProvider = function(){        angular.forEach(['error', 'warn', 'info', 'log'], function(logLevel) {          angular.forEach($log[logLevel].logs, function(log) {            angular.forEach(log, function (logItem) { -            errors.push('MOCK $log (' + logLevel + '): ' + (logItem.stack || logItem)); +            errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + (logItem.stack || ''));            });          });        }); @@ -569,3 +580,76 @@ angular.mock.TzDate = function (offset, timestamp) {  //make "tzDateInstance instanceof Date" return true  angular.mock.TzDate.prototype = Date.prototype; + + +/** + * Method for serializing common objects into strings, useful for debugging. + * @param {*} object - any object to turn into string. + * @return a serialized string of the argument + */ +angular.mock.dump = function(object){ +  var out; +  if (angular.isElement(object)) { +    object = angular.element(object); +    out = angular.element('<div></div>') +    angular.forEach(object, function(element){ +      out.append(angular.element(element).clone()); +    }); +    out = out.html(); +  } else if (angular.isObject(object)) { +    if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) { +      out = serializeScope(object); +    } else { +      out = angular.toJson(object, true); +    } +  } else { +    out = String(object); +  } +  return out; + +  function serializeScope(scope, offset) { +    offset = offset ||  '  '; +    var log = [offset + 'Scope(' + scope.$id + '): {']; +    for ( var key in scope ) { +      if (scope.hasOwnProperty(key) && !key.match(/^(\$|this)/)) { +        log.push('  ' + key + ': ' + angular.toJson(scope[key])); +      } +    } +    var child = scope.$$childHead; +    while(child) { +      log.push(serializeScope(child, offset + '  ')); +      child = child.$$nextSibling; +    } +    log.push('}'); +    return log.join('\n' + offset); +  } +}; + +window.jstestdriver && (function(window){ +  /** +   * Global method to output any number of objects into JSTD console. Useful for debugging. +   */ +  window.dump = function() { +    var args = []; +    angular.forEach(arguments, function(arg){ +      args.push(angular.mock.dump(arg)); +    }); +    jstestdriver.console.log.apply(jstestdriver.console, args); +  }; +})(window); + + +window.jasmine && (function(window){ +  window.inject = function (){ +    var blockFns = Array.prototype.slice.call(arguments, 0); +    return function(){ +      var injector = this.$injector; +      if (!injector) { +        injector = this.$injector =  angular.injector('NG', 'NG_MOCK'); +      } +      for(var i = 0, ii = blockFns.length; i < ii; i++) { +        injector.invoke(this, blockFns[i]); +      } +    }; +  } +})(window); diff --git a/test/BinderSpec.js b/test/BinderSpec.js index 01e61b57..691e2db8 100644 --- a/test/BinderSpec.js +++ b/test/BinderSpec.js @@ -1,6 +1,11 @@  'use strict';  describe('Binder', function() { + +  function childNode(element, index) { +    return jqLite(element[0].childNodes[index]); +  } +    beforeEach(function() {      this.compileToHtml = function (content) {        var html; diff --git a/test/ResourceSpec.js b/test/ResourceSpec.js index 2091a936..57aaffe0 100644 --- a/test/ResourceSpec.js +++ b/test/ResourceSpec.js @@ -3,6 +3,10 @@  describe("resource", function() {    var resource, CreditCard, callback; +  function nakedExpect(obj) { +    return expect(angular.fromJson(angular.toJson(obj))); +  } +    beforeEach(inject(      function($provide) {        $provide.value('$xhr.error', jasmine.createSpy('xhr.error')); diff --git a/test/angular-mocksSpec.js b/test/angular-mocksSpec.js index b4ceb275..1668d500 100644 --- a/test/angular-mocksSpec.js +++ b/test/angular-mocksSpec.js @@ -283,4 +283,58 @@ describe('mocks', function() {        }).toThrow("Unknown mode 'XXX', only 'log'/'rethrow' modes are allowed!");      }));    }); + + +  describe('angular.mock.debug', function(){ +    var d = angular.mock.dump; + + +    it('should serialize primitive types', function(){ +      expect(d(undefined)).toEqual('undefined'); +      expect(d(1)).toEqual('1'); +      expect(d(null)).toEqual('null'); +      expect(d('abc')).toEqual('abc'); +    }); + + +    it('should serialize element', function(){ +      var e = angular.element('<div>abc</div><span>xyz</span>'); +      expect(d(e).toLowerCase()).toEqual('<div>abc</div><span>xyz</span>'); +      expect(d(e[0]).toLowerCase()).toEqual('<div>abc</div>'); +    }); + +    it('should serialize scope', inject(function($rootScope){ +      $rootScope.obj = {abc:'123'}; +      expect(d($rootScope)).toMatch(/Scope\(.*\): \{/); +      expect(d($rootScope)).toMatch(/{"abc":"123"}/); +    })); + + +    it('should be published on window', function(){ +      expect(window.dump instanceof Function).toBe(true); +    }); +  }); + +  describe('jasmine inject', function(){ +    it('should call invoke', function(){ +      var count = 0; +      function fn1(){ +        expect(this).toBe(self); +        count++; +      } +      function fn2(){ +        expect(this).toBe(self); +        count++; +      } +      var fn = inject(fn1, fn2); +      var self = { +        $injector: { +          invoke: function(self, fn) { fn.call(self); } +        } +      }; + +      fn.call(self); +      expect(count).toBe(2); +    }); +  });  }); diff --git a/test/matchers.js b/test/matchers.js index 9923bd7e..fbe86a5a 100644 --- a/test/matchers.js +++ b/test/matchers.js @@ -23,6 +23,13 @@ beforeEach(function() {      };    } +  function indexOf(array, obj) { +    for ( var i = 0; i < array.length; i++) { +      if (obj === array[i]) return i; +    } +    return -1; +  } +    this.addMatchers({      toBeInvalid: cssMatcher('ng-invalid', 'ng-valid'),      toBeValid: cssMatcher('ng-valid', 'ng-invalid'), @@ -84,6 +91,16 @@ beforeEach(function() {      toBeOneOf: function() {        return indexOf(arguments, this.actual) !== -1; +    }, + +    toHaveClass: function(clazz) { +      this.message = function() { +        return "Expected '" + angular.mock.dump(this.actual) + "' to have class '" + clazz + "'."; +      }; +      return this.actual.hasClass ? +              this.actual.hasClass(clazz) : +              angular.element(this.actual).hasClass(clazz);      } +    });  }); diff --git a/test/scenario/RunnerSpec.js b/test/scenario/RunnerSpec.js index 62d84fca..15bcc4b0 100644 --- a/test/scenario/RunnerSpec.js +++ b/test/scenario/RunnerSpec.js @@ -46,8 +46,8 @@ describe('angular.scenario.Runner', function() {      runner.createSpecRunner_ = function(scope) {        return scope.$new(MockSpecRunner);      }; -    runner.on('SpecError', rethrow); -    runner.on('StepError', rethrow); +    runner.on('SpecError', angular.mock.rethrow); +    runner.on('StepError', angular.mock.rethrow);    });    afterEach(function() { diff --git a/test/scenario/matchersSpec.js b/test/scenario/matchersSpec.js index 7ab41cf2..7a5217d7 100644 --- a/test/scenario/matchersSpec.js +++ b/test/scenario/matchersSpec.js @@ -42,4 +42,10 @@ describe('angular.scenario.matchers', function () {      expectMatcher(3, function() { matchers.toBeLessThan(10); });      expectMatcher(3, function() { matchers.toBeGreaterThan(-5); });    }); + +  it('should have toHaveClass matcher', function(){ +    var e = angular.element('<div class="abc">'); +    expect(e).not.toHaveClass('none'); +    expect(e).toHaveClass('abc'); +  });  }); diff --git a/test/service/filter/orderBySpec.js b/test/service/filter/orderBySpec.js index f59fad55..5c117891 100644 --- a/test/service/filter/orderBySpec.js +++ b/test/service/filter/orderBySpec.js @@ -12,18 +12,15 @@ describe('Filter: orderBy', function() {    });    it('shouldSortArrayInReverse', function() { -    assertJsonEquals([{a:15},{a:2}], orderBy([{a:15},{a:2}], 'a', true)); -    assertJsonEquals([{a:15},{a:2}], orderBy([{a:15},{a:2}], 'a', "T")); -    assertJsonEquals([{a:15},{a:2}], orderBy([{a:15},{a:2}], 'a', "reverse")); +    expect(orderBy([{a:15}, {a:2}], 'a', true)).toEqualData([{a:15}, {a:2}]); +    expect(orderBy([{a:15}, {a:2}], 'a', "T")).toEqualData([{a:15}, {a:2}]); +    expect(orderBy([{a:15}, {a:2}], 'a', "reverse")).toEqualData([{a:15}, {a:2}]);    });    it('should sort array by predicate', function() { -    assertJsonEquals([{a:2, b:1},{a:15, b:1}], -        orderBy([{a:15, b:1},{a:2, b:1}], ['a', 'b'])); -    assertJsonEquals([{a:2, b:1},{a:15, b:1}], -        orderBy([{a:15, b:1},{a:2, b:1}], ['b', 'a'])); -    assertJsonEquals([{a:15, b:1},{a:2, b:1}], -        orderBy([{a:15, b:1},{a:2, b:1}], ['+b', '-a'])); +    expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['a', 'b'])).toEqualData([{a:2, b:1}, {a:15, b:1}]); +    expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['b', 'a'])).toEqualData([{a:2, b:1}, {a:15, b:1}]); +    expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['+b', '-a'])).toEqualData([{a:15, b:1}, {a:2, b:1}]);    });    it('should use function', function() { diff --git a/test/service/logSpec.js b/test/service/logSpec.js index 93e705a9..ee250b66 100644 --- a/test/service/logSpec.js +++ b/test/service/logSpec.js @@ -13,7 +13,7 @@ describe('$log', function() {      $window = {};      logger = '';      $provide.service('$log', $LogProvider); -    $provide.value('$exceptionHandler', rethrow); +    $provide.value('$exceptionHandler', angular.mock.rethrow);      $provide.value('$window', $window);    })); diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 96deac5a..b2bcb519 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -9,46 +9,10 @@  _jQuery.event.special.change = undefined; -if (window.jstestdriver) { -  window.jstd = jstestdriver; -  window.dump = function dump() { -    var args = []; -    forEach(arguments, function(arg){ -      if (isElement(arg)) { -        arg = sortedHtml(arg); -      } else if (isObject(arg)) { -        if (isFunction(arg.$eval) && isFunction(arg.$apply)) { -          arg = dumpScope(arg); -        } else { -          arg = toJson(arg, true); -        } -      } -      args.push(arg); -    }); -    jstd.console.log.apply(jstd.console, args); -  }; -} - -function dumpScope(scope, offset) { -  offset = offset ||  '  '; -  var log = [offset + 'Scope(' + scope.$id + '): {']; -  for ( var key in scope ) { -    if (scope.hasOwnProperty(key) && !key.match(/^(\$|this)/)) { -      log.push('  ' + key + ': ' + toJson(scope[key])); -    } -  } -  var child = scope.$$childHead; -  while(child) { -    log.push(dumpScope(child, offset + '  ')); -    child = child.$$nextSibling; -  } -  log.push('}'); -  return log.join('\n' + offset); -} - -publishExternalAPI(angular) +publishExternalAPI(angular); +bindJQuery();  beforeEach(function() { -  publishExternalAPI(angular) +  publishExternalAPI(angular);    // workaround for IE bug https://plus.google.com/104744871076396904202/posts/Kqjuj6RSbbT    // IE overwrite window.jQuery with undefined because of empty jQuery var statement, so we have to @@ -63,43 +27,20 @@ beforeEach(function() {    // reset to jQuery or default to us.    bindJQuery();    jqLite(document.body).html(''); - -  this.addMatchers({ -    toHaveClass: function(clazz) { -      this.message = function() { -        return "Expected '" + sortedHtml(this.actual) + "' to have class '" + clazz + "'."; -      }; -      return this.actual.hasClass ? -              this.actual.hasClass(clazz) : -              jqLite(this.actual).hasClass(clazz); -    } -  }); -  }); -function inject(){ -  var blockFns = sliceArgs(arguments); -  return function(){ -    var spec = this; -    spec.$injector = spec.$injector || angular.injector('NG', 'NG_MOCK'); -    angular.forEach(blockFns, function(fn){ -      spec.$injector.invoke(spec, fn); -    }); -  }; -} - - -afterEach(inject(function($rootScope, $log) { -  // release the injector -  dealoc($rootScope); - -  // check $log mock -  $log.assertEmpty && $log.assertEmpty(); +afterEach(function() { +  if (this.$injector) { +    var $rootScope = this.$injector('$rootScope'); +    var $log = this.$injector('$log'); +    // release the injector +    dealoc($rootScope); -  clearJqCache(); -})); +    // check $log mock +    $log.assertEmpty && $log.assertEmpty(); +  } -function clearJqCache() { +  // complain about uncleared jqCache references    var count = 0;    forEachSorted(jqCache, function(value, key){      count ++; @@ -115,15 +56,8 @@ function clearJqCache() {    if (count) {      fail('Found jqCache references that were not deallocated!');    } -} - -function nakedExpect(obj) { -  return expect(angular.fromJson(angular.toJson(obj))); -} +}); -function childNode(element, index) { -  return jqLite(element[0].childNodes[index]); -}  function dealoc(obj) {    if (obj) { @@ -240,43 +174,10 @@ function isCssVisible(node) {  }  function assertHidden(node) { -  assertFalse("Node should be hidden but vas visible: " + sortedHtml(node), isCssVisible(node)); +  assertFalse("Node should be hidden but vas visible: " + angular.mock.dump(node), isCssVisible(node));  }  function assertVisible(node) { -  assertTrue("Node should be visible but vas hidden: " + sortedHtml(node), isCssVisible(node)); -} - -function assertJsonEquals(expected, actual) { -  assertEquals(toJson(expected), toJson(actual)); -} - -function assertUndefined(value) { -  assertEquals('undefined', typeof value); +  assertTrue("Node should be visible but vas hidden: " + angular.mock.dump(node), isCssVisible(node));  } -function assertDefined(value) { -  assertTrue(toJson(value), !!value); -} - -function assertThrows(error, fn){ -  var exception = null; -  try { -    fn(); -  } catch(e) { -    exception = e; -  } -  if (!exception) { -    fail("Expecting exception, none thrown"); -  } -  assertEquals(error, exception); -} - -window.log = noop; -window.error = noop; - -function rethrow(e) { -  if(e) { -    throw e; -  } -} | 
