refactor(mock): moved mocks into its own module

This commit is contained in:
Misko Hevery 2011-11-04 12:33:01 -07:00
parent c27aba4354
commit a87f2fb9e4
11 changed files with 219 additions and 139 deletions

View file

@ -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,

106
src/angular-mocks.js vendored
View file

@ -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);

View file

@ -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;

View file

@ -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'));

View file

@ -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);
});
});
});

View file

@ -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);
}
});
});

View file

@ -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() {

View file

@ -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');
});
});

View file

@ -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() {

View file

@ -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);
}));

View file

@ -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(function() {
if (this.$injector) {
var $rootScope = this.$injector('$rootScope');
var $log = this.$injector('$log');
// release the injector
dealoc($rootScope);
// check $log mock
$log.assertEmpty && $log.assertEmpty();
}
afterEach(inject(function($rootScope, $log) {
// release the injector
dealoc($rootScope);
// check $log mock
$log.assertEmpty && $log.assertEmpty();
clearJqCache();
}));
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));
assertTrue("Node should be visible but vas hidden: " + angular.mock.dump(node), isCssVisible(node));
}
function assertJsonEquals(expected, actual) {
assertEquals(toJson(expected), toJson(actual));
}
function assertUndefined(value) {
assertEquals('undefined', typeof value);
}
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;
}
}