diff options
Diffstat (limited to 'test/ng')
| -rw-r--r-- | test/ng/parseSpec.js | 363 |
1 files changed, 245 insertions, 118 deletions
diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index 19182332..277178a1 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -1,12 +1,20 @@ 'use strict'; describe('parser', function() { + + beforeEach(function() { + // clear caches + getterFnCache = {}; + promiseWarningCache = {}; + }); + + describe('lexer', function() { var lex; beforeEach(function () { lex = function () { - var lexer = new Lexer(); + var lexer = new Lexer({csp: false, unwrapPromises: false}); return lexer.lex.apply(lexer, arguments); }; }); @@ -198,7 +206,6 @@ describe('parser', function() { beforeEach(inject(function ($rootScope, $sniffer) { scope = $rootScope; $sniffer.csp = cspEnabled; - getterFnCache = {}; // clear cache })); @@ -854,15 +861,228 @@ describe('parser', function() { }); - describe('promises', function() { - var deferred, promise, q; + describe('assignable', function() { + it('should expose assignment function', inject(function($parse) { + var fn = $parse('a'); + expect(fn.assign).toBeTruthy(); + var scope = {}; + fn.assign(scope, 123); + expect(scope).toEqual({a:123}); + })); + }); + + + describe('locals', function() { + it('should expose local variables', inject(function($parse) { + expect($parse('a')({a: 0}, {a: 1})).toEqual(1); + expect($parse('add(a,b)')({b: 1, add: function(a, b) { return a + b; }}, {a: 2})).toEqual(3); + })); + + it('should expose traverse locals', inject(function($parse) { + expect($parse('a.b')({a: {b: 0}}, {a: {b:1}})).toEqual(1); + expect($parse('a.b')({a: null}, {a: {b:1}})).toEqual(1); + expect($parse('a.b')({a: {b: 0}}, {a: null})).toEqual(undefined); + })); + }); + + describe('literal', function() { + it('should mark scalar value expressions as literal', inject(function($parse) { + expect($parse('0').literal).toBe(true); + expect($parse('"hello"').literal).toBe(true); + expect($parse('true').literal).toBe(true); + expect($parse('false').literal).toBe(true); + expect($parse('null').literal).toBe(true); + expect($parse('undefined').literal).toBe(true); + })); + + it('should mark array expressions as literal', inject(function($parse) { + expect($parse('[]').literal).toBe(true); + expect($parse('[1, 2, 3]').literal).toBe(true); + expect($parse('[1, identifier]').literal).toBe(true); + })); + + it('should mark object expressions as literal', inject(function($parse) { + expect($parse('{}').literal).toBe(true); + expect($parse('{x: 1}').literal).toBe(true); + expect($parse('{foo: bar}').literal).toBe(true); + })); + + it('should not mark function calls or operator expressions as literal', inject(function($parse) { + expect($parse('1 + 1').literal).toBe(false); + expect($parse('call()').literal).toBe(false); + expect($parse('[].length').literal).toBe(false); + })); + }); + + describe('constant', function() { + it('should mark scalar value expressions as constant', inject(function($parse) { + expect($parse('12.3').constant).toBe(true); + expect($parse('"string"').constant).toBe(true); + expect($parse('true').constant).toBe(true); + expect($parse('false').constant).toBe(true); + expect($parse('null').constant).toBe(true); + expect($parse('undefined').constant).toBe(true); + })); + + it('should mark arrays as constant if they only contain constant elements', inject(function($parse) { + expect($parse('[]').constant).toBe(true); + expect($parse('[1, 2, 3]').constant).toBe(true); + expect($parse('["string", null]').constant).toBe(true); + expect($parse('[[]]').constant).toBe(true); + expect($parse('[1, [2, 3], {4: 5}]').constant).toBe(true); + })); + + it('should not mark arrays as constant if they contain any non-constant elements', inject(function($parse) { + expect($parse('[foo]').constant).toBe(false); + expect($parse('[x + 1]').constant).toBe(false); + expect($parse('[bar[0]]').constant).toBe(false); + })); + + it('should mark complex expressions involving constant values as constant', inject(function($parse) { + expect($parse('!true').constant).toBe(true); + expect($parse('1 - 1').constant).toBe(true); + expect($parse('"foo" + "bar"').constant).toBe(true); + expect($parse('5 != null').constant).toBe(true); + expect($parse('{standard: 4/3, wide: 16/9}').constant).toBe(true); + })); + + it('should not mark any expression involving variables or function calls as constant', inject(function($parse) { + expect($parse('true.toString()').constant).toBe(false); + expect($parse('foo(1, 2, 3)').constant).toBe(false); + expect($parse('"name" + id').constant).toBe(false); + })); + }); + }); + }); + + + describe('promises', function() { + + var deferred, promise, q; + + describe('unwrapPromises setting', function () { + + beforeEach(inject(function($rootScope, $q) { + scope = $rootScope; + + $rootScope.$apply(function() { + deferred = $q.defer(); + deferred.resolve('Bobo'); + promise = deferred.promise; + }); + })); + + it('should not unwrap promises by default', inject(function ($parse) { + scope.person = promise; + scope.things = {person: promise}; + scope.getPerson = function () { return promise; }; + + var getter = $parse('person'); + var propGetter = $parse('things.person'); + var fnGetter = $parse('getPerson()'); + + expect(getter(scope)).toBe(promise); + expect(propGetter(scope)).toBe(promise); + expect(fnGetter(scope)).toBe(promise); + })); + }); + + + forEach([true, false], function(cspEnabled) { + + describe('promise logging (csp:' + cspEnabled + ')', function() { + + var $log; + var PROMISE_WARNING_REGEXP = /\[\$parse\] Promise found in the expression `[^`]+`. Automatic unwrapping of promises in Angular expressions is deprecated\./; + + beforeEach(module(function($parseProvider) { + $parseProvider.unwrapPromises(true); + })); + + beforeEach(inject(function($rootScope, $q, _$log_) { + scope = $rootScope; + + $rootScope.$apply(function() { + deferred = $q.defer(); + deferred.resolve('Bobo'); + promise = deferred.promise; + }); + + $log = _$log_; + })); + + it('should log warnings by default', function() { + scope.person = promise; + scope.$eval('person'); + expect($log.warn.logs.pop()).toEqual(['[$parse] Promise found in the expression `person`. ' + + 'Automatic unwrapping of promises in Angular expressions is deprecated.']); + }); + + + it('should log warnings for deep promises', function() { + scope.car = {wheel: {disc: promise}}; + scope.$eval('car.wheel.disc.pad'); + expect($log.warn.logs.pop()).toMatch(PROMISE_WARNING_REGEXP); + }); + + + it('should log warnings for setters', function() { + scope.person = promise; + scope.$eval('person.name = "Bubu"'); + expect($log.warn.logs.pop()).toMatch(PROMISE_WARNING_REGEXP); + }); + + + it('should log only a single warning for each expression', function() { + scope.person1 = promise; + scope.person2 = promise; + + scope.$eval('person1'); + scope.$eval('person1'); + expect($log.warn.logs.pop()).toMatch(/`person1`/); + expect($log.warn.logs).toEqual([]); + + scope.$eval('person1'); + scope.$eval('person2'); + scope.$eval('person1'); + scope.$eval('person2'); + expect($log.warn.logs.pop()).toMatch(/`person2`/); + expect($log.warn.logs).toEqual([]); + }); + + + it('should log warning for complex expressions', function() { + scope.person1 = promise; + scope.person2 = promise; + + scope.$eval('person1 + person2'); + expect($log.warn.logs.pop()).toMatch(/`person1 \+ person2`/); + expect($log.warn.logs).toEqual([]); + }); + }); + }); + + + forEach([true, false], function(cspEnabled) { + + describe('csp ' + cspEnabled, function() { + + beforeEach(module(function($parseProvider) { + $parseProvider.unwrapPromises(true); + $parseProvider.logPromiseWarnings(false); + })); + + + beforeEach(inject(function($rootScope, $sniffer, $q) { + scope = $rootScope; + $sniffer.csp = cspEnabled; - beforeEach(inject(function($q) { q = $q; deferred = q.defer(); promise = deferred.promise; })); + describe('{{promise}}', function() { it('should evaluated resolved promise and get its value', function() { deferred.resolve('hello!'); @@ -876,7 +1096,7 @@ describe('parser', function() { it('should evaluated rejected promise and ignore the rejection reason', function() { deferred.reject('sorry'); scope.greeting = promise; - expect(scope.$eval('gretting')).toBe(undefined); + expect(scope.$eval('greeting')).toBe(undefined); scope.$digest(); expect(scope.$eval('greeting')).toBe(undefined); }); @@ -929,7 +1149,7 @@ describe('parser', function() { scope.person = promise; deferred.resolve({'name': 'Bill Gates'}); - var getter = $parse('person.name'); + var getter = $parse('person.name', { unwrapPromises: true }); expect(getter(scope)).toBe(undefined); scope.$digest(); @@ -943,7 +1163,7 @@ describe('parser', function() { scope.greeting = promise; deferred.resolve('Salut!'); - var getter = $parse('greeting'); + var getter = $parse('greeting', { unwrapPromises: true }); expect(getter(scope)).toBe(undefined); scope.$digest(); @@ -957,7 +1177,7 @@ describe('parser', function() { it('should evaluate an unresolved promise and set and remember its value', inject(function($parse) { scope.person = promise; - var getter = $parse('person.name'); + var getter = $parse('person.name', { unwrapPromises: true }); expect(getter(scope)).toBe(undefined); scope.$digest(); @@ -968,7 +1188,7 @@ describe('parser', function() { expect(getter(scope)).toBe('Bonjour'); - var c1Getter = $parse('person.A.B.C1'); + var c1Getter = $parse('person.A.B.C1', { unwrapPromises: true }); scope.$digest(); expect(c1Getter(scope)).toBe(undefined); c1Getter.assign(scope, 'c1_value'); @@ -976,7 +1196,7 @@ describe('parser', function() { expect(c1Getter(scope)).toBe('c1_value'); // Set another property on the person.A.B - var c2Getter = $parse('person.A.B.C2'); + var c2Getter = $parse('person.A.B.C2', { unwrapPromises: true }); scope.$digest(); expect(c2Getter(scope)).toBe(undefined); c2Getter.assign(scope, 'c2_value'); @@ -984,15 +1204,15 @@ describe('parser', function() { expect(c2Getter(scope)).toBe('c2_value'); // c1 should be unchanged. - expect($parse('person.A')(scope)).toEqual( + expect($parse('person.A', { unwrapPromises: true })(scope)).toEqual( {B: {C1: 'c1_value', C2: 'c2_value'}}); })); it('should evaluate a resolved promise and overwrite the previous set value in the absense of the getter', - inject(function($parse) { + inject(function($parse) { scope.person = promise; - var c1Getter = $parse('person.A.B.C1'); + var c1Getter = $parse('person.A.B.C1', { unwrapPromises: true }); c1Getter.assign(scope, 'c1_value'); // resolving the promise should update the tree. deferred.resolve({A: {B: {C1: 'resolved_c1'}}}); @@ -1037,19 +1257,19 @@ describe('parser', function() { it('should evaluate and dereference array references leading to and from a promise', function() { - scope.greetings = [promise]; - expect(scope.$eval('greetings[0]')).toBe(undefined); - expect(scope.$eval('greetings[0][0]')).toBe(undefined); + scope.greetings = [promise]; + expect(scope.$eval('greetings[0]')).toBe(undefined); + expect(scope.$eval('greetings[0][0]')).toBe(undefined); - scope.$digest(); - expect(scope.$eval('greetings[0]')).toBe(undefined); - expect(scope.$eval('greetings[0][0]')).toBe(undefined); + scope.$digest(); + expect(scope.$eval('greetings[0]')).toBe(undefined); + expect(scope.$eval('greetings[0][0]')).toBe(undefined); - deferred.resolve(['Hi!', 'Cau!']); - scope.$digest(); - expect(scope.$eval('greetings[0]')).toEqual(['Hi!', 'Cau!']); - expect(scope.$eval('greetings[0][0]')).toBe('Hi!'); - }); + deferred.resolve(['Hi!', 'Cau!']); + scope.$digest(); + expect(scope.$eval('greetings[0]')).toEqual(['Hi!', 'Cau!']); + expect(scope.$eval('greetings[0][0]')).toBe('Hi!'); + }); it('should evaluate and dereference promises used as function arguments', function() { @@ -1109,99 +1329,6 @@ describe('parser', function() { }); }); }); - - - describe('assignable', function() { - it('should expose assignment function', inject(function($parse) { - var fn = $parse('a'); - expect(fn.assign).toBeTruthy(); - var scope = {}; - fn.assign(scope, 123); - expect(scope).toEqual({a:123}); - })); - }); - - - describe('locals', function() { - it('should expose local variables', inject(function($parse) { - expect($parse('a')({a: 0}, {a: 1})).toEqual(1); - expect($parse('add(a,b)')({b: 1, add: function(a, b) { return a + b; }}, {a: 2})).toEqual(3); - })); - - it('should expose traverse locals', inject(function($parse) { - expect($parse('a.b')({a: {b: 0}}, {a: {b:1}})).toEqual(1); - expect($parse('a.b')({a: null}, {a: {b:1}})).toEqual(1); - expect($parse('a.b')({a: {b: 0}}, {a: null})).toEqual(undefined); - })); - }); - - describe('literal', function() { - it('should mark scalar value expressions as literal', inject(function($parse) { - expect($parse('0').literal).toBe(true); - expect($parse('"hello"').literal).toBe(true); - expect($parse('true').literal).toBe(true); - expect($parse('false').literal).toBe(true); - expect($parse('null').literal).toBe(true); - expect($parse('undefined').literal).toBe(true); - })); - - it('should mark array expressions as literal', inject(function($parse) { - expect($parse('[]').literal).toBe(true); - expect($parse('[1, 2, 3]').literal).toBe(true); - expect($parse('[1, identifier]').literal).toBe(true); - })); - - it('should mark object expressions as literal', inject(function($parse) { - expect($parse('{}').literal).toBe(true); - expect($parse('{x: 1}').literal).toBe(true); - expect($parse('{foo: bar}').literal).toBe(true); - })); - - it('should not mark function calls or operator expressions as literal', inject(function($parse) { - expect($parse('1 + 1').literal).toBe(false); - expect($parse('call()').literal).toBe(false); - expect($parse('[].length').literal).toBe(false); - })); - }); - - describe('constant', function() { - it('should mark scalar value expressions as constant', inject(function($parse) { - expect($parse('12.3').constant).toBe(true); - expect($parse('"string"').constant).toBe(true); - expect($parse('true').constant).toBe(true); - expect($parse('false').constant).toBe(true); - expect($parse('null').constant).toBe(true); - expect($parse('undefined').constant).toBe(true); - })); - - it('should mark arrays as constant if they only contain constant elements', inject(function($parse) { - expect($parse('[]').constant).toBe(true); - expect($parse('[1, 2, 3]').constant).toBe(true); - expect($parse('["string", null]').constant).toBe(true); - expect($parse('[[]]').constant).toBe(true); - expect($parse('[1, [2, 3], {4: 5}]').constant).toBe(true); - })); - - it('should not mark arrays as constant if they contain any non-constant elements', inject(function($parse) { - expect($parse('[foo]').constant).toBe(false); - expect($parse('[x + 1]').constant).toBe(false); - expect($parse('[bar[0]]').constant).toBe(false); - })); - - it('should mark complex expressions involving constant values as constant', inject(function($parse) { - expect($parse('!true').constant).toBe(true); - expect($parse('1 - 1').constant).toBe(true); - expect($parse('"foo" + "bar"').constant).toBe(true); - expect($parse('5 != null').constant).toBe(true); - expect($parse('{standard: 4/3, wide: 16/9}').constant).toBe(true); - })); - - it('should not mark any expression involving variables or function calls as constant', inject(function($parse) { - expect($parse('true.toString()').constant).toBe(false); - expect($parse('foo(1, 2, 3)').constant).toBe(false); - expect($parse('"name" + id').constant).toBe(false); - })); - }); }); }); }); |
