aboutsummaryrefslogtreecommitdiffstats
path: root/test/ng/parseSpec.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/ng/parseSpec.js')
-rw-r--r--test/ng/parseSpec.js363
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);
- }));
- });
});
});
});