Files
angular.js/test/AngularSpec.js
Peter Bacon Darwin 7a586e5c19 fix(*): protect calls to hasOwnProperty in public API
Objects received from outside AngularJS may have had their `hasOwnProperty`
method overridden with something else. In cases where we can do this without
incurring a performance penalty we call directly on Object.prototype.hasOwnProperty
to ensure that we use the correct method.

Also, we have some internal hash objects, where the keys for the map are provided
from outside AngularJS. In such cases we either prevent `hasOwnProperty` from
being used as a key or provide some other way of preventing our objects from
having their `hasOwnProperty` overridden.

BREAKING CHANGE: Inputs with name equal to "hasOwnProperty" are not allowed inside
form or ngForm directives.

Before, inputs whose name was "hasOwnProperty" were quietly ignored and not added
to the scope.  Now a badname exception is thrown.

Using "hasOwnProperty" for an input name would be very unusual and bad practice.
Either do not include such an input in a `form` or `ngForm` directive or change
the name of the input.

Closes #3331
2013-10-07 09:01:13 -07:00

1043 lines
34 KiB
JavaScript

'use strict';
describe('angular', function() {
var element;
afterEach(function(){
dealoc(element);
});
describe('case', function() {
it('should change case', function() {
expect(lowercase('ABC90')).toEqual('abc90');
expect(manualLowercase('ABC90')).toEqual('abc90');
expect(uppercase('abc90')).toEqual('ABC90');
expect(manualUppercase('abc90')).toEqual('ABC90');
});
});
describe("copy", function() {
it("should return same object", function () {
var obj = {};
var arr = [];
expect(copy({}, obj)).toBe(obj);
expect(copy([], arr)).toBe(arr);
});
it("should copy Date", function() {
var date = new Date(123);
expect(copy(date) instanceof Date).toBeTruthy();
expect(copy(date).getTime()).toEqual(123);
expect(copy(date) === date).toBeFalsy();
});
it("should copy RegExp", function() {
var re = new RegExp(".*");
expect(copy(re) instanceof RegExp).toBeTruthy();
expect(copy(re).source).toBe(".*");
expect(copy(re) === re).toBe(false);
});
it("should copy literal RegExp", function() {
var re = /.*/;
expect(copy(re) instanceof RegExp).toBeTruthy();
expect(copy(re).source).toEqual(".*");
expect(copy(re) === re).toBeFalsy();
});
it("should deeply copy literal RegExp", function() {
var objWithRegExp = {
re: /.*/
};
expect(copy(objWithRegExp).re instanceof RegExp).toBeTruthy();
expect(copy(objWithRegExp).re.source).toEqual(".*");
expect(copy(objWithRegExp.re) === objWithRegExp.re).toBeFalsy();
});
it("should deeply copy an array into an existing array", function() {
var src = [1, {name:"value"}];
var dst = [{key:"v"}];
expect(copy(src, dst)).toBe(dst);
expect(dst).toEqual([1, {name:"value"}]);
expect(dst[1]).toEqual({name:"value"});
expect(dst[1]).not.toBe(src[1]);
});
it("should deeply copy an array into a new array", function() {
var src = [1, {name:"value"}];
var dst = copy(src);
expect(src).toEqual([1, {name:"value"}]);
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst[1]).not.toBe(src[1]);
});
it('should copy empty array', function() {
var src = [];
var dst = [{key: "v"}];
expect(copy(src, dst)).toEqual([]);
expect(dst).toEqual([]);
});
it("should deeply copy an object into an existing object", function() {
var src = {a:{name:"value"}};
var dst = {b:{key:"v"}};
expect(copy(src, dst)).toBe(dst);
expect(dst).toEqual({a:{name:"value"}});
expect(dst.a).toEqual(src.a);
expect(dst.a).not.toBe(src.a);
});
it("should deeply copy an object into a non-existing object", function() {
var src = {a:{name:"value"}};
var dst = copy(src, undefined);
expect(src).toEqual({a:{name:"value"}});
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.a).toEqual(src.a);
expect(dst.a).not.toBe(src.a);
});
it("should copy primitives", function() {
expect(copy(null)).toEqual(null);
expect(copy('')).toBe('');
expect(copy('lala')).toBe('lala');
expect(copy(123)).toEqual(123);
expect(copy([{key:null}])).toEqual([{key:null}]);
});
it('should throw an exception if a Scope is being copied', inject(function($rootScope) {
expect(function() { copy($rootScope.$new()); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
}));
it('should throw an exception if a Window is being copied', function() {
expect(function() { copy(window); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
});
it('should throw an exception when source and destination are equivalent', function() {
var src, dst;
src = dst = {key: 'value'};
expect(function() { copy(src, dst); }).toThrowMinErr("ng", "cpi", "Can't copy! Source and destination are identical.");
src = dst = [2, 4];
expect(function() { copy(src, dst); }).toThrowMinErr("ng", "cpi", "Can't copy! Source and destination are identical.");
});
it('should not copy the private $$hashKey', function() {
var src,dst;
src = {};
hashKey(src);
dst = copy(src);
expect(hashKey(dst)).not.toEqual(hashKey(src));
});
it('should retain the previous $$hashKey', function() {
var src,dst,h;
src = {};
dst = {};
// force creation of a hashkey
h = hashKey(dst);
hashKey(src);
dst = copy(src,dst);
// make sure we don't copy the key
expect(hashKey(dst)).not.toEqual(hashKey(src));
// make sure we retain the old key
expect(hashKey(dst)).toEqual(h);
});
});
describe("extend", function() {
it('should not copy the private $$hashKey', function() {
var src,dst;
src = {};
dst = {};
hashKey(src);
dst = extend(dst,src);
expect(hashKey(dst)).not.toEqual(hashKey(src));
});
it('should retain the previous $$hashKey', function() {
var src,dst,h;
src = {};
dst = {};
h = hashKey(dst);
hashKey(src);
dst = extend(dst,src);
// make sure we don't copy the key
expect(hashKey(dst)).not.toEqual(hashKey(src));
// make sure we retain the old key
expect(hashKey(dst)).toEqual(h);
});
it('should work when extending with itself', function() {
var src,dst,h;
dst = src = {};
h = hashKey(dst);
dst = extend(dst,src);
// make sure we retain the old key
expect(hashKey(dst)).toEqual(h);
});
});
describe('shallow copy', function() {
it('should make a copy', function() {
var original = {key:{}};
var copy = shallowCopy(original);
expect(copy).toEqual(original);
expect(copy.key).toBe(original.key);
});
it('should not copy $$ properties nor prototype properties', function() {
var original = {$$some: true, $$: true};
var clone = {};
expect(shallowCopy(original, clone)).toBe(clone);
expect(clone.$$some).toBeUndefined();
expect(clone.$$).toBeUndefined();
});
});
describe('elementHTML', function() {
it('should dump element', function() {
expect(startingTag('<div attr="123">something<span></span></div>')).
toEqual('<div attr="123">');
});
});
describe('equals', function() {
it('should return true if same object', function() {
var o = {};
expect(equals(o, o)).toEqual(true);
expect(equals(o, {})).toEqual(true);
expect(equals(1, '1')).toEqual(false);
expect(equals(1, '2')).toEqual(false);
});
it('should recurse into object', function() {
expect(equals({}, {})).toEqual(true);
expect(equals({name:'misko'}, {name:'misko'})).toEqual(true);
expect(equals({name:'misko', age:1}, {name:'misko'})).toEqual(false);
expect(equals({name:'misko'}, {name:'misko', age:1})).toEqual(false);
expect(equals({name:'misko'}, {name:'adam'})).toEqual(false);
expect(equals(['misko'], ['misko'])).toEqual(true);
expect(equals(['misko'], ['adam'])).toEqual(false);
expect(equals(['misko'], ['misko', 'adam'])).toEqual(false);
});
it('should ignore undefined member variables during comparison', function() {
var obj1 = {name: 'misko'},
obj2 = {name: 'misko', undefinedvar: undefined};
expect(equals(obj1, obj2)).toBe(true);
expect(equals(obj2, obj1)).toBe(true);
});
it('should ignore $ member variables', function() {
expect(equals({name:'misko', $id:1}, {name:'misko', $id:2})).toEqual(true);
expect(equals({name:'misko'}, {name:'misko', $id:2})).toEqual(true);
expect(equals({name:'misko', $id:1}, {name:'misko'})).toEqual(true);
});
it('should ignore functions', function() {
expect(equals({func: function() {}}, {bar: function() {}})).toEqual(true);
});
it('should work well with nulls', function() {
expect(equals(null, '123')).toBe(false);
expect(equals('123', null)).toBe(false);
var obj = {foo:'bar'};
expect(equals(null, obj)).toBe(false);
expect(equals(obj, null)).toBe(false);
expect(equals(null, null)).toBe(true);
});
it('should work well with undefined', function() {
expect(equals(undefined, '123')).toBe(false);
expect(equals('123', undefined)).toBe(false);
var obj = {foo:'bar'};
expect(equals(undefined, obj)).toBe(false);
expect(equals(obj, undefined)).toBe(false);
expect(equals(undefined, undefined)).toBe(true);
});
it('should treat two NaNs as equal', function() {
expect(equals(NaN, NaN)).toBe(true);
});
it('should compare Scope instances only by identity', inject(function($rootScope) {
var scope1 = $rootScope.$new(),
scope2 = $rootScope.$new();
expect(equals(scope1, scope1)).toBe(true);
expect(equals(scope1, scope2)).toBe(false);
expect(equals($rootScope, scope1)).toBe(false);
expect(equals(undefined, scope1)).toBe(false);
}));
it('should compare Window instances only by identity', function() {
expect(equals(window, window)).toBe(true);
expect(equals(window, window.parent)).toBe(false);
expect(equals(window, undefined)).toBe(false);
});
it('should compare dates', function() {
expect(equals(new Date(0), new Date(0))).toBe(true);
expect(equals(new Date(0), new Date(1))).toBe(false);
expect(equals(new Date(0), 0)).toBe(false);
expect(equals(0, new Date(0))).toBe(false);
});
it('should correctly test for keys that are present on Object.prototype', function() {
// MS IE8 just doesn't work for this kind of thing, since "for ... in" doesn't return
// things like hasOwnProperty even if it is explicitly defined on the actual object!
if (msie<=8) return;
expect(equals({}, {hasOwnProperty: 1})).toBe(false);
expect(equals({}, {toString: null})).toBe(false);
});
it('should compare regular expressions', function() {
expect(equals(/abc/, /abc/)).toBe(true);
expect(equals(/abc/i, new RegExp('abc', 'i'))).toBe(true);
expect(equals(new RegExp('abc', 'i'), new RegExp('abc', 'i'))).toBe(true);
expect(equals(new RegExp('abc', 'i'), new RegExp('abc'))).toBe(false);
expect(equals(/abc/i, /abc/)).toBe(false);
expect(equals(/abc/, /def/)).toBe(false);
expect(equals(/^abc/, /abc/)).toBe(false);
expect(equals(/^abc/, '/^abc/')).toBe(false);
});
it('should return false when comparing an object and an array', function() {
expect(equals({}, [])).toBe(false);
expect(equals([], {})).toBe(false);
});
});
describe('size', function() {
it('should return the number of items in an array', function() {
expect(size([])).toBe(0);
expect(size(['a', 'b', 'c'])).toBe(3);
});
it('should return the number of properties of an object', function() {
expect(size({})).toBe(0);
expect(size({a:1, b:'a', c:noop})).toBe(3);
});
it('should return the number of own properties of an object', function() {
var obj = inherit({protoProp: 'c', protoFn: noop}, {a:1, b:'a', c:noop});
expect(size(obj)).toBe(5);
expect(size(obj, true)).toBe(3);
});
it('should return the string length', function() {
expect(size('')).toBe(0);
expect(size('abc')).toBe(3);
});
it('should not rely on length property of an object to determine its size', function() {
expect(size({length:99})).toBe(1);
});
});
describe('parseKeyValue', function() {
it('should parse a string into key-value pairs', function() {
expect(parseKeyValue('')).toEqual({});
expect(parseKeyValue('simple=pair')).toEqual({simple: 'pair'});
expect(parseKeyValue('first=1&second=2')).toEqual({first: '1', second: '2'});
expect(parseKeyValue('escaped%20key=escaped%20value')).
toEqual({'escaped key': 'escaped value'});
expect(parseKeyValue('emptyKey=')).toEqual({emptyKey: ''});
expect(parseKeyValue('flag1&key=value&flag2')).
toEqual({flag1: true, key: 'value', flag2: true});
});
it('should ignore key values that are not valid URI components', function() {
expect(function() { parseKeyValue('%'); }).not.toThrow();
expect(parseKeyValue('%')).toEqual({});
expect(parseKeyValue('invalid=%')).toEqual({ invalid: undefined });
expect(parseKeyValue('invalid=%&valid=good')).toEqual({ invalid: undefined, valid: 'good' });
});
it('should parse a string into key-value pairs with duplicates grouped in an array', function() {
expect(parseKeyValue('')).toEqual({});
expect(parseKeyValue('duplicate=pair')).toEqual({duplicate: 'pair'});
expect(parseKeyValue('first=1&first=2')).toEqual({first: ['1','2']});
expect(parseKeyValue('escaped%20key=escaped%20value&&escaped%20key=escaped%20value2')).
toEqual({'escaped key': ['escaped value','escaped value2']});
expect(parseKeyValue('flag1&key=value&flag1')).
toEqual({flag1: [true,true], key: 'value'});
expect(parseKeyValue('flag1&flag1=value&flag1=value2&flag1')).
toEqual({flag1: [true,'value','value2',true]});
});
});
describe('toKeyValue', function() {
it('should serialize key-value pairs into string', function() {
expect(toKeyValue({})).toEqual('');
expect(toKeyValue({simple: 'pair'})).toEqual('simple=pair');
expect(toKeyValue({first: '1', second: '2'})).toEqual('first=1&second=2');
expect(toKeyValue({'escaped key': 'escaped value'})).
toEqual('escaped%20key=escaped%20value');
expect(toKeyValue({emptyKey: ''})).toEqual('emptyKey=');
});
it('should serialize true values into flags', function() {
expect(toKeyValue({flag1: true, key: 'value', flag2: true})).toEqual('flag1&key=value&flag2');
});
it('should serialize duplicates into duplicate param strings', function() {
expect(toKeyValue({key: [323,'value',true]})).toEqual('key=323&key=value&key');
expect(toKeyValue({key: [323,'value',true, 1234]})).
toEqual('key=323&key=value&key&key=1234');
});
});
describe('forEach', function() {
it('should iterate over *own* object properties', function() {
function MyObj() {
this.bar = 'barVal';
this.baz = 'bazVal';
}
MyObj.prototype.foo = 'fooVal';
var obj = new MyObj(),
log = [];
forEach(obj, function(value, key) { log.push(key + ':' + value)});
expect(log).toEqual(['bar:barVal', 'baz:bazVal']);
});
it('should not break if obj is an array we override hasOwnProperty', function() {
var obj = [];
obj[0] = 1;
obj[1] = 2;
obj.hasOwnProperty = null;
var log = [];
forEach(obj, function(value, key) {
log.push(key + ':' + value);
});
expect(log).toEqual(['0:1', '1:2']);
});
it('should handle JQLite and jQuery objects like arrays', function() {
var jqObject = jqLite("<p><span>s1</span><span>s2</span></p>").find("span"),
log = [];
forEach(jqObject, function(value, key) { log.push(key + ':' + value.innerHTML); });
expect(log).toEqual(['0:s1', '1:s2']);
});
it('should handle NodeList objects like arrays', function() {
var nodeList = jqLite("<p><span>a</span><span>b</span><span>c</span></p>")[0].childNodes,
log = [];
forEach(nodeList, function(value, key) { log.push(key + ':' + value.innerHTML)});
expect(log).toEqual(['0:a', '1:b', '2:c']);
});
it('should handle HTMLCollection objects like arrays', function() {
document.body.innerHTML = "<p>" +
"<a name='x'>a</a>" +
"<a name='y'>b</a>" +
"<a name='x'>c</a>" +
"</p>";
var htmlCollection = document.getElementsByName('x'),
log = [];
forEach(htmlCollection, function(value, key) { log.push(key + ':' + value.innerHTML)});
expect(log).toEqual(['0:a', '1:c']);
});
it('should handle arguments objects like arrays', function() {
var args,
log = [];
(function(){ args = arguments}('a', 'b', 'c'));
forEach(args, function(value, key) { log.push(key + ':' + value)});
expect(log).toEqual(['0:a', '1:b', '2:c']);
});
it('should handle string values like arrays', function() {
var log = [];
forEach('bar', function(value, key) { log.push(key + ':' + value)});
expect(log).toEqual(['0:b', '1:a', '2:r']);
});
it('should handle objects with length property as objects', function() {
var obj = {
'foo' : 'bar',
'length': 2
},
log = [];
forEach(obj, function(value, key) { log.push(key + ':' + value)});
expect(log).toEqual(['foo:bar', 'length:2']);
});
it('should handle objects of custom types with length property as objects', function() {
function CustomType() {
this.length = 2;
this.foo = 'bar'
}
var obj = new CustomType(),
log = [];
forEach(obj, function(value, key) { log.push(key + ':' + value)});
expect(log).toEqual(['length:2', 'foo:bar']);
});
});
describe('sortedKeys', function() {
it('should collect keys from object', function() {
expect(sortedKeys({c:0, b:0, a:0})).toEqual(['a', 'b', 'c']);
});
});
describe('encodeUriSegment', function() {
it('should correctly encode uri segment and not encode chars defined as pchar set in rfc3986',
function() {
//don't encode alphanum
expect(encodeUriSegment('asdf1234asdf')).
toEqual('asdf1234asdf');
//don't encode unreserved'
expect(encodeUriSegment("-_.!~*'() -_.!~*'()")).
toEqual("-_.!~*'()%20-_.!~*'()");
//don't encode the rest of pchar'
expect(encodeUriSegment(':@&=+$, :@&=+$,')).
toEqual(':@&=+$,%20:@&=+$,');
//encode '/', ';' and ' ''
expect(encodeUriSegment('/; /;')).
toEqual('%2F%3B%20%2F%3B');
});
});
describe('encodeUriQuery', function() {
it('should correctly encode uri query and not encode chars defined as pchar set in rfc3986',
function() {
//don't encode alphanum
expect(encodeUriQuery('asdf1234asdf')).
toEqual('asdf1234asdf');
//don't encode unreserved
expect(encodeUriQuery("-_.!~*'() -_.!~*'()")).
toEqual("-_.!~*'()+-_.!~*'()");
//don't encode the rest of pchar
expect(encodeUriQuery(':@$, :@$,')).
toEqual(':@$,+:@$,');
//encode '&', ';', '=', '+', and '#'
expect(encodeUriQuery('&;=+# &;=+#')).
toEqual('%26%3B%3D%2B%23+%26%3B%3D%2B%23');
//encode ' ' as '+'
expect(encodeUriQuery(' ')).
toEqual('++');
//encode ' ' as '%20' when a flag is used
expect(encodeUriQuery(' ', true)).
toEqual('%20%20');
//do not encode `null` as '+' when flag is used
expect(encodeUriQuery('null', true)).
toEqual('null');
//do not encode `null` with no flag
expect(encodeUriQuery('null')).
toEqual('null');
});
});
describe('angularInit', function() {
var bootstrapSpy;
var element;
beforeEach(function() {
element = {
getElementById: function (id) {
return element.getElementById[id] || [];
},
querySelectorAll: function(arg) {
return element.querySelectorAll[arg] || [];
},
getAttribute: function(name) {
return element[name];
}
};
bootstrapSpy = jasmine.createSpy('bootstrapSpy');
});
it('should do nothing when not found', function() {
angularInit(element, bootstrapSpy);
expect(bootstrapSpy).not.toHaveBeenCalled();
});
it('should look for ngApp directive as attr', function() {
var appElement = jqLite('<div ng-app="ABC"></div>')[0];
element.querySelectorAll['[ng-app]'] = [appElement];
angularInit(element, bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnceWith(appElement, ['ABC']);
});
it('should look for ngApp directive in id', function() {
var appElement = jqLite('<div id="ng-app" data-ng-app="ABC"></div>')[0];
jqLite(document.body).append(appElement);
angularInit(element, bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnceWith(appElement, ['ABC']);
});
it('should look for ngApp directive in className', function() {
var appElement = jqLite('<div data-ng-app="ABC"></div>')[0];
element.querySelectorAll['.ng\\:app'] = [appElement];
angularInit(element, bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnceWith(appElement, ['ABC']);
});
it('should look for ngApp directive using querySelectorAll', function() {
var appElement = jqLite('<div x-ng-app="ABC"></div>')[0];
element.querySelectorAll['[ng\\:app]'] = [ appElement ];
angularInit(element, bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnceWith(appElement, ['ABC']);
});
it('should bootstrap using class name', function() {
var appElement = jqLite('<div class="ng-app: ABC;"></div>')[0];
angularInit(jqLite('<div></div>').append(appElement)[0], bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnceWith(appElement, ['ABC']);
});
it('should bootstrap anonymously', function() {
var appElement = jqLite('<div x-ng-app></div>')[0];
element.querySelectorAll['[x-ng-app]'] = [ appElement ];
angularInit(element, bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnceWith(appElement, []);
});
it('should bootstrap anonymously using class only', function() {
var appElement = jqLite('<div class="ng-app"></div>')[0];
angularInit(jqLite('<div></div>').append(appElement)[0], bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnceWith(appElement, []);
});
it('should bootstrap if the annotation is on the root element', function() {
var appElement = jqLite('<div class="ng-app"></div>')[0];
angularInit(appElement, bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnceWith(appElement, []);
});
it('should complain if app module cannot be found', function() {
var appElement = jqLite('<div ng-app="doesntexist"></div>')[0];
expect(function() {
angularInit(appElement, bootstrap);
}).toThrowMatching(
/\[\$injector:modulerr] Failed to instantiate module doesntexist due to:\n.*\[\$injector:nomod] Module 'doesntexist' is not available! You either misspelled the module name or forgot to load it\./
);
});
it('should complain if an element has already been bootstrapped', function () {
var element = jqLite('<div>bootstrap me!</div>');
angular.bootstrap(element);
expect(function () {
angular.bootstrap(element);
}).toThrowMatching(
/\[ng:btstrpd\] App Already Bootstrapped with this Element '<div class="?ng\-scope"?( ng\-[0-9]+="?[0-9]+"?)?>'/i
);
dealoc(element);
});
it('should complain if manually bootstrapping a document whose <html> element has already been bootstrapped', function () {
angular.bootstrap(document.getElementsByTagName('html')[0]);
expect(function () {
angular.bootstrap(document);
}).toThrowMatching(
/\[ng:btstrpd\] App Already Bootstrapped with this Element 'document'/i
);
dealoc(document);
})
});
describe('angular service', function() {
it('should override services', function() {
module(function($provide){
$provide.value('fake', 'old');
$provide.value('fake', 'new');
});
inject(function(fake) {
expect(fake).toEqual('new');
});
});
it('should inject dependencies specified by $inject and ignore function argument name', function() {
expect(angular.injector([function($provide){
$provide.factory('svc1', function() { return 'svc1'; });
$provide.factory('svc2', ['svc1', function(s) { return 'svc2-' + s; }]);
}]).get('svc2')).toEqual('svc2-svc1');
});
});
describe('isDate', function() {
it('should return true for Date object', function() {
expect(isDate(new Date())).toBe(true);
});
it('should return false for non Date objects', function() {
expect(isDate([])).toBe(false);
expect(isDate('')).toBe(false);
expect(isDate(23)).toBe(false);
expect(isDate({})).toBe(false);
});
});
describe('isRegExp', function() {
it('should return true for RegExp object', function() {
expect(isRegExp(/^foobar$/)).toBe(true);
expect(isRegExp(new RegExp('^foobar$/'))).toBe(true);
});
it('should return false for non RegExp objects', function() {
expect(isRegExp([])).toBe(false);
expect(isRegExp('')).toBe(false);
expect(isRegExp(23)).toBe(false);
expect(isRegExp({})).toBe(false);
expect(isRegExp(new Date())).toBe(false);
});
});
describe('compile', function() {
it('should link to existing node and create scope', inject(function($rootScope, $compile) {
var template = angular.element('<div>{{greeting = "hello world"}}</div>');
element = $compile(template)($rootScope);
$rootScope.$digest();
expect(template.text()).toEqual('hello world');
expect($rootScope.greeting).toEqual('hello world');
}));
it('should link to existing node and given scope', inject(function($rootScope, $compile) {
var template = angular.element('<div>{{greeting = "hello world"}}</div>');
element = $compile(template)($rootScope);
$rootScope.$digest();
expect(template.text()).toEqual('hello world');
}));
it('should link to new node and given scope', inject(function($rootScope, $compile) {
var template = jqLite('<div>{{greeting = "hello world"}}</div>');
var compile = $compile(template);
var templateClone = template.clone();
element = compile($rootScope, function(clone){
templateClone = clone;
});
$rootScope.$digest();
expect(template.text()).toEqual('{{greeting = "hello world"}}');
expect(element.text()).toEqual('hello world');
expect(element).toEqual(templateClone);
expect($rootScope.greeting).toEqual('hello world');
}));
it('should link to cloned node and create scope', inject(function($rootScope, $compile) {
var template = jqLite('<div>{{greeting = "hello world"}}</div>');
element = $compile(template)($rootScope, noop);
$rootScope.$digest();
expect(template.text()).toEqual('{{greeting = "hello world"}}');
expect(element.text()).toEqual('hello world');
expect($rootScope.greeting).toEqual('hello world');
}));
});
describe('nodeName_', function() {
it('should correctly detect node name with "namespace" when xmlns is defined', function() {
var div = jqLite('<div xmlns:ngtest="http://angularjs.org/">' +
'<ngtest:foo ngtest:attr="bar"></ngtest:foo>' +
'</div>')[0];
expect(nodeName_(div.childNodes[0])).toBe('NGTEST:FOO');
expect(div.childNodes[0].getAttribute('ngtest:attr')).toBe('bar');
});
if (!msie || msie >= 9) {
it('should correctly detect node name with "namespace" when xmlns is NOT defined', function() {
var div = jqLite('<div xmlns:ngtest="http://angularjs.org/">' +
'<ngtest:foo ngtest:attr="bar"></ng-test>' +
'</div>')[0];
expect(nodeName_(div.childNodes[0])).toBe('NGTEST:FOO');
expect(div.childNodes[0].getAttribute('ngtest:attr')).toBe('bar');
});
}
});
describe('nextUid()', function() {
it('should return new id per call', function() {
var seen = {};
var count = 100;
while(count--) {
var current = nextUid();
expect(current.match(/[\d\w]+/)).toBeTruthy();
expect(seen[current]).toBeFalsy();
seen[current] = true;
}
});
});
describe('version', function() {
it('version should have full/major/minor/dot/codeName properties', function() {
expect(version).toBeDefined();
expect(version.full).toBe('"NG_VERSION_FULL"');
expect(version.major).toBe("NG_VERSION_MAJOR");
expect(version.minor).toBe("NG_VERSION_MINOR");
expect(version.dot).toBe("NG_VERSION_DOT");
expect(version.codeName).toBe('"NG_VERSION_CODENAME"');
});
});
describe('bootstrap', function() {
it('should bootstrap app', function(){
var element = jqLite('<div>{{1+2}}</div>');
var injector = angular.bootstrap(element);
expect(injector).toBeDefined();
expect(element.injector()).toBe(injector);
dealoc(element);
});
it("should complain if app module can't be found", function() {
var element = jqLite('<div>{{1+2}}</div>');
expect(function() {
angular.bootstrap(element, ['doesntexist']);
}).toThrowMatching(
/\[\$injector:modulerr\] Failed to instantiate module doesntexist due to:\n.*\[\$injector:nomod\] Module 'doesntexist' is not available! You either misspelled the module name or forgot to load it\./);
expect(element.html()).toBe('{{1+2}}');
dealoc(element);
});
describe('deferred bootstrap', function() {
var originalName = window.name,
element;
beforeEach(function() {
window.name = '';
element = jqLite('<div>{{1+2}}</div>');
});
afterEach(function() {
dealoc(element);
window.name = originalName;
});
it('should wait for extra modules', function() {
window.name = 'NG_DEFER_BOOTSTRAP!';
angular.bootstrap(element);
expect(element.html()).toBe('{{1+2}}');
angular.resumeBootstrap();
expect(element.html()).toBe('3');
expect(window.name).toEqual('');
});
it('should load extra modules', function() {
element = jqLite('<div>{{1+2}}</div>');
window.name = 'NG_DEFER_BOOTSTRAP!';
var bootstrapping = jasmine.createSpy('bootstrapping');
angular.bootstrap(element, [bootstrapping]);
expect(bootstrapping).not.toHaveBeenCalled();
expect(element.injector()).toBeUndefined();
angular.module('addedModule', []).value('foo', 'bar');
angular.resumeBootstrap(['addedModule']);
expect(bootstrapping).toHaveBeenCalledOnce();
expect(element.injector().get('foo')).toEqual('bar');
});
it('should not defer bootstrap without window.name cue', function() {
angular.bootstrap(element, []);
angular.module('addedModule', []).value('foo', 'bar');
expect(function() {
element.injector().get('foo');
}).toThrowMinErr('$injector', 'unpr', 'Unknown provider: fooProvider <- foo');
expect(element.injector().get('$http')).toBeDefined();
});
it('should restore the original window.name after bootstrap', function() {
window.name = 'NG_DEFER_BOOTSTRAP!my custom name';
angular.bootstrap(element);
expect(element.html()).toBe('{{1+2}}');
angular.resumeBootstrap();
expect(element.html()).toBe('3');
expect(window.name).toEqual('my custom name');
});
});
});
describe('startingElementHtml', function(){
it('should show starting element tag only', function(){
expect(startingTag('<ng-abc x="2A"><div>text</div></ng-abc>')).
toBe('<ng-abc x="2A">');
});
});
describe('startingTag', function() {
it('should allow passing in Nodes instead of Elements', function() {
var txtNode = document.createTextNode('some text');
expect(startingTag(txtNode)).toBe('some text');
});
});
describe('snake_case', function(){
it('should convert to snake_case', function() {
expect(snake_case('ABC')).toEqual('a_b_c');
expect(snake_case('alanBobCharles')).toEqual('alan_bob_charles');
});
it('should allow seperator to be overridden', function() {
expect(snake_case('ABC', '&')).toEqual('a&b&c');
expect(snake_case('alanBobCharles', '&')).toEqual('alan&bob&charles');
});
});
describe('fromJson', function() {
it('should delegate to JSON.parse', function() {
var spy = spyOn(JSON, 'parse').andCallThrough();
expect(fromJson('{}')).toEqual({});
expect(spy).toHaveBeenCalled();
});
});
describe('toJson', function() {
it('should delegate to JSON.stringify', function() {
var spy = spyOn(JSON, 'stringify').andCallThrough();
expect(toJson({})).toEqual('{}');
expect(spy).toHaveBeenCalled();
});
it('should format objects pretty', function() {
expect(toJson({a: 1, b: 2}, true)).
toBeOneOf('{\n "a": 1,\n "b": 2\n}', '{\n "a":1,\n "b":2\n}');
expect(toJson({a: {b: 2}}, true)).
toBeOneOf('{\n "a": {\n "b": 2\n }\n}', '{\n "a":{\n "b":2\n }\n}');
});
it('should not serialize properties starting with $', function() {
expect(toJson({$few: 'v', $$some:'value'}, false)).toEqual('{}');
});
it('should not serialize $window object', function() {
expect(toJson(window)).toEqual('"$WINDOW"');
});
it('should not serialize $document object', function() {
expect(toJson(document)).toEqual('"$DOCUMENT"');
});
it('should not serialize scope instances', inject(function($rootScope) {
expect(toJson({key: $rootScope})).toEqual('{"key":"$SCOPE"}');
}));
it('should serialize undefined as undefined', function() {
expect(toJson(undefined)).toEqual(undefined);
});
});
describe('msie UA parsing', function() {
if (/ Trident\/.*; rv:/.test(window.navigator.userAgent)) {
it('should fail when the Trident and the rv versions disagree for IE11+', function() {
// When this test fails, we can think about whether we want to use the version from the
// Trident token in the UA string or stick with the version from rv: as we currently do.
// Refer https://github.com/angular/angular.js/pull/3758#issuecomment-23529245 for the
// discussion.
var UA = window.navigator.userAgent;
var tridentVersion = parseInt((/Trident\/(\d+)/.exec(UA) || [])[1], 10) + 4;
var rvVersion = parseInt((/Trident\/.*; rv:(\d+)/.exec(UA) || [])[1], 10);
expect(tridentVersion).toBe(rvVersion);
});
}
});
});