aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMisko Hevery2011-11-04 12:33:01 -0700
committerMisko Hevery2011-11-14 20:31:14 -0800
commita87f2fb9e4d65ac5d260e914b5e31aa0e0f47b2c (patch)
tree93e69475affd24ef5b16c68e47d1476bc37787a7
parentc27aba4354c69c4a67fab587a59a8079cc9edc91 (diff)
downloadangular.js-a87f2fb9e4d65ac5d260e914b5e31aa0e0f47b2c.tar.bz2
refactor(mock): moved mocks into its own module
-rw-r--r--src/Angular.js14
-rw-r--r--src/angular-mocks.js106
-rw-r--r--test/BinderSpec.js5
-rw-r--r--test/ResourceSpec.js4
-rw-r--r--test/angular-mocksSpec.js54
-rw-r--r--test/matchers.js17
-rw-r--r--test/scenario/RunnerSpec.js4
-rw-r--r--test/scenario/matchersSpec.js6
-rw-r--r--test/service/filter/orderBySpec.js15
-rw-r--r--test/service/logSpec.js2
-rw-r--r--test/testabilityPatch.js131
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;
- }
-}