PageRenderTime 50ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/test/ng/parseSpec.js

https://github.com/IngageStroliaC/angular.js
JavaScript | 1397 lines | 1377 code | 19 blank | 1 comment | 5 complexity | de7023a75bf86c7117935f256c6f8070 MD5 | raw file
Possible License(s): JSON

Large files files are truncated, but you can click here to view the full file

  1. 'use strict';
  2. describe('parser', function() {
  3. beforeEach(function() {
  4. // clear caches
  5. getterFnCache = {};
  6. promiseWarningCache = {};
  7. });
  8. describe('lexer', function() {
  9. var lex;
  10. beforeEach(function () {
  11. lex = function () {
  12. var lexer = new Lexer({csp: false, unwrapPromises: false});
  13. return lexer.lex.apply(lexer, arguments);
  14. };
  15. });
  16. it('should tokenize a string', function() {
  17. var tokens = lex("a.bc[22]+1.3|f:'a\\\'c':\"d\\\"e\"");
  18. var i = 0;
  19. expect(tokens[i].index).toEqual(0);
  20. expect(tokens[i].text).toEqual('a.bc');
  21. i++;
  22. expect(tokens[i].index).toEqual(4);
  23. expect(tokens[i].text).toEqual('[');
  24. i++;
  25. expect(tokens[i].index).toEqual(5);
  26. expect(tokens[i].text).toEqual(22);
  27. i++;
  28. expect(tokens[i].index).toEqual(7);
  29. expect(tokens[i].text).toEqual(']');
  30. i++;
  31. expect(tokens[i].index).toEqual(8);
  32. expect(tokens[i].text).toEqual('+');
  33. i++;
  34. expect(tokens[i].index).toEqual(9);
  35. expect(tokens[i].text).toEqual(1.3);
  36. i++;
  37. expect(tokens[i].index).toEqual(12);
  38. expect(tokens[i].text).toEqual('|');
  39. i++;
  40. expect(tokens[i].index).toEqual(13);
  41. expect(tokens[i].text).toEqual('f');
  42. i++;
  43. expect(tokens[i].index).toEqual(14);
  44. expect(tokens[i].text).toEqual(':');
  45. i++;
  46. expect(tokens[i].index).toEqual(15);
  47. expect(tokens[i].string).toEqual("a'c");
  48. i++;
  49. expect(tokens[i].index).toEqual(21);
  50. expect(tokens[i].text).toEqual(':');
  51. i++;
  52. expect(tokens[i].index).toEqual(22);
  53. expect(tokens[i].string).toEqual('d"e');
  54. });
  55. it('should tokenize undefined', function() {
  56. var tokens = lex("undefined");
  57. var i = 0;
  58. expect(tokens[i].index).toEqual(0);
  59. expect(tokens[i].text).toEqual('undefined');
  60. expect(undefined).toEqual(tokens[i].fn());
  61. });
  62. it('should tokenize quoted string', function() {
  63. var str = "['\\'', \"\\\"\"]";
  64. var tokens = lex(str);
  65. expect(tokens[1].index).toEqual(1);
  66. expect(tokens[1].string).toEqual("'");
  67. expect(tokens[3].index).toEqual(7);
  68. expect(tokens[3].string).toEqual('"');
  69. });
  70. it('should tokenize escaped quoted string', function() {
  71. var str = '"\\"\\n\\f\\r\\t\\v\\u00A0"';
  72. var tokens = lex(str);
  73. expect(tokens[0].string).toEqual('"\n\f\r\t\v\u00A0');
  74. });
  75. it('should tokenize unicode', function() {
  76. var tokens = lex('"\\u00A0"');
  77. expect(tokens.length).toEqual(1);
  78. expect(tokens[0].string).toEqual('\u00a0');
  79. });
  80. it('should ignore whitespace', function() {
  81. var tokens = lex("a \t \n \r b");
  82. expect(tokens[0].text).toEqual('a');
  83. expect(tokens[1].text).toEqual('b');
  84. });
  85. it('should tokenize relation and equality', function() {
  86. var tokens = lex("! == != < > <= >= === !==");
  87. expect(tokens[0].text).toEqual('!');
  88. expect(tokens[1].text).toEqual('==');
  89. expect(tokens[2].text).toEqual('!=');
  90. expect(tokens[3].text).toEqual('<');
  91. expect(tokens[4].text).toEqual('>');
  92. expect(tokens[5].text).toEqual('<=');
  93. expect(tokens[6].text).toEqual('>=');
  94. expect(tokens[7].text).toEqual('===');
  95. expect(tokens[8].text).toEqual('!==');
  96. });
  97. it('should tokenize logical and ternary', function() {
  98. var tokens = lex("&& || ? :");
  99. expect(tokens[0].text).toEqual('&&');
  100. expect(tokens[1].text).toEqual('||');
  101. expect(tokens[2].text).toEqual('?');
  102. expect(tokens[3].text).toEqual(':');
  103. });
  104. it('should tokenize statements', function() {
  105. var tokens = lex("a;b;");
  106. expect(tokens[0].text).toEqual('a');
  107. expect(tokens[1].text).toEqual(';');
  108. expect(tokens[2].text).toEqual('b');
  109. expect(tokens[3].text).toEqual(';');
  110. });
  111. it('should tokenize function invocation', function() {
  112. var tokens = lex("a()");
  113. expect(map(tokens, function(t) { return t.text;})).toEqual(['a', '(', ')']);
  114. });
  115. it('should tokenize method invocation', function() {
  116. var tokens = lex("a.b.c (d) - e.f()");
  117. expect(map(tokens, function(t) { return t.text;})).
  118. toEqual(['a.b', '.', 'c', '(', 'd', ')', '-', 'e', '.', 'f', '(', ')']);
  119. });
  120. it('should tokenize number', function() {
  121. var tokens = lex("0.5");
  122. expect(tokens[0].text).toEqual(0.5);
  123. });
  124. it('should tokenize negative number', inject(function($rootScope) {
  125. var value = $rootScope.$eval("-0.5");
  126. expect(value).toEqual(-0.5);
  127. value = $rootScope.$eval("{a:-0.5}");
  128. expect(value).toEqual({a:-0.5});
  129. }));
  130. it('should tokenize number with exponent', inject(function($rootScope) {
  131. var tokens = lex("0.5E-10");
  132. expect(tokens[0].text).toEqual(0.5E-10);
  133. expect($rootScope.$eval("0.5E-10")).toEqual(0.5E-10);
  134. tokens = lex("0.5E+10");
  135. expect(tokens[0].text).toEqual(0.5E+10);
  136. }));
  137. it('should throws exception for invalid exponent', function() {
  138. expect(function() {
  139. lex("0.5E-");
  140. }).toThrowMinErr('$parse', 'lexerr', 'Lexer Error: Invalid exponent at column 4 in expression [0.5E-].');
  141. expect(function() {
  142. lex("0.5E-A");
  143. }).toThrowMinErr('$parse', 'lexerr', 'Lexer Error: Invalid exponent at column 4 in expression [0.5E-A].');
  144. });
  145. it('should tokenize number starting with a dot', function() {
  146. var tokens = lex(".5");
  147. expect(tokens[0].text).toEqual(0.5);
  148. });
  149. it('should throw error on invalid unicode', function() {
  150. expect(function() {
  151. lex("'\\u1''bla'");
  152. }).toThrowMinErr("$parse", "lexerr", "Lexer Error: Invalid unicode escape [\\u1''b] at column 2 in expression ['\\u1''bla'].");
  153. });
  154. });
  155. var $filterProvider, scope;
  156. beforeEach(module(['$filterProvider', function (filterProvider) {
  157. $filterProvider = filterProvider;
  158. }]));
  159. forEach([true, false], function(cspEnabled) {
  160. forEach([true, false], function(unwrapPromisesEnabled) {
  161. describe('csp: ' + cspEnabled + ", unwrapPromises: " + unwrapPromisesEnabled, function() {
  162. beforeEach(module(function ($parseProvider) {
  163. $parseProvider.unwrapPromises(unwrapPromisesEnabled);
  164. }));
  165. beforeEach(inject(function ($rootScope, $sniffer) {
  166. scope = $rootScope;
  167. $sniffer.csp = cspEnabled;
  168. }));
  169. it('should parse expressions', function() {
  170. expect(scope.$eval("-1")).toEqual(-1);
  171. expect(scope.$eval("1 + 2.5")).toEqual(3.5);
  172. expect(scope.$eval("1 + -2.5")).toEqual(-1.5);
  173. expect(scope.$eval("1+2*3/4")).toEqual(1+2*3/4);
  174. expect(scope.$eval("0--1+1.5")).toEqual(0- -1 + 1.5);
  175. expect(scope.$eval("-0--1++2*-3/-4")).toEqual(-0- -1+ +2*-3/-4);
  176. expect(scope.$eval("1/2*3")).toEqual(1/2*3);
  177. });
  178. it('should parse comparison', function() {
  179. expect(scope.$eval("false")).toBeFalsy();
  180. expect(scope.$eval("!true")).toBeFalsy();
  181. expect(scope.$eval("1==1")).toBeTruthy();
  182. expect(scope.$eval("1==true")).toBeTruthy();
  183. expect(scope.$eval("1===1")).toBeTruthy();
  184. expect(scope.$eval("1==='1'")).toBeFalsy();
  185. expect(scope.$eval("1===true")).toBeFalsy();
  186. expect(scope.$eval("'true'===true")).toBeFalsy();
  187. expect(scope.$eval("1!==2")).toBeTruthy();
  188. expect(scope.$eval("1!=='1'")).toBeTruthy();
  189. expect(scope.$eval("1!=2")).toBeTruthy();
  190. expect(scope.$eval("1<2")).toBeTruthy();
  191. expect(scope.$eval("1<=1")).toBeTruthy();
  192. expect(scope.$eval("1>2")).toEqual(1>2);
  193. expect(scope.$eval("2>=1")).toEqual(2>=1);
  194. expect(scope.$eval("true==2<3")).toEqual(true == 2<3);
  195. expect(scope.$eval("true===2<3")).toEqual(true === 2<3);
  196. });
  197. it('should parse logical', function() {
  198. expect(scope.$eval("0&&2")).toEqual(0&&2);
  199. expect(scope.$eval("0||2")).toEqual(0||2);
  200. expect(scope.$eval("0||1&&2")).toEqual(0||1&&2);
  201. });
  202. it('should parse ternary', function(){
  203. var returnTrue = scope.returnTrue = function(){ return true; };
  204. var returnFalse = scope.returnFalse = function(){ return false; };
  205. var returnString = scope.returnString = function(){ return 'asd'; };
  206. var returnInt = scope.returnInt = function(){ return 123; };
  207. var identity = scope.identity = function(x){ return x; };
  208. // Simple.
  209. expect(scope.$eval('0?0:2')).toEqual(0?0:2);
  210. expect(scope.$eval('1?0:2')).toEqual(1?0:2);
  211. // Nested on the left.
  212. expect(scope.$eval('0?0?0:0:2')).toEqual(0?0?0:0:2);
  213. expect(scope.$eval('1?0?0:0:2')).toEqual(1?0?0:0:2);
  214. expect(scope.$eval('0?1?0:0:2')).toEqual(0?1?0:0:2);
  215. expect(scope.$eval('0?0?1:0:2')).toEqual(0?0?1:0:2);
  216. expect(scope.$eval('0?0?0:2:3')).toEqual(0?0?0:2:3);
  217. expect(scope.$eval('1?1?0:0:2')).toEqual(1?1?0:0:2);
  218. expect(scope.$eval('1?1?1:0:2')).toEqual(1?1?1:0:2);
  219. expect(scope.$eval('1?1?1:2:3')).toEqual(1?1?1:2:3);
  220. expect(scope.$eval('1?1?1:2:3')).toEqual(1?1?1:2:3);
  221. // Nested on the right.
  222. expect(scope.$eval('0?0:0?0:2')).toEqual(0?0:0?0:2);
  223. expect(scope.$eval('1?0:0?0:2')).toEqual(1?0:0?0:2);
  224. expect(scope.$eval('0?1:0?0:2')).toEqual(0?1:0?0:2);
  225. expect(scope.$eval('0?0:1?0:2')).toEqual(0?0:1?0:2);
  226. expect(scope.$eval('0?0:0?2:3')).toEqual(0?0:0?2:3);
  227. expect(scope.$eval('1?1:0?0:2')).toEqual(1?1:0?0:2);
  228. expect(scope.$eval('1?1:1?0:2')).toEqual(1?1:1?0:2);
  229. expect(scope.$eval('1?1:1?2:3')).toEqual(1?1:1?2:3);
  230. expect(scope.$eval('1?1:1?2:3')).toEqual(1?1:1?2:3);
  231. // Precedence with respect to logical operators.
  232. expect(scope.$eval('0&&1?0:1')).toEqual(0&&1?0:1);
  233. expect(scope.$eval('1||0?0:0')).toEqual(1||0?0:0);
  234. expect(scope.$eval('0?0&&1:2')).toEqual(0?0&&1:2);
  235. expect(scope.$eval('0?1&&1:2')).toEqual(0?1&&1:2);
  236. expect(scope.$eval('0?0||0:1')).toEqual(0?0||0:1);
  237. expect(scope.$eval('0?0||1:2')).toEqual(0?0||1:2);
  238. expect(scope.$eval('1?0&&1:2')).toEqual(1?0&&1:2);
  239. expect(scope.$eval('1?1&&1:2')).toEqual(1?1&&1:2);
  240. expect(scope.$eval('1?0||0:1')).toEqual(1?0||0:1);
  241. expect(scope.$eval('1?0||1:2')).toEqual(1?0||1:2);
  242. expect(scope.$eval('0?1:0&&1')).toEqual(0?1:0&&1);
  243. expect(scope.$eval('0?2:1&&1')).toEqual(0?2:1&&1);
  244. expect(scope.$eval('0?1:0||0')).toEqual(0?1:0||0);
  245. expect(scope.$eval('0?2:0||1')).toEqual(0?2:0||1);
  246. expect(scope.$eval('1?1:0&&1')).toEqual(1?1:0&&1);
  247. expect(scope.$eval('1?2:1&&1')).toEqual(1?2:1&&1);
  248. expect(scope.$eval('1?1:0||0')).toEqual(1?1:0||0);
  249. expect(scope.$eval('1?2:0||1')).toEqual(1?2:0||1);
  250. // Function calls.
  251. expect(scope.$eval('returnTrue() ? returnString() : returnInt()')).toEqual(returnTrue() ? returnString() : returnInt());
  252. expect(scope.$eval('returnFalse() ? returnString() : returnInt()')).toEqual(returnFalse() ? returnString() : returnInt());
  253. expect(scope.$eval('returnTrue() ? returnString() : returnInt()')).toEqual(returnTrue() ? returnString() : returnInt());
  254. expect(scope.$eval('identity(returnFalse() ? returnString() : returnInt())')).toEqual(identity(returnFalse() ? returnString() : returnInt()));
  255. });
  256. it('should parse string', function() {
  257. expect(scope.$eval("'a' + 'b c'")).toEqual("ab c");
  258. });
  259. it('should parse filters', function() {
  260. $filterProvider.register('substring', valueFn(function(input, start, end) {
  261. return input.substring(start, end);
  262. }));
  263. expect(function() {
  264. scope.$eval("1|nonexistent");
  265. }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: nonexistentFilterProvider <- nonexistentFilter');
  266. scope.offset = 3;
  267. expect(scope.$eval("'abcd'|substring:1:offset")).toEqual("bc");
  268. expect(scope.$eval("'abcd'|substring:1:3|uppercase")).toEqual("BC");
  269. });
  270. it('should access scope', function() {
  271. scope.a = 123;
  272. scope.b = {c: 456};
  273. expect(scope.$eval("a", scope)).toEqual(123);
  274. expect(scope.$eval("b.c", scope)).toEqual(456);
  275. expect(scope.$eval("x.y.z", scope)).not.toBeDefined();
  276. });
  277. it('should resolve deeply nested paths (important for CSP mode)', function() {
  278. scope.a = {b: {c: {d: {e: {f: {g: {h: {i: {j: {k: {l: {m: {n: 'nooo!'}}}}}}}}}}}}};
  279. expect(scope.$eval("a.b.c.d.e.f.g.h.i.j.k.l.m.n", scope)).toBe('nooo!');
  280. });
  281. it('should be forgiving', function() {
  282. scope.a = {b: 23};
  283. expect(scope.$eval('b')).toBeUndefined();
  284. expect(scope.$eval('a.x')).toBeUndefined();
  285. expect(scope.$eval('a.b.c.d')).toBeUndefined();
  286. });
  287. it('should support property names that collide with native object properties', function() {
  288. // regression
  289. scope.watch = 1;
  290. scope.toString = function toString() {
  291. return "custom toString";
  292. };
  293. expect(scope.$eval('watch', scope)).toBe(1);
  294. expect(scope.$eval('toString()', scope)).toBe('custom toString');
  295. });
  296. it('should not break if hasOwnProperty is referenced in an expression', function() {
  297. scope.obj = { value: 1};
  298. // By evaluating an expression that calls hasOwnProperty, the getterFnCache
  299. // will store a property called hasOwnProperty. This is effectively:
  300. // getterFnCache['hasOwnProperty'] = null
  301. scope.$eval('obj.hasOwnProperty("value")');
  302. // If we rely on this property then evaluating any expression will fail
  303. // because it is not able to find out if obj.value is there in the cache
  304. expect(scope.$eval('obj.value')).toBe(1);
  305. });
  306. it('should not break if the expression is "hasOwnProperty"', function() {
  307. scope.fooExp = 'barVal';
  308. // By evaluating hasOwnProperty, the $parse cache will store a getter for
  309. // the scope's own hasOwnProperty function, which will mess up future cache look ups.
  310. // i.e. cache['hasOwnProperty'] = function(scope) { return scope.hasOwnProperty; }
  311. scope.$eval('hasOwnProperty');
  312. expect(scope.$eval('fooExp')).toBe('barVal');
  313. });
  314. it('should evaluate grouped expressions', function() {
  315. expect(scope.$eval("(1+2)*3")).toEqual((1+2)*3);
  316. });
  317. it('should evaluate assignments', function() {
  318. expect(scope.$eval("a=12")).toEqual(12);
  319. expect(scope.a).toEqual(12);
  320. expect(scope.$eval("x.y.z=123;")).toEqual(123);
  321. expect(scope.x.y.z).toEqual(123);
  322. expect(scope.$eval("a=123; b=234")).toEqual(234);
  323. expect(scope.a).toEqual(123);
  324. expect(scope.b).toEqual(234);
  325. });
  326. it('should evaluate function call without arguments', function() {
  327. scope['const'] = function(a,b){return 123;};
  328. expect(scope.$eval("const()")).toEqual(123);
  329. });
  330. it('should evaluate function call with arguments', function() {
  331. scope.add = function(a,b) {
  332. return a+b;
  333. };
  334. expect(scope.$eval("add(1,2)")).toEqual(3);
  335. });
  336. it('should evaluate function call from a return value', function() {
  337. scope.val = 33;
  338. scope.getter = function() { return function() { return this.val; }};
  339. expect(scope.$eval("getter()()")).toBe(33);
  340. });
  341. it('should evaluate multiplication and division', function() {
  342. scope.taxRate = 8;
  343. scope.subTotal = 100;
  344. expect(scope.$eval("taxRate / 100 * subTotal")).toEqual(8);
  345. expect(scope.$eval("subTotal * taxRate / 100")).toEqual(8);
  346. });
  347. it('should evaluate array', function() {
  348. expect(scope.$eval("[]").length).toEqual(0);
  349. expect(scope.$eval("[1, 2]").length).toEqual(2);
  350. expect(scope.$eval("[1, 2]")[0]).toEqual(1);
  351. expect(scope.$eval("[1, 2]")[1]).toEqual(2);
  352. });
  353. it('should evaluate array access', function() {
  354. expect(scope.$eval("[1][0]")).toEqual(1);
  355. expect(scope.$eval("[[1]][0][0]")).toEqual(1);
  356. expect(scope.$eval("[].length")).toEqual(0);
  357. expect(scope.$eval("[1, 2].length")).toEqual(2);
  358. });
  359. it('should evaluate object', function() {
  360. expect(toJson(scope.$eval("{}"))).toEqual("{}");
  361. expect(toJson(scope.$eval("{a:'b'}"))).toEqual('{"a":"b"}');
  362. expect(toJson(scope.$eval("{'a':'b'}"))).toEqual('{"a":"b"}');
  363. expect(toJson(scope.$eval("{\"a\":'b'}"))).toEqual('{"a":"b"}');
  364. });
  365. it('should evaluate object access', function() {
  366. expect(scope.$eval("{false:'WC', true:'CC'}[false]")).toEqual("WC");
  367. });
  368. it('should evaluate JSON', function() {
  369. expect(toJson(scope.$eval("[{}]"))).toEqual("[{}]");
  370. expect(toJson(scope.$eval("[{a:[]}, {b:1}]"))).toEqual('[{"a":[]},{"b":1}]');
  371. });
  372. it('should evaluate multiple statements', function() {
  373. expect(scope.$eval("a=1;b=3;a+b")).toEqual(4);
  374. expect(scope.$eval(";;1;;")).toEqual(1);
  375. });
  376. it('should evaluate object methods in correct context (this)', function() {
  377. var C = function () {
  378. this.a = 123;
  379. };
  380. C.prototype.getA = function() {
  381. return this.a;
  382. };
  383. scope.obj = new C();
  384. expect(scope.$eval("obj.getA()")).toEqual(123);
  385. expect(scope.$eval("obj['getA']()")).toEqual(123);
  386. });
  387. it('should evaluate methods in correct context (this) in argument', function() {
  388. var C = function () {
  389. this.a = 123;
  390. };
  391. C.prototype.sum = function(value) {
  392. return this.a + value;
  393. };
  394. C.prototype.getA = function() {
  395. return this.a;
  396. };
  397. scope.obj = new C();
  398. expect(scope.$eval("obj.sum(obj.getA())")).toEqual(246);
  399. expect(scope.$eval("obj['sum'](obj.getA())")).toEqual(246);
  400. });
  401. it('should evaluate objects on scope context', function() {
  402. scope.a = "abc";
  403. expect(scope.$eval("{a:a}").a).toEqual("abc");
  404. });
  405. it('should evaluate field access on function call result', function() {
  406. scope.a = function() {
  407. return {name:'misko'};
  408. };
  409. expect(scope.$eval("a().name")).toEqual("misko");
  410. });
  411. it('should evaluate field access after array access', function () {
  412. scope.items = [{}, {name:'misko'}];
  413. expect(scope.$eval('items[1].name')).toEqual("misko");
  414. });
  415. it('should evaluate array assignment', function() {
  416. scope.items = [];
  417. expect(scope.$eval('items[1] = "abc"')).toEqual("abc");
  418. expect(scope.$eval('items[1]')).toEqual("abc");
  419. // Dont know how to make this work....
  420. // expect(scope.$eval('books[1] = "moby"')).toEqual("moby");
  421. // expect(scope.$eval('books[1]')).toEqual("moby");
  422. });
  423. it('should evaluate grouped filters', function() {
  424. scope.name = 'MISKO';
  425. expect(scope.$eval('n = (name|lowercase)')).toEqual('misko');
  426. expect(scope.$eval('n')).toEqual('misko');
  427. });
  428. it('should evaluate remainder', function() {
  429. expect(scope.$eval('1%2')).toEqual(1);
  430. });
  431. it('should evaluate sum with undefined', function() {
  432. expect(scope.$eval('1+undefined')).toEqual(1);
  433. expect(scope.$eval('undefined+1')).toEqual(1);
  434. });
  435. it('should throw exception on non-closed bracket', function() {
  436. expect(function() {
  437. scope.$eval('[].count(');
  438. }).toThrowMinErr('$parse', 'ueoe', 'Unexpected end of expression: [].count(');
  439. });
  440. it('should evaluate double negation', function() {
  441. expect(scope.$eval('true')).toBeTruthy();
  442. expect(scope.$eval('!true')).toBeFalsy();
  443. expect(scope.$eval('!!true')).toBeTruthy();
  444. expect(scope.$eval('{true:"a", false:"b"}[!!true]')).toEqual('a');
  445. });
  446. it('should evaluate negation', function() {
  447. expect(scope.$eval("!false || true")).toEqual(!false || true);
  448. expect(scope.$eval("!11 == 10")).toEqual(!11 == 10);
  449. expect(scope.$eval("12/6/2")).toEqual(12/6/2);
  450. });
  451. it('should evaluate exclamation mark', function() {
  452. expect(scope.$eval('suffix = "!"')).toEqual('!');
  453. });
  454. it('should evaluate minus', function() {
  455. expect(scope.$eval("{a:'-'}")).toEqual({a: "-"});
  456. });
  457. it('should evaluate undefined', function() {
  458. expect(scope.$eval("undefined")).not.toBeDefined();
  459. expect(scope.$eval("a=undefined")).not.toBeDefined();
  460. expect(scope.a).not.toBeDefined();
  461. });
  462. it('should allow assignment after array dereference', function() {
  463. scope.obj = [{}];
  464. scope.$eval('obj[0].name=1');
  465. expect(scope.obj.name).toBeUndefined();
  466. expect(scope.obj[0].name).toEqual(1);
  467. });
  468. it('should short-circuit AND operator', function() {
  469. scope.run = function() {
  470. throw "IT SHOULD NOT HAVE RUN";
  471. };
  472. expect(scope.$eval('false && run()')).toBe(false);
  473. });
  474. it('should short-circuit OR operator', function() {
  475. scope.run = function() {
  476. throw "IT SHOULD NOT HAVE RUN";
  477. };
  478. expect(scope.$eval('true || run()')).toBe(true);
  479. });
  480. it('should support method calls on primitive types', function() {
  481. scope.empty = '';
  482. scope.zero = 0;
  483. scope.bool = false;
  484. expect(scope.$eval('empty.substr(0)')).toBe('');
  485. expect(scope.$eval('zero.toString()')).toBe('0');
  486. expect(scope.$eval('bool.toString()')).toBe('false');
  487. });
  488. it('should evaluate expressions with line terminators', function() {
  489. scope.a = "a";
  490. scope.b = {c: "bc"};
  491. expect(scope.$eval('a + \n b.c + \r "\td" + \t \r\n\r "\r\n\n"')).toEqual("abc\td\r\n\n");
  492. });
  493. describe('sandboxing', function() {
  494. describe('Function constructor', function() {
  495. it('should NOT allow access to Function constructor in getter', function() {
  496. expect(function() {
  497. scope.$eval('{}.toString.constructor');
  498. }).toThrowMinErr(
  499. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  500. 'Expression: {}.toString.constructor');
  501. expect(function() {
  502. scope.$eval('{}.toString.constructor("alert(1)")');
  503. }).toThrowMinErr(
  504. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  505. 'Expression: {}.toString.constructor("alert(1)")');
  506. expect(function() {
  507. scope.$eval('[].toString.constructor.foo');
  508. }).toThrowMinErr(
  509. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  510. 'Expression: [].toString.constructor.foo');
  511. expect(function() {
  512. scope.$eval('{}.toString["constructor"]');
  513. }).toThrowMinErr(
  514. '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
  515. 'Expression: {}.toString["constructor"]');
  516. expect(function() {
  517. scope.$eval('{}["toString"]["constructor"]');
  518. }).toThrowMinErr(
  519. '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
  520. 'Expression: {}["toString"]["constructor"]');
  521. scope.a = [];
  522. expect(function() {
  523. scope.$eval('a.toString.constructor', scope);
  524. }).toThrowMinErr(
  525. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  526. 'Expression: a.toString.constructor');
  527. expect(function() {
  528. scope.$eval('a.toString["constructor"]', scope);
  529. }).toThrowMinErr(
  530. '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
  531. 'Expression: a.toString["constructor"]');
  532. });
  533. it('should NOT allow access to Function constructor in setter', function() {
  534. expect(function() {
  535. scope.$eval('{}.toString.constructor = 1');
  536. }).toThrowMinErr(
  537. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  538. 'Expression: {}.toString.constructor = 1');
  539. expect(function() {
  540. scope.$eval('{}.toString.constructor.a = 1');
  541. }).toThrowMinErr(
  542. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  543. 'Expression: {}.toString.constructor.a = 1');
  544. expect(function() {
  545. scope.$eval('{}.toString["constructor"]["constructor"] = 1');
  546. }).toThrowMinErr(
  547. '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
  548. 'Expression: {}.toString["constructor"]["constructor"] = 1');
  549. scope.key1 = "const";
  550. scope.key2 = "ructor";
  551. expect(function() {
  552. scope.$eval('{}.toString[key1 + key2].foo = 1');
  553. }).toThrowMinErr(
  554. '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
  555. 'Expression: {}.toString[key1 + key2].foo = 1');
  556. expect(function() {
  557. scope.$eval('{}.toString["constructor"]["a"] = 1');
  558. }).toThrowMinErr(
  559. '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
  560. 'Expression: {}.toString["constructor"]["a"] = 1');
  561. scope.a = [];
  562. expect(function() {
  563. scope.$eval('a.toString.constructor = 1', scope);
  564. }).toThrowMinErr(
  565. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  566. 'Expression: a.toString.constructor = 1');
  567. });
  568. it('should NOT allow access to Function constructor that has been aliased', function() {
  569. scope.foo = { "bar": Function };
  570. expect(function() {
  571. scope.$eval('foo["bar"]');
  572. }).toThrowMinErr(
  573. '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
  574. 'Expression: foo["bar"]');
  575. });
  576. it('should NOT allow access to Function constructor in getter', function() {
  577. expect(function() {
  578. scope.$eval('{}.toString.constructor');
  579. }).toThrowMinErr(
  580. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  581. 'Expression: {}.toString.constructor');
  582. });
  583. });
  584. describe('Window and $element/node', function() {
  585. it('should NOT allow access to the Window or DOM when indexing', inject(function($window, $document) {
  586. scope.wrap = {w: $window, d: $document};
  587. expect(function() {
  588. scope.$eval('wrap["w"]', scope);
  589. }).toThrowMinErr(
  590. '$parse', 'isecwindow', 'Referencing the Window in Angular expressions is ' +
  591. 'disallowed! Expression: wrap["w"]');
  592. expect(function() {
  593. scope.$eval('wrap["d"]', scope);
  594. }).toThrowMinErr(
  595. '$parse', 'isecdom', 'Referencing DOM nodes in Angular expressions is ' +
  596. 'disallowed! Expression: wrap["d"]');
  597. }));
  598. it('should NOT allow access to the Window or DOM returned from a function', inject(function($window, $document) {
  599. scope.getWin = valueFn($window);
  600. scope.getDoc = valueFn($document);
  601. expect(function() {
  602. scope.$eval('getWin()', scope);
  603. }).toThrowMinErr(
  604. '$parse', 'isecwindow', 'Referencing the Window in Angular expressions is ' +
  605. 'disallowed! Expression: getWin()');
  606. expect(function() {
  607. scope.$eval('getDoc()', scope);
  608. }).toThrowMinErr(
  609. '$parse', 'isecdom', 'Referencing DOM nodes in Angular expressions is ' +
  610. 'disallowed! Expression: getDoc()');
  611. }));
  612. it('should NOT allow calling functions on Window or DOM', inject(function($window, $document) {
  613. scope.a = {b: { win: $window, doc: $document }};
  614. expect(function() {
  615. scope.$eval('a.b.win.alert(1)', scope);
  616. }).toThrowMinErr(
  617. '$parse', 'isecwindow', 'Referencing the Window in Angular expressions is ' +
  618. 'disallowed! Expression: a.b.win.alert(1)');
  619. expect(function() {
  620. scope.$eval('a.b.doc.on("click")', scope);
  621. }).toThrowMinErr(
  622. '$parse', 'isecdom', 'Referencing DOM nodes in Angular expressions is ' +
  623. 'disallowed! Expression: a.b.doc.on("click")');
  624. }));
  625. });
  626. });
  627. describe('overriding constructor', function() {
  628. it('should evaluate grouped expressions', function() {
  629. scope.foo = function foo() {
  630. return "foo";
  631. };
  632. // When not overridden, access should be restricted both by the dot operator and by the
  633. // index operator.
  634. expect(function() {
  635. scope.$eval('foo.constructor()', scope)
  636. }).toThrowMinErr(
  637. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  638. 'Expression: foo.constructor()');
  639. expect(function() {
  640. scope.$eval('foo["constructor"]()', scope)
  641. }).toThrowMinErr(
  642. '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
  643. 'Expression: foo["constructor"]()');
  644. // User defined value assigned to constructor.
  645. scope.foo.constructor = function constructor() {
  646. return "custom constructor";
  647. };
  648. // Dot operator should still block it.
  649. expect(function() {
  650. scope.$eval('foo.constructor()', scope)
  651. }).toThrowMinErr(
  652. '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
  653. 'Expression: foo.constructor()');
  654. // However, the index operator should allow it.
  655. expect(scope.$eval('foo["constructor"]()', scope)).toBe('custom constructor');
  656. });
  657. });
  658. it('should call the function from the received instance and not from a new one', function() {
  659. var n = 0;
  660. scope.fn = function() {
  661. var c = n++;
  662. return { c: c, anotherFn: function() { return this.c == c; } };
  663. };
  664. expect(scope.$eval('fn().anotherFn()')).toBe(true);
  665. });
  666. it('should call the function once when it is part of the context', function() {
  667. var count = 0;
  668. scope.fn = function() {
  669. count++;
  670. return { anotherFn: function() { return "lucas"; } };
  671. };
  672. expect(scope.$eval('fn().anotherFn()')).toBe('lucas');
  673. expect(count).toBe(1);
  674. });
  675. it('should call the function once when it is not part of the context', function() {
  676. var count = 0;
  677. scope.fn = function() {
  678. count++;
  679. return function() { return 'lucas'; };
  680. };
  681. expect(scope.$eval('fn()()')).toBe('lucas');
  682. expect(count).toBe(1);
  683. });
  684. it('should call the function once when it is part of the context on assignments', function() {
  685. var count = 0;
  686. var element = {};
  687. scope.fn = function() {
  688. count++;
  689. return element;
  690. };
  691. expect(scope.$eval('fn().name = "lucas"')).toBe('lucas');
  692. expect(element.name).toBe('lucas');
  693. expect(count).toBe(1);
  694. });
  695. it('should call the function once when it is part of the context on array lookups', function() {
  696. var count = 0;
  697. var element = [];
  698. scope.fn = function() {
  699. count++;
  700. return element;
  701. };
  702. expect(scope.$eval('fn()[0] = "lucas"')).toBe('lucas');
  703. expect(element[0]).toBe('lucas');
  704. expect(count).toBe(1);
  705. });
  706. it('should call the function once when it is part of the context on array lookup function', function() {
  707. var count = 0;
  708. var element = [{anotherFn: function() { return 'lucas';} }];
  709. scope.fn = function() {
  710. count++;
  711. return element;
  712. };
  713. expect(scope.$eval('fn()[0].anotherFn()')).toBe('lucas');
  714. expect(count).toBe(1);
  715. });
  716. it('should call the function once when it is part of the context on property lookup function', function() {
  717. var count = 0;
  718. var element = {name: {anotherFn: function() { return 'lucas';} } };
  719. scope.fn = function() {
  720. count++;
  721. return element;
  722. };
  723. expect(scope.$eval('fn().name.anotherFn()')).toBe('lucas');
  724. expect(count).toBe(1);
  725. });
  726. it('should call the function once when it is part of a sub-expression', function() {
  727. var count = 0;
  728. scope.element = [{}];
  729. scope.fn = function() {
  730. count++;
  731. return 0;
  732. };
  733. expect(scope.$eval('element[fn()].name = "lucas"')).toBe('lucas');
  734. expect(scope.element[0].name).toBe('lucas');
  735. expect(count).toBe(1);
  736. });
  737. describe('assignable', function() {
  738. it('should expose assignment function', inject(function($parse) {
  739. var fn = $parse('a');
  740. expect(fn.assign).toBeTruthy();
  741. var scope = {};
  742. fn.assign(scope, 123);
  743. expect(scope).toEqual({a:123});
  744. }));
  745. });
  746. describe('locals', function() {
  747. it('should expose local variables', inject(function($parse) {
  748. expect($parse('a')({a: 0}, {a: 1})).toEqual(1);
  749. expect($parse('add(a,b)')({b: 1, add: function(a, b) { return a + b; }}, {a: 2})).toEqual(3);
  750. }));
  751. it('should expose traverse locals', inject(function($parse) {
  752. expect($parse('a.b')({a: {b: 0}}, {a: {b:1}})).toEqual(1);
  753. expect($parse('a.b')({a: null}, {a: {b:1}})).toEqual(1);
  754. expect($parse('a.b')({a: {b: 0}}, {a: null})).toEqual(undefined);
  755. expect($parse('a.b.c')({a: null}, {a: {b: {c: 1}}})).toEqual(1);
  756. }));
  757. });
  758. describe('literal', function() {
  759. it('should mark scalar value expressions as literal', inject(function($parse) {
  760. expect($parse('0').literal).toBe(true);
  761. expect($parse('"hello"').literal).toBe(true);
  762. expect($parse('true').literal).toBe(true);
  763. expect($parse('false').literal).toBe(true);
  764. expect($parse('null').literal).toBe(true);
  765. expect($parse('undefined').literal).toBe(true);
  766. }));
  767. it('should mark array expressions as literal', inject(function($parse) {
  768. expect($parse('[]').literal).toBe(true);
  769. expect($parse('[1, 2, 3]').literal).toBe(true);
  770. expect($parse('[1, identifier]').literal).toBe(true);
  771. }));
  772. it('should mark object expressions as literal', inject(function($parse) {
  773. expect($parse('{}').literal).toBe(true);
  774. expect($parse('{x: 1}').literal).toBe(true);
  775. expect($parse('{foo: bar}').literal).toBe(true);
  776. }));
  777. it('should not mark function calls or operator expressions as literal', inject(function($parse) {
  778. expect($parse('1 + 1').literal).toBe(false);
  779. expect($parse('call()').literal).toBe(false);
  780. expect($parse('[].length').literal).toBe(false);
  781. }));
  782. });
  783. describe('constant', function() {
  784. it('should mark scalar value expressions as constant', inject(function($parse) {
  785. expect($parse('12.3').constant).toBe(true);
  786. expect($parse('"string"').constant).toBe(true);
  787. expect($parse('true').constant).toBe(true);
  788. expect($parse('false').constant).toBe(true);
  789. expect($parse('null').constant).toBe(true);
  790. expect($parse('undefined').constant).toBe(true);
  791. }));
  792. it('should mark arrays as constant if they only contain constant elements', inject(function($parse) {
  793. expect($parse('[]').constant).toBe(true);
  794. expect($parse('[1, 2, 3]').constant).toBe(true);
  795. expect($parse('["string", null]').constant).toBe(true);
  796. expect($parse('[[]]').constant).toBe(true);
  797. expect($parse('[1, [2, 3], {4: 5}]').constant).toBe(true);
  798. }));
  799. it('should not mark arrays as constant if they contain any non-constant elements', inject(function($parse) {
  800. expect($parse('[foo]').constant).toBe(false);
  801. expect($parse('[x + 1]').constant).toBe(false);
  802. expect($parse('[bar[0]]').constant).toBe(false);
  803. }));
  804. it('should mark complex expressions involving constant values as constant', inject(function($parse) {
  805. expect($parse('!true').constant).toBe(true);
  806. expect($parse('1 - 1').constant).toBe(true);
  807. expect($parse('"foo" + "bar"').constant).toBe(true);
  808. expect($parse('5 != null').constant).toBe(true);
  809. expect($parse('{standard: 4/3, wide: 16/9}').constant).toBe(true);
  810. }));
  811. it('should not mark any expression involving variables or function calls as constant', inject(function($parse) {
  812. expect($parse('true.toString()').constant).toBe(false);
  813. expect($parse('foo(1, 2, 3)').constant).toBe(false);
  814. expect($parse('"name" + id').constant).toBe(false);
  815. }));
  816. });
  817. describe('nulls in expressions', function() {
  818. // simpleGetterFn1
  819. it('should return null for `a` where `a` is null', inject(function($rootScope) {
  820. $rootScope.a = null;
  821. expect($rootScope.$eval('a')).toBe(null);
  822. }));
  823. it('should return undefined for `a` where `a` is undefined', inject(function($rootScope) {
  824. expect($rootScope.$eval('a')).toBeUndefined();
  825. }));
  826. // simpleGetterFn2
  827. it('should return undefined for properties of `null` constant', inject(function($rootScope) {
  828. expect($rootScope.$eval('null.a')).toBeUndefined();
  829. }));
  830. it('should return undefined for properties of `null` values', inject(function($rootScope) {
  831. $rootScope.a = null;
  832. expect($rootScope.$eval('a.b')).toBeUndefined();
  833. }));
  834. it('should return null for `a.b` where `b` is null', inject(function($rootScope) {
  835. $rootScope.a = { b: null };
  836. expect($rootScope.$eval('a.b')).toBe(null);
  837. }));
  838. // cspSafeGetter && pathKeys.length < 6 || pathKeys.length > 2
  839. it('should return null for `a.b.c.d.e` where `e` is null', inject(function($rootScope) {
  840. $rootScope.a = { b: { c: { d: { e: null } } } };
  841. expect($rootScope.$eval('a.b.c.d.e')).toBe(null);
  842. }));
  843. it('should return undefined for `a.b.c.d.e` where `d` is null', inject(function($rootScope) {
  844. $rootScope.a = { b: { c: { d: null } } };
  845. expect($rootScope.$eval('a.b.c.d.e')).toBeUndefined();
  846. }));
  847. // cspSafeGetter || pathKeys.length > 6
  848. it('should return null for `a.b.c.d.e.f.g` where `g` is null', inject(function($rootScope) {
  849. $rootScope.a = { b: { c: { d: { e: { f: { g: null } } } } } };
  850. expect($rootScope.$eval('a.b.c.d.e.f.g')).toBe(null);
  851. }));
  852. it('should return undefined for `a.b.c.d.e.f.g` where `f` is null', inject(function($rootScope) {
  853. $rootScope.a = { b: { c: { d: { e: { f: null } } } } };
  854. expect($rootScope.$eval('a.b.c.d.e.f.g')).toBeUndefined();
  855. }));
  856. });
  857. });
  858. });
  859. });
  860. describe('promises', function() {
  861. var deferred, promise, q;
  862. describe('unwrapPromises setting', function () {
  863. beforeEach(inject(function($rootScope, $q) {
  864. scope = $rootScope;
  865. $rootScope.$apply(function() {
  866. deferred = $q.defer();
  867. deferred.resolve('Bobo');
  868. promise = deferred.promise;
  869. });
  870. }));
  871. it('should not unwrap promises by default', inject(function ($parse) {
  872. scope.person = promise;
  873. scope.things = {person: promise};
  874. scope.getPerson = function () { return promise; };
  875. var getter = $parse('person');
  876. var propGetter = $parse('things.person');
  877. var fnGetter = $parse('getPerson()');
  878. expect(getter(scope)).toBe(promise);
  879. expect(propGetter(scope)).toBe(promise);
  880. expect(fnGetter(scope)).toBe(promise);
  881. }));
  882. });
  883. forEach([true, false], function(cspEnabled) {
  884. describe('promise logging (csp:' + cspEnabled + ')', function() {
  885. var $log;
  886. var PROMISE_WARNING_REGEXP = /\[\$parse\] Promise found in the expression `[^`]+`. Automatic unwrapping of promises in Angular expressions is deprecated\./;
  887. beforeEach(module(function($parseProvider) {
  888. $parseProvider.unwrapPromises(true);
  889. }));
  890. beforeEach(inject(function($rootScope, $q, _$log_) {
  891. scope = $rootScope;
  892. $rootScope.$apply(function() {
  893. deferred = $q.defer();
  894. deferred.resolve('Bobo');
  895. promise = deferred.promise;
  896. });
  897. $log = _$log_;
  898. }));
  899. it('should log warnings by default', function() {
  900. scope.person = promise;
  901. scope.$eval('person');
  902. expect($log.warn.logs.pop()).toEqual(['[$parse] Promise found in the expression `person`. ' +
  903. 'Automatic unwrapping of promises in Angular expressions is deprecated.']);
  904. });
  905. it('should log warnings for deep promises', function() {
  906. scope.car = {wheel: {disc: promise}};
  907. scope.$eval('car.wheel.disc.pad');
  908. expect($log.warn.logs.pop()).toMatch(PROMISE_WARNING_REGEXP);
  909. });
  910. it('should log warnings for setters', function() {
  911. scope.person = promise;
  912. scope.$eval('person.name = "Bubu"');
  913. expect($log.warn.logs.pop()).toMatch(PROMISE_WARNING_REGEXP);
  914. });
  915. it('should log only a single warning for each expression', function() {
  916. scope.person1 = promise;
  917. scope.person2 = promise;
  918. scope.$eval('person1');
  919. scope.$eval('person1');
  920. expect($log.warn.logs.pop()).toMatch(/`person1`/);
  921. expect($log.warn.logs).toEqual([]);
  922. scope.$eval('person1');
  923. scope.$eval('person2');
  924. scope.$eval('person1');
  925. scope.$eval('person2');
  926. expect($log.warn.logs.pop()).toMatch(/`person2`/);
  927. expect($log.warn.logs).toEqual([]);
  928. });
  929. it('should log warning for complex expressions', function() {
  930. scope.person1 = promise;
  931. scope.person2 = promise;
  932. scope.$eval('person1 + person2');
  933. expect($log.warn.logs.pop()).toMatch(/`person1 \+ person2`/);
  934. expect($log.warn.logs).toEqual([]);
  935. });
  936. });
  937. });
  938. forEach([true, false], function(cspEnabled) {
  939. describe('csp ' + cspEnabled, function() {
  940. beforeEach(module(function($parseProvider) {
  941. $parseProvider.unwrapPromises(true);
  942. $parseProvider.logPromiseWarnings(false);
  943. }));
  944. beforeEach(inject(function($rootScope, $sniffer, $q) {
  945. scope = $rootScope;
  946. $sniffer.csp = cspEnabled;
  947. q = $q;
  948. deferred = q.defer();
  949. promise = deferred.promise;
  950. }));
  951. describe('{{promise}}', function() {
  952. it('should evaluated resolved promise and get its value', function() {
  953. deferred.resolve('hello!');
  954. scope.greeting = promise;
  955. expect(scope.$eval('greeting')).toBe(undefined);
  956. scope.$digest();
  957. expect(scope.$eval('greeting')).toBe('hello!');
  958. });
  959. it('should evaluated rejected promise and ignore the rejection reason', function() {
  960. deferred.reject('sorry');
  961. scope.greeting = promise;
  962. expect(scope.$eval('greeting')).toBe(undefined);
  963. scope.$digest();
  964. expect(scope.$eval('greeting')).toBe(undefined);
  965. });
  966. it('should evaluate a promise and eventualy get its value', function() {
  967. scope.greeting = promise;
  968. expect(scope.$eval('greeting')).toBe(undefined);
  969. scope.$digest();
  970. expect(scope.$eval('greeting')).toBe(undefined);
  971. deferred.resolve('hello!');
  972. expect(scope.$eval('greeting')).toBe(undefined);
  973. scope.$digest();
  974. expect(scope.$eval('greeting')).toBe('hello!');
  975. });
  976. it('should evaluate a promise and eventualy ignore its rejection', function() {
  977. scope.greeting = promise;
  978. expect(scope.$eval('greeting')).toBe(undefined);
  979. scope.$digest();
  980. expect(scope.$eval('greeting')).toBe(undefined);
  981. deferred.reject('sorry');
  982. expect(scope.$eval('greeting')).toBe(undefined);
  983. scope.$digest();
  984. expect(scope.$eval('greeting')).toBe(undefined);
  985. });
  986. describe('assignment into promises', function() {
  987. // This behavior is analogous to assignments to non-promise values
  988. // that are lazily set on the scope.
  989. it('should evaluate a resolved object promise and set its value', inject(function($parse) {
  990. scope.person = promise;
  991. deferred.resolve({'name': 'Bill Gates'});
  992. var getter = $parse('person.name', { unwrapPromises: true });
  993. expect(getter(scope)).toBe(undefined);
  994. scope.$digest();
  995. expect(getter(scope)).toBe('Bill Gates');
  996. getter.assign(scope, 'Warren Buffet');
  997. expect(getter(scope)).toBe('Warren Buffet');
  998. }));
  999. it('should evaluate a resolved primitive type promise and set its value', inject(function($parse) {
  1000. scope.greeting = promise;
  1001. deferred.resolve('Salut!');
  1002. var getter = $parse('greeting', { unwrapPromises: true });
  1003. expect(getter(scope)).toBe(undefined);
  1004. scope.$digest();
  1005. expect(getter(scope)).toBe('Salut!');
  1006. getter.assign(scope, 'Bonjour');
  1007. expect(getter(scope)).toBe('Bonjour');
  1008. }));
  1009. it('should evaluate an unresolved promise and set and remember its value', inject(function($parse) {
  1010. scope.person = promise;
  1011. var getter = $parse('person.name', { unwrapPromises: true });
  1012. expect(getter(scope)).toBe(undefined);
  1013. scope.$digest();
  1014. expect(getter(scope)).toBe(undefined);
  1015. getter.assign(scope, 'Bonjour');
  1016. scope.$digest();
  1017. expect(getter(scope)).toBe('Bonjour');
  1018. var c1Getter = $parse('person.A.B.C1', { unwrapPromises: true });
  1019. scope.$digest();
  1020. expect(c1Getter(scope)).toBe(undefined);
  1021. c1Getter.assign(scope, 'c1_value');
  1022. scope.$digest();
  1023. expect(c1Getter(scope)).toBe('c1_value');
  1024. // Set another property on the person.A.B
  1025. var c2Getter = $parse('person.A.B.C2', { unwrapPromises: true });
  1026. scope.$digest();
  1027. expect(c2Getter(scope)).toBe(undefined);
  1028. c2Getter.assign(scope, 'c2_value');
  1029. scope.$digest();
  1030. expect(c2Getter(scope)).toBe('c2_value');
  1031. // c1 should be unchanged.
  1032. expect($parse('person.A', { unwrapPromises: true })(scope)).toEqual(
  1033. {B: {C1: 'c1_value', C2: 'c2_value'}});
  1034. }));
  1035. it('should evaluate a res…

Large files files are truncated, but you can click here to view the full file