diff options
Diffstat (limited to 'test/ng/parseSpec.js')
| -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); -        })); -      });      });    });  }); | 
