PageRenderTime 34ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/packages/ember-handlebars/tests/handlebars_test.js

https://gitlab.com/intelij/ember.js
JavaScript | 2483 lines | 1810 code | 616 blank | 57 comment | 12 complexity | 17e08387f15e6a4862e1086177805a74 MD5 | raw file
  1. /*globals TemplateTests:true MyApp:true App:true */
  2. var get = Ember.get, set = Ember.set;
  3. var forEach = Ember.EnumerableUtils.forEach;
  4. var trim = Ember.$.trim;
  5. var firstGrandchild = function(view) {
  6. return get(get(view, 'childViews').objectAt(0), 'childViews').objectAt(0);
  7. };
  8. var nthChild = function(view, nth) {
  9. return get(view, 'childViews').objectAt(nth || 0);
  10. };
  11. var firstChild = nthChild;
  12. var originalLog, logCalls;
  13. (function() {
  14. Ember.$.fn.caretPosition = function() {
  15. var ctrl = this[0];
  16. var CaretPos = 0;
  17. // IE Support
  18. if (document.selection) {
  19. ctrl.focus();
  20. var Sel = document.selection.createRange ();
  21. Sel.moveStart ('character', -ctrl.value.length);
  22. CaretPos = Sel.text.length;
  23. }
  24. // Firefox support
  25. else if (ctrl.selectionStart || ctrl.selectionStart === '0') {
  26. CaretPos = ctrl.selectionStart;
  27. }
  28. return (CaretPos);
  29. };
  30. Ember.$.fn.setCaretPosition = function(pos) {
  31. var ctrl = this[0];
  32. if(ctrl.setSelectionRange) {
  33. ctrl.focus();
  34. ctrl.setSelectionRange(pos,pos);
  35. } else if (ctrl.createTextRange) {
  36. var range = ctrl.createTextRange();
  37. range.collapse(true);
  38. range.moveEnd('character', pos);
  39. range.moveStart('character', pos);
  40. range.select();
  41. }
  42. };
  43. })();
  44. var view;
  45. var appendView = function() {
  46. Ember.run(function() { view.appendTo('#qunit-fixture'); });
  47. };
  48. var additionalTeardown;
  49. var originalLookup = Ember.lookup, lookup;
  50. var TemplateTests, container;
  51. /**
  52. This module specifically tests integration with Handlebars and Ember-specific
  53. Handlebars extensions.
  54. If you add additional template support to Ember.View, you should create a new
  55. file in which to test.
  56. */
  57. module("Ember.View - handlebars integration", {
  58. setup: function() {
  59. Ember.lookup = lookup = { Ember: Ember };
  60. lookup.TemplateTests = TemplateTests = Ember.Namespace.create();
  61. container = new Ember.Container();
  62. container.optionsForType('template', { instantiate: false });
  63. },
  64. teardown: function() {
  65. if (view) {
  66. Ember.run(function() {
  67. view.destroy();
  68. });
  69. view = null;
  70. }
  71. Ember.lookup = originalLookup;
  72. }
  73. });
  74. test("template view should call the function of the associated template", function() {
  75. container.register('template', 'testTemplate', Ember.Handlebars.compile("<h1 id='twas-called'>template was called</h1>"));
  76. view = Ember.View.create({
  77. container: container,
  78. templateName: 'testTemplate'
  79. });
  80. appendView();
  81. ok(view.$('#twas-called').length, "the named template was called");
  82. });
  83. test("template view should call the function of the associated template with itself as the context", function() {
  84. container.register('template', 'testTemplate', Ember.Handlebars.compile("<h1 id='twas-called'>template was called for {{view.personName}}. Yea {{view.personName}}</h1>"));
  85. view = Ember.View.createWithMixins({
  86. container: container,
  87. templateName: 'testTemplate',
  88. _personName: "Tom DAAAALE",
  89. _i: 0,
  90. personName: Ember.computed(function() {
  91. this._i++;
  92. return this._personName + this._i;
  93. })
  94. });
  95. appendView();
  96. equal("template was called for Tom DAAAALE1. Yea Tom DAAAALE1", view.$('#twas-called').text(), "the named template was called with the view as the data source");
  97. });
  98. test("should allow values from normal JavaScript hash objects to be used", function() {
  99. view = Ember.View.create({
  100. template: Ember.Handlebars.compile('{{#with view.person}}{{firstName}} {{lastName}} (and {{pet.name}}){{/with}}'),
  101. person: {
  102. firstName: 'Señor',
  103. lastName: 'CFC',
  104. pet: {
  105. name: 'Fido'
  106. }
  107. }
  108. });
  109. appendView();
  110. equal(view.$().text(), "Señor CFC (and Fido)", "prints out values from a hash");
  111. });
  112. test("htmlSafe should return an instance of Handlebars.SafeString", function() {
  113. var safeString = Ember.String.htmlSafe("you need to be more <b>bold</b>");
  114. ok(safeString instanceof Handlebars.SafeString, "should return SafeString");
  115. });
  116. test("should escape HTML in normal mustaches", function() {
  117. view = Ember.View.create({
  118. template: Ember.Handlebars.compile('{{view.output}}'),
  119. output: "you need to be more <b>bold</b>"
  120. });
  121. Ember.run(function() { view.appendTo('#qunit-fixture'); });
  122. equal(view.$('b').length, 0, "does not create an element");
  123. equal(view.$().text(), 'you need to be more <b>bold</b>', "inserts entities, not elements");
  124. Ember.run(function() { set(view, 'output', "you are so <i>super</i>"); });
  125. equal(view.$().text(), 'you are so <i>super</i>', "updates with entities, not elements");
  126. equal(view.$('i').length, 0, "does not create an element when value is updated");
  127. });
  128. test("should not escape HTML in triple mustaches", function() {
  129. view = Ember.View.create({
  130. template: Ember.Handlebars.compile('{{{view.output}}}'),
  131. output: "you need to be more <b>bold</b>"
  132. });
  133. Ember.run(function() {
  134. view.appendTo('#qunit-fixture');
  135. });
  136. equal(view.$('b').length, 1, "creates an element");
  137. Ember.run(function() {
  138. set(view, 'output', "you are so <i>super</i>");
  139. });
  140. equal(view.$('i').length, 1, "creates an element when value is updated");
  141. });
  142. test("should not escape HTML if string is a Handlebars.SafeString", function() {
  143. view = Ember.View.create({
  144. template: Ember.Handlebars.compile('{{view.output}}'),
  145. output: new Handlebars.SafeString("you need to be more <b>bold</b>")
  146. });
  147. Ember.run(function() {
  148. view.appendTo('#qunit-fixture');
  149. });
  150. equal(view.$('b').length, 1, "creates an element");
  151. Ember.run(function() {
  152. set(view, 'output', new Handlebars.SafeString("you are so <i>super</i>"));
  153. });
  154. equal(view.$('i').length, 1, "creates an element when value is updated");
  155. });
  156. test("child views can be inserted using the {{view}} Handlebars helper", function() {
  157. container.register('template', 'nester', Ember.Handlebars.compile("<h1 id='hello-world'>Hello {{world}}</h1>{{view \"TemplateTests.LabelView\"}}"));
  158. container.register('template', 'nested', Ember.Handlebars.compile("<div id='child-view'>Goodbye {{cruel}} {{world}}</div>"));
  159. var context = {
  160. world: "world!"
  161. };
  162. TemplateTests.LabelView = Ember.View.extend({
  163. container: container,
  164. tagName: "aside",
  165. templateName: 'nested'
  166. });
  167. view = Ember.View.create({
  168. container: container,
  169. templateName: 'nester',
  170. context: context
  171. });
  172. Ember.set(context, 'cruel', "cruel");
  173. appendView();
  174. ok(view.$("#hello-world:contains('Hello world!')").length, "The parent view renders its contents");
  175. ok(view.$("#child-view:contains('Goodbye cruel world!')").length === 1, "The child view renders its content once");
  176. ok(view.$().text().match(/Hello world!.*Goodbye cruel world\!/), "parent view should appear before the child view");
  177. });
  178. test("should accept relative paths to views", function() {
  179. view = Ember.View.create({
  180. template: Ember.Handlebars.compile('Hey look, at {{view "view.myCool.view"}}'),
  181. myCool: Ember.Object.create({
  182. view: Ember.View.extend({
  183. template: Ember.Handlebars.compile("my cool view")
  184. })
  185. })
  186. });
  187. appendView();
  188. equal(view.$().text(), "Hey look, at my cool view");
  189. });
  190. test("child views can be inserted inside a bind block", function() {
  191. container.register('template', 'nester', Ember.Handlebars.compile("<h1 id='hello-world'>Hello {{world}}</h1>{{view \"TemplateTests.BQView\"}}"));
  192. container.register('template', 'nested', Ember.Handlebars.compile("<div id='child-view'>Goodbye {{#with content}}{{blah}} {{view \"TemplateTests.OtherView\"}}{{/with}} {{world}}</div>"));
  193. container.register('template', 'other', Ember.Handlebars.compile("cruel"));
  194. var context = {
  195. world: "world!"
  196. };
  197. TemplateTests.BQView = Ember.View.extend({
  198. container: container,
  199. tagName: "blockquote",
  200. templateName: 'nested'
  201. });
  202. TemplateTests.OtherView = Ember.View.extend({
  203. container: container,
  204. templateName: 'other'
  205. });
  206. view = Ember.View.create({
  207. container: container,
  208. context: context,
  209. templateName: 'nester'
  210. });
  211. Ember.set(context, 'content', Ember.Object.create({ blah: "wot" }));
  212. appendView();
  213. ok(view.$("#hello-world:contains('Hello world!')").length, "The parent view renders its contents");
  214. ok(view.$("blockquote").text().match(/Goodbye.*wot.*cruel.*world\!/), "The child view renders its content once");
  215. ok(view.$().text().match(/Hello world!.*Goodbye.*wot.*cruel.*world\!/), "parent view should appear before the child view");
  216. });
  217. test("Ember.View should bind properties in the parent context", function() {
  218. var context = {
  219. content: Ember.Object.create({
  220. wham: 'bam'
  221. }),
  222. blam: "shazam"
  223. };
  224. view = Ember.View.create({
  225. context: context,
  226. template: Ember.Handlebars.compile('<h1 id="first">{{#with content}}{{wham}}-{{../blam}}{{/with}}</h1>')
  227. });
  228. Ember.run(function() {
  229. view.appendTo('#qunit-fixture');
  230. });
  231. equal(view.$('#first').text(), "bam-shazam", "renders parent properties");
  232. });
  233. test("Ember.View should bind properties in the grandparent context", function() {
  234. var context = {
  235. content: Ember.Object.create({
  236. wham: 'bam',
  237. thankYou: Ember.Object.create({
  238. value: "ma'am"
  239. })
  240. }),
  241. blam: "shazam"
  242. };
  243. view = Ember.View.create({
  244. context: context,
  245. template: Ember.Handlebars.compile('<h1 id="first">{{#with content}}{{#with thankYou}}{{value}}-{{../wham}}-{{../../blam}}{{/with}}{{/with}}</h1>')
  246. });
  247. Ember.run(function() {
  248. view.appendTo('#qunit-fixture');
  249. });
  250. equal(view.$('#first').text(), "ma'am-bam-shazam", "renders grandparent properties");
  251. });
  252. test("Ember.View should update when a property changes and the bind helper is used", function() {
  253. container.register('template', 'foo', Ember.Handlebars.compile('<h1 id="first">{{#with view.content}}{{bind "wham"}}{{/with}}</h1>'));
  254. view = Ember.View.create({
  255. container: container,
  256. templateName: 'foo',
  257. content: Ember.Object.create({
  258. wham: 'bam',
  259. thankYou: "ma'am"
  260. })
  261. });
  262. Ember.run(function() {
  263. view.appendTo('#qunit-fixture');
  264. });
  265. equal(view.$('#first').text(), "bam", "precond - view renders Handlebars template");
  266. Ember.run(function() { set(get(view, 'content'), 'wham', 'bazam'); });
  267. equal(view.$('#first').text(), "bazam", "view updates when a bound property changes");
  268. });
  269. test("Ember.View should not use keyword incorrectly - Issue #1315", function() {
  270. container.register('template', 'foo', Ember.Handlebars.compile('{{#each value in view.content}}{{value}}-{{#each option in view.options}}{{option.value}}:{{option.label}} {{/each}}{{/each}}'));
  271. view = Ember.View.create({
  272. container: container,
  273. templateName: 'foo',
  274. content: Ember.A(['X', 'Y']),
  275. options: Ember.A([
  276. { label: 'One', value: 1 },
  277. { label: 'Two', value: 2 }
  278. ])
  279. });
  280. Ember.run(function() {
  281. view.appendTo('#qunit-fixture');
  282. });
  283. equal(view.$().text(), 'X-1:One 2:Two Y-1:One 2:Two ');
  284. });
  285. test("Ember.View should update when a property changes and no bind helper is used", function() {
  286. container.register('template', 'foo', Ember.Handlebars.compile('<h1 id="first">{{#with view.content}}{{wham}}{{/with}}</h1>'));
  287. var templates = Ember.Object.create({
  288. foo: Ember.Handlebars.compile('<h1 id="first">{{#with view.content}}{{wham}}{{/with}}</h1>')
  289. });
  290. view = Ember.View.create({
  291. container: container,
  292. templateName: 'foo',
  293. content: Ember.Object.create({
  294. wham: 'bam',
  295. thankYou: "ma'am"
  296. })
  297. });
  298. Ember.run(function() {
  299. view.appendTo('#qunit-fixture');
  300. });
  301. equal(view.$('#first').text(), "bam", "precond - view renders Handlebars template");
  302. Ember.run(function() { set(get(view, 'content'), 'wham', 'bazam'); });
  303. equal(view.$('#first').text(), "bazam", "view updates when a bound property changes");
  304. });
  305. test("Ember.View should update when the property used with the #with helper changes", function() {
  306. container.register('template', 'foo', Ember.Handlebars.compile('<h1 id="first">{{#with view.content}}{{wham}}{{/with}}</h1>'));
  307. view = Ember.View.create({
  308. container: container,
  309. templateName: 'foo',
  310. content: Ember.Object.create({
  311. wham: 'bam',
  312. thankYou: "ma'am"
  313. })
  314. });
  315. appendView();
  316. equal(view.$('#first').text(), "bam", "precond - view renders Handlebars template");
  317. Ember.run(function() {
  318. set(view, 'content', Ember.Object.create({
  319. wham: 'bazam'
  320. }));
  321. });
  322. equal(view.$('#first').text(), "bazam", "view updates when a bound property changes");
  323. });
  324. test("should not update when a property is removed from the view", function() {
  325. container.register('template', 'foo', Ember.Handlebars.compile('<h1 id="first">{{#bind "view.content"}}{{#bind "foo"}}{{bind "baz"}}{{/bind}}{{/bind}}</h1>'));
  326. view = Ember.View.create({
  327. container: container,
  328. templateName: 'foo',
  329. content: Ember.Object.create({
  330. foo: Ember.Object.create({
  331. baz: "unicorns"
  332. })
  333. })
  334. });
  335. appendView();
  336. equal(view.$('#first').text(), "unicorns", "precond - renders the bound value");
  337. var oldContent = get(view, 'content');
  338. Ember.run(function() {
  339. set(view, 'content', Ember.Object.create({
  340. foo: Ember.Object.create({
  341. baz: "ninjas"
  342. })
  343. }));
  344. });
  345. equal(view.$('#first').text(), 'ninjas', "updates to new content value");
  346. Ember.run(function() {
  347. set(oldContent, 'foo.baz', 'rockstars');
  348. });
  349. Ember.run(function() {
  350. set(oldContent, 'foo.baz', 'ewoks');
  351. });
  352. equal(view.$('#first').text(), "ninjas", "does not update removed object");
  353. });
  354. test("Handlebars templates update properties if a content object changes", function() {
  355. container.register('template', 'menu', Ember.Handlebars.compile('<h1>Today\'s Menu</h1>{{#bind "view.coffee"}}<h2>{{color}} coffee</h2><span id="price">{{bind "price"}}</span>{{/bind}}'));
  356. Ember.run(function() {
  357. view = Ember.View.create({
  358. container: container,
  359. templateName: 'menu',
  360. coffee: Ember.Object.create({
  361. color: 'brown',
  362. price: '$4'
  363. })
  364. });
  365. });
  366. appendView();
  367. equal(view.$('h2').text(), "brown coffee", "precond - renders color correctly");
  368. equal(view.$('#price').text(), '$4', "precond - renders price correctly");
  369. Ember.run(function() {
  370. set(view, 'coffee', Ember.Object.create({
  371. color: "mauve",
  372. price: "$4.50"
  373. }));
  374. });
  375. equal(view.$('h2').text(), "mauve coffee", "should update name field when content changes");
  376. equal(view.$('#price').text(), "$4.50", "should update price field when content changes");
  377. Ember.run(function() {
  378. set(view, 'coffee', Ember.Object.create({
  379. color: "mauve",
  380. price: "$5.50"
  381. }));
  382. });
  383. equal(view.$('h2').text(), "mauve coffee", "should update name field when content changes");
  384. equal(view.$('#price').text(), "$5.50", "should update price field when content changes");
  385. Ember.run(function() {
  386. set(view, 'coffee.price', "$5");
  387. });
  388. equal(view.$('#price').text(), "$5", "should update price field when price property is changed");
  389. Ember.run(function() {
  390. view.destroy();
  391. });
  392. });
  393. test("Template updates correctly if a path is passed to the bind helper", function() {
  394. container.register('template', 'menu', Ember.Handlebars.compile('<h1>{{bind "view.coffee.price"}}</h1>'));
  395. view = Ember.View.create({
  396. container: container,
  397. templateName: 'menu',
  398. coffee: Ember.Object.create({
  399. price: '$4'
  400. })
  401. });
  402. appendView();
  403. equal(view.$('h1').text(), "$4", "precond - renders price");
  404. Ember.run(function() {
  405. set(view, 'coffee.price', "$5");
  406. });
  407. equal(view.$('h1').text(), "$5", "updates when property changes");
  408. Ember.run(function() {
  409. set(view, 'coffee', { price: "$6" });
  410. });
  411. equal(view.$('h1').text(), "$6", "updates when parent property changes");
  412. });
  413. // test("Template updates correctly if a path is passed to the bind helper and the context object is an Ember.ObjectController", function() {
  414. // var templates;
  415. // templates = Ember.Object.create({
  416. // menu: Ember.Handlebars.compile('<h1>{{bind "coffee.price"}}</h1>')
  417. // });
  418. // var controller = Ember.ObjectController.create();
  419. // var realObject = Ember.Object.create({
  420. // price: "$4"
  421. // });
  422. // set(controller, 'content', realObject);
  423. // var view = Ember.View.create({
  424. // templateName: 'menu',
  425. // templates: templates,
  426. // coffee: controller
  427. // });
  428. // view.createElement();
  429. // equal(view.$('h1').text(), "$4", "precond - renders price");
  430. // set(realObject, 'price', "$5");
  431. // equal(view.$('h1').text(), "$5", "updates when property is set on real object");
  432. // Ember.run(function() {
  433. // set(controller, 'price', "$6" );
  434. // });
  435. // equal(view.$('h1').text(), "$6", "updates when property is set on object controller");
  436. // });
  437. test("should update the block when object passed to #if helper changes", function() {
  438. container.register('template', 'menu', Ember.Handlebars.compile('<h1>{{#if view.inception}}{{view.INCEPTION}}{{/if}}</h1>'));
  439. view = Ember.View.create({
  440. container: container,
  441. templateName: 'menu',
  442. INCEPTION: "BOOOOOOOONG doodoodoodoodooodoodoodoo",
  443. inception: 'OOOOoooooOOOOOOooooooo'
  444. });
  445. appendView();
  446. equal(view.$('h1').text(), "BOOOOOOOONG doodoodoodoodooodoodoodoo", "renders block if a string");
  447. var tests = [false, null, undefined, [], '', 0];
  448. forEach(tests, function(val) {
  449. Ember.run(function() {
  450. set(view, 'inception', val);
  451. });
  452. equal(view.$('h1').text(), '', Ember.String.fmt("hides block when conditional is '%@'", [String(val)]));
  453. Ember.run(function() {
  454. set(view, 'inception', true);
  455. });
  456. equal(view.$('h1').text(), "BOOOOOOOONG doodoodoodoodooodoodoodoo", "precond - renders block when conditional is true");
  457. });
  458. });
  459. test("should update the block when object passed to #unless helper changes", function() {
  460. container.register('template', 'advice', Ember.Handlebars.compile('<h1>{{#unless view.onDrugs}}{{view.doWellInSchool}}{{/unless}}</h1>'));
  461. view = Ember.View.create({
  462. container: container,
  463. templateName: 'advice',
  464. onDrugs: true,
  465. doWellInSchool: "Eat your vegetables"
  466. });
  467. appendView();
  468. equal(view.$('h1').text(), "", "hides block if true");
  469. var tests = [false, null, undefined, [], '', 0];
  470. forEach(tests, function(val) {
  471. Ember.run(function() {
  472. set(view, 'onDrugs', val);
  473. });
  474. equal(view.$('h1').text(), 'Eat your vegetables', Ember.String.fmt("renders block when conditional is '%@'; %@", [String(val), Ember.typeOf(val)]));
  475. Ember.run(function() {
  476. set(view, 'onDrugs', true);
  477. });
  478. equal(view.$('h1').text(), "", "precond - hides block when conditional is true");
  479. });
  480. });
  481. test("should update the block when object passed to #if helper changes and an inverse is supplied", function() {
  482. container.register('template', 'menu', Ember.Handlebars.compile('<h1>{{#if view.inception}}{{view.INCEPTION}}{{else}}{{view.SAD}}{{/if}}</h1>'));
  483. view = Ember.View.create({
  484. container: container,
  485. templateName: 'menu',
  486. INCEPTION: "BOOOOOOOONG doodoodoodoodooodoodoodoo",
  487. inception: false,
  488. SAD: 'BOONG?'
  489. });
  490. appendView();
  491. equal(view.$('h1').text(), "BOONG?", "renders alternate if false");
  492. Ember.run(function() { set(view, 'inception', true); });
  493. var tests = [false, null, undefined, [], '', 0];
  494. forEach(tests, function(val) {
  495. Ember.run(function() {
  496. set(view, 'inception', val);
  497. });
  498. equal(view.$('h1').text(), 'BOONG?', Ember.String.fmt("renders alternate if %@", [String(val)]));
  499. Ember.run(function() {
  500. set(view, 'inception', true);
  501. });
  502. equal(view.$('h1').text(), "BOOOOOOOONG doodoodoodoodooodoodoodoo", "precond - renders block when conditional is true");
  503. });
  504. });
  505. test("edge case: child conditional should not render children if parent conditional becomes false", function() {
  506. var childCreated = false;
  507. view = Ember.View.create({
  508. cond1: true,
  509. cond2: false,
  510. viewClass: Ember.View.extend({
  511. init: function() {
  512. this._super();
  513. childCreated = true;
  514. }
  515. }),
  516. template: Ember.Handlebars.compile('{{#if view.cond1}}{{#if view.cond2}}{{#view view.viewClass}}test{{/view}}{{/if}}{{/if}}')
  517. });
  518. appendView();
  519. Ember.run(function() {
  520. // The order of these sets is important for the test
  521. view.set('cond2', true);
  522. view.set('cond1', false);
  523. });
  524. ok(!childCreated, 'child should not be created');
  525. });
  526. // test("Should insert a localized string if the {{loc}} helper is used", function() {
  527. // Ember.stringsFor('en', {
  528. // 'Brazil': 'Brasilia'
  529. // });
  530. // templates = Ember.Object.create({
  531. // 'loc': Ember.Handlebars.compile('<h1>Country: {{loc "Brazil"}}')
  532. // });
  533. // var view = Ember.View.create({
  534. // templateName: 'loc',
  535. // templates: templates,
  536. // country: 'Brazil'
  537. // });
  538. // view.createElement();
  539. // equal(view.$('h1').text(), 'Country: Brasilia', "returns localized value");
  540. // });
  541. test("Template views return throw if their template cannot be found", function() {
  542. view = Ember.View.create({
  543. templateName: 'cantBeFound'
  544. });
  545. throws(function() {
  546. get(view, 'template');
  547. }, /cantBeFound/);
  548. });
  549. test("Layout views return throw if their layout cannot be found", function() {
  550. view = Ember.View.create({
  551. layoutName: 'cantBeFound'
  552. });
  553. throws(function() {
  554. get(view, 'layout');
  555. }, /cantBeFound/);
  556. });
  557. test("Template views add an elementId to child views created using the view helper", function() {
  558. container.register('template', 'parent', Ember.Handlebars.compile('<div>{{view "TemplateTests.ChildView"}}</div>'));
  559. container.register('template', 'child', Ember.Handlebars.compile("I can't believe it's not butter."));
  560. TemplateTests.ChildView = Ember.View.extend({
  561. container: container,
  562. templateName: 'child'
  563. });
  564. view = Ember.View.create({
  565. container: container,
  566. templateName: 'parent'
  567. });
  568. appendView();
  569. var childView = get(view, 'childViews.firstObject');
  570. equal(view.$().children().first().children().first().attr('id'), get(childView, 'elementId'));
  571. });
  572. test("views set the template of their children to a passed block", function() {
  573. container.register('template', 'parent', Ember.Handlebars.compile('<h1>{{#view "TemplateTests.NoTemplateView"}}<span>It worked!</span>{{/view}}</h1>'));
  574. TemplateTests.NoTemplateView = Ember.View.extend();
  575. view = Ember.View.create({
  576. container: container,
  577. templateName: 'parent'
  578. });
  579. appendView();
  580. ok(view.$('h1:has(span)').length === 1, "renders the passed template inside the parent template");
  581. });
  582. test("views render their template in the context of the parent view's context", function() {
  583. container.register('template', 'parent', Ember.Handlebars.compile('<h1>{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}</h1>'));
  584. var context = {
  585. content: {
  586. firstName: "Lana",
  587. lastName: "del Heeeyyyyyy"
  588. }
  589. };
  590. view = Ember.View.create({
  591. container: container,
  592. templateName: 'parent',
  593. context: context
  594. });
  595. appendView();
  596. equal(view.$('h1').text(), "Lana del Heeeyyyyyy", "renders properties from parent context");
  597. });
  598. test("views make a view keyword available that allows template to reference view context", function() {
  599. container.register('template', 'parent', Ember.Handlebars.compile('<h1>{{#with view.content}}{{#view subview}}{{view.firstName}} {{lastName}}{{/view}}{{/with}}</h1>'));
  600. view = Ember.View.create({
  601. container: container,
  602. templateName: 'parent',
  603. content: {
  604. subview: Ember.View.extend({
  605. firstName: "Brodele"
  606. }),
  607. firstName: "Lana",
  608. lastName: "del Heeeyyyyyy"
  609. }
  610. });
  611. appendView();
  612. equal(view.$('h1').text(), "Brodele del Heeeyyyyyy", "renders properties from parent context");
  613. });
  614. test("a view helper's bindings are to the parent context", function(){
  615. var Subview = Ember.View.extend({
  616. classNameBindings: ['color'],
  617. controller: Ember.Object.create({
  618. color: 'green',
  619. name: "bar"
  620. }),
  621. template: Ember.Handlebars.compile('{{view.someController.name}} {{name}}')
  622. });
  623. var View = Ember.View.extend({
  624. controller: Ember.Object.create({
  625. color: "mauve",
  626. name: 'foo'
  627. }),
  628. Subview: Subview,
  629. template: Ember.Handlebars.compile('<h1>{{view view.Subview colorBinding="color" someControllerBinding="this"}}</h1>')
  630. });
  631. view = View.create();
  632. appendView();
  633. equal(view.$('h1 .mauve').length, 1, "renders property on helper declaration from parent context");
  634. equal(view.$('h1 .mauve').text(), "foo bar", "renders property bound in template from subview context");
  635. });
  636. test("should warn if setting a template on a view with a templateName already specified", function() {
  637. view = Ember.View.create({
  638. childView: Ember.View.extend({
  639. templateName: 'foo'
  640. }),
  641. template: Ember.Handlebars.compile('{{#view childView}}test{{/view}}')
  642. });
  643. raises(function() {
  644. appendView();
  645. }, Error, "raises if conflicting template and templateName are provided");
  646. Ember.run(function() {
  647. view.destroy();
  648. });
  649. view = Ember.View.create({
  650. childView: Ember.View.extend(),
  651. template: Ember.Handlebars.compile('{{#view childView templateName="foo"}}test{{/view}}')
  652. });
  653. raises(function() {
  654. appendView();
  655. }, Error, "raises if conflicting template and templateName are provided via a Handlebars template");
  656. });
  657. test("Child views created using the view helper should have their parent view set properly", function() {
  658. TemplateTests = {};
  659. var template = '{{#view "Ember.View"}}{{#view "Ember.View"}}{{view "Ember.View"}}{{/view}}{{/view}}';
  660. view = Ember.View.create({
  661. template: Ember.Handlebars.compile(template)
  662. });
  663. appendView();
  664. var childView = firstGrandchild(view);
  665. equal(childView, get(firstChild(childView), 'parentView'), 'parent view is correct');
  666. });
  667. test("Child views created using the view helper should have their IDs registered for events", function() {
  668. TemplateTests = {};
  669. var template = '{{view "Ember.View"}}{{view "Ember.View" id="templateViewTest"}}';
  670. view = Ember.View.create({
  671. template: Ember.Handlebars.compile(template)
  672. });
  673. appendView();
  674. var childView = firstChild(view);
  675. var id = childView.$()[0].id;
  676. equal(Ember.View.views[id], childView, 'childView without passed ID is registered with Ember.View.views so that it can properly receive events from RootResponder');
  677. childView = nthChild(view, 1);
  678. id = childView.$()[0].id;
  679. equal(id, 'templateViewTest', 'precond -- id of childView should be set correctly');
  680. equal(Ember.View.views[id], childView, 'childView with passed ID is registered with Ember.View.views so that it can properly receive events from RootResponder');
  681. });
  682. test("Child views created using the view helper and that have a viewName should be registered as properties on their parentView", function() {
  683. TemplateTests = {};
  684. var template = '{{#view Ember.View}}{{view Ember.View viewName="ohai"}}{{/view}}';
  685. view = Ember.View.create({
  686. template: Ember.Handlebars.compile(template)
  687. });
  688. appendView();
  689. var parentView = firstChild(view),
  690. childView = firstGrandchild(view);
  691. equal(get(parentView, 'ohai'), childView);
  692. });
  693. test("Collection views that specify an example view class have their children be of that class", function() {
  694. TemplateTests.ExampleViewCollection = Ember.CollectionView.extend({
  695. itemViewClass: Ember.View.extend({
  696. isCustom: true
  697. }),
  698. content: Ember.A(['foo'])
  699. });
  700. var parentView = Ember.View.create({
  701. template: Ember.Handlebars.compile('{{#collection "TemplateTests.ExampleViewCollection"}}OHAI{{/collection}}')
  702. });
  703. Ember.run(function() {
  704. parentView.append();
  705. });
  706. ok(firstGrandchild(parentView).isCustom, "uses the example view class");
  707. Ember.run(function() {
  708. parentView.destroy();
  709. });
  710. });
  711. test("itemViewClass works in the #collection helper", function() {
  712. TemplateTests.ExampleController = Ember.ArrayProxy.create({
  713. content: Ember.A(['alpha'])
  714. });
  715. TemplateTests.ExampleItemView = Ember.View.extend({
  716. isAlsoCustom: true
  717. });
  718. var parentView = Ember.View.create({
  719. template: Ember.Handlebars.compile('{{#collection contentBinding="TemplateTests.ExampleController" itemViewClass="TemplateTests.ExampleItemView"}}beta{{/collection}}')
  720. });
  721. Ember.run(function() {
  722. parentView.append();
  723. });
  724. ok(firstGrandchild(parentView).isAlsoCustom, "uses the example view class specified in the #collection helper");
  725. Ember.run(function() {
  726. parentView.destroy();
  727. });
  728. });
  729. test("itemViewClass works in the #collection helper relatively", function() {
  730. TemplateTests.ExampleController = Ember.ArrayProxy.create({
  731. content: Ember.A(['alpha'])
  732. });
  733. TemplateTests.ExampleItemView = Ember.View.extend({
  734. isAlsoCustom: true
  735. });
  736. TemplateTests.CollectionView = Ember.CollectionView.extend({
  737. possibleItemView: TemplateTests.ExampleItemView
  738. });
  739. var parentView = Ember.View.create({
  740. template: Ember.Handlebars.compile('{{#collection TemplateTests.CollectionView contentBinding="TemplateTests.ExampleController" itemViewClass="possibleItemView"}}beta{{/collection}}')
  741. });
  742. Ember.run(function() {
  743. parentView.append();
  744. });
  745. ok(firstGrandchild(parentView).isAlsoCustom, "uses the example view class specified in the #collection helper");
  746. Ember.run(function() {
  747. parentView.destroy();
  748. });
  749. });
  750. test("should update boundIf blocks if the conditional changes", function() {
  751. container.register('template', 'foo', Ember.Handlebars.compile('<h1 id="first">{{#boundIf "view.content.myApp.isEnabled"}}{{view.content.wham}}{{/boundIf}}</h1>'));
  752. view = Ember.View.create({
  753. container: container,
  754. templateName: 'foo',
  755. content: Ember.Object.create({
  756. wham: 'bam',
  757. thankYou: "ma'am",
  758. myApp: Ember.Object.create({
  759. isEnabled: true
  760. })
  761. })
  762. });
  763. appendView();
  764. equal(view.$('#first').text(), "bam", "renders block when condition is true");
  765. Ember.run(function() {
  766. set(get(view, 'content'), 'myApp.isEnabled', false);
  767. });
  768. equal(view.$('#first').text(), "", "re-renders without block when condition is false");
  769. Ember.run(function() {
  770. set(get(view, 'content'), 'myApp.isEnabled', true);
  771. });
  772. equal(view.$('#first').text(), "bam", "re-renders block when condition changes to true");
  773. });
  774. test("should not update boundIf if truthiness does not change", function() {
  775. var renderCount = 0;
  776. view = Ember.View.create({
  777. template: Ember.Handlebars.compile('<h1 id="first">{{#boundIf "view.shouldDisplay"}}{{view view.InnerViewClass}}{{/boundIf}}</h1>'),
  778. shouldDisplay: true,
  779. InnerViewClass: Ember.View.extend({
  780. template: Ember.Handlebars.compile("bam"),
  781. render: function() {
  782. renderCount++;
  783. return this._super.apply(this, arguments);
  784. }
  785. })
  786. });
  787. appendView();
  788. equal(renderCount, 1, "precond - should have rendered once");
  789. equal(view.$('#first').text(), "bam", "renders block when condition is true");
  790. Ember.run(function() {
  791. set(view, 'shouldDisplay', 1);
  792. });
  793. equal(renderCount, 1, "should not have rerendered");
  794. equal(view.$('#first').text(), "bam", "renders block when condition is true");
  795. });
  796. test("boundIf should support parent access", function(){
  797. view = Ember.View.create({
  798. template: Ember.Handlebars.compile(
  799. '<h1 id="first">{{#with view.content}}{{#with thankYou}}'+
  800. '{{#boundIf ../view.show}}parent{{/boundIf}}-{{#boundIf ../../view.show}}grandparent{{/boundIf}}'+
  801. '{{/with}}{{/with}}</h1>'
  802. ),
  803. content: Ember.Object.create({
  804. show: true,
  805. thankYou: Ember.Object.create()
  806. }),
  807. show: true
  808. });
  809. appendView();
  810. equal(view.$('#first').text(), "parent-grandparent", "renders boundIfs using ..");
  811. });
  812. test("{{view}} id attribute should set id on layer", function() {
  813. container.register('template', 'foo', Ember.Handlebars.compile('{{#view "TemplateTests.IdView" id="bar"}}baz{{/view}}'));
  814. TemplateTests.IdView = Ember.View;
  815. view = Ember.View.create({
  816. container: container,
  817. templateName: 'foo'
  818. });
  819. appendView();
  820. equal(view.$('#bar').length, 1, "adds id attribute to layer");
  821. equal(view.$('#bar').text(), 'baz', "emits content");
  822. });
  823. test("{{view}} class attribute should set class on layer", function() {
  824. container.register('template', 'foo', Ember.Handlebars.compile('{{#view "TemplateTests.IdView" class="bar"}}baz{{/view}}'));
  825. TemplateTests.IdView = Ember.View;
  826. view = Ember.View.create({
  827. container: container,
  828. templateName: 'foo'
  829. });
  830. appendView();
  831. equal(view.$('.bar').length, 1, "adds class attribute to layer");
  832. equal(view.$('.bar').text(), 'baz', "emits content");
  833. });
  834. test("{{view}} should not allow attributeBindings to be set", function() {
  835. raises(function() {
  836. view = Ember.View.create({
  837. template: Ember.Handlebars.compile('{{view "Ember.View" attributeBindings="one two"}}')
  838. });
  839. appendView();
  840. }, /Setting 'attributeBindings' via Handlebars is not allowed/, "should raise attributeBindings error");
  841. });
  842. test("{{view}} should be able to point to a local view", function() {
  843. view = Ember.View.create({
  844. template: Ember.Handlebars.compile("{{view view.common}}"),
  845. common: Ember.View.extend({
  846. template: Ember.Handlebars.compile("common")
  847. })
  848. });
  849. appendView();
  850. equal(view.$().text(), "common", "tries to look up view name locally");
  851. });
  852. test("{{view}} should evaluate class bindings set to global paths", function() {
  853. var App;
  854. Ember.run(function() {
  855. lookup.App = App = Ember.Application.create({
  856. isApp: true,
  857. isGreat: true,
  858. directClass: "app-direct",
  859. isEnabled: true
  860. });
  861. });
  862. view = Ember.View.create({
  863. template: Ember.Handlebars.compile('{{view Ember.TextField class="unbound" classBinding="App.isGreat:great App.directClass App.isApp App.isEnabled:enabled:disabled"}}')
  864. });
  865. appendView();
  866. ok(view.$('input').hasClass('unbound'), "sets unbound classes directly");
  867. ok(view.$('input').hasClass('great'), "evaluates classes bound to global paths");
  868. ok(view.$('input').hasClass('app-direct'), "evaluates classes bound directly to global paths");
  869. ok(view.$('input').hasClass('is-app'), "evaluates classes bound directly to booleans in global paths - dasherizes and sets class when true");
  870. ok(view.$('input').hasClass('enabled'), "evaluates ternary operator in classBindings");
  871. ok(!view.$('input').hasClass('disabled'), "evaluates ternary operator in classBindings");
  872. Ember.run(function() {
  873. App.set('isApp', false);
  874. App.set('isEnabled', false);
  875. });
  876. ok(!view.$('input').hasClass('is-app'), "evaluates classes bound directly to booleans in global paths - removes class when false");
  877. ok(!view.$('input').hasClass('enabled'), "evaluates ternary operator in classBindings");
  878. ok(view.$('input').hasClass('disabled'), "evaluates ternary operator in classBindings");
  879. Ember.run(function() {
  880. lookup.App.destroy();
  881. });
  882. });
  883. test("{{view}} should evaluate class bindings set in the current context", function() {
  884. view = Ember.View.create({
  885. isView: true,
  886. isEditable: true,
  887. directClass: "view-direct",
  888. isEnabled: true,
  889. template: Ember.Handlebars.compile('{{view Ember.TextField class="unbound" classBinding="view.isEditable:editable view.directClass view.isView view.isEnabled:enabled:disabled"}}')
  890. });
  891. appendView();
  892. ok(view.$('input').hasClass('unbound'), "sets unbound classes directly");
  893. ok(view.$('input').hasClass('editable'), "evaluates classes bound in the current context");
  894. ok(view.$('input').hasClass('view-direct'), "evaluates classes bound directly in the current context");
  895. ok(view.$('input').hasClass('is-view'), "evaluates classes bound directly to booleans in the current context - dasherizes and sets class when true");
  896. ok(view.$('input').hasClass('enabled'), "evaluates ternary operator in classBindings");
  897. ok(!view.$('input').hasClass('disabled'), "evaluates ternary operator in classBindings");
  898. Ember.run(function() {
  899. view.set('isView', false);
  900. view.set('isEnabled', false);
  901. });
  902. ok(!view.$('input').hasClass('is-view'), "evaluates classes bound directly to booleans in the current context - removes class when false");
  903. ok(!view.$('input').hasClass('enabled'), "evaluates ternary operator in classBindings");
  904. ok(view.$('input').hasClass('disabled'), "evaluates ternary operator in classBindings");
  905. });
  906. test("{{view}} should evaluate class bindings set with either classBinding or classNameBindings", function() {
  907. var App;
  908. Ember.run(function() {
  909. lookup.App = App = Ember.Application.create({
  910. isGreat: true,
  911. isEnabled: true
  912. });
  913. });
  914. view = Ember.View.create({
  915. template: Ember.Handlebars.compile('{{view Ember.TextField class="unbound" classBinding="App.isGreat:great App.isEnabled:enabled:disabled" classNameBindings="App.isGreat:really-great App.isEnabled:really-enabled:really-disabled"}}')
  916. });
  917. appendView();
  918. ok(view.$('input').hasClass('unbound'), "sets unbound classes directly");
  919. ok(view.$('input').hasClass('great'), "evaluates classBinding");
  920. ok(view.$('input').hasClass('really-great'), "evaluates classNameBinding");
  921. ok(view.$('input').hasClass('enabled'), "evaluates ternary operator in classBindings");
  922. ok(view.$('input').hasClass('really-enabled'), "evaluates ternary operator in classBindings");
  923. ok(!view.$('input').hasClass('disabled'), "evaluates ternary operator in classBindings");
  924. ok(!view.$('input').hasClass('really-disabled'), "evaluates ternary operator in classBindings");
  925. Ember.run(function() {
  926. App.set('isEnabled', false);
  927. });
  928. ok(!view.$('input').hasClass('enabled'), "evaluates ternary operator in classBindings");
  929. ok(!view.$('input').hasClass('really-enabled'), "evaluates ternary operator in classBindings");
  930. ok(view.$('input').hasClass('disabled'), "evaluates ternary operator in classBindings");
  931. ok(view.$('input').hasClass('really-disabled'), "evaluates ternary operator in classBindings");
  932. Ember.run(function() {
  933. lookup.App.destroy();
  934. });
  935. });
  936. test("{{view}} should evaluate other attribute bindings set to global paths", function() {
  937. Ember.run(function() {
  938. lookup.App = Ember.Application.create({
  939. name: "myApp"
  940. });
  941. });
  942. view = Ember.View.create({
  943. template: Ember.Handlebars.compile('{{view Ember.TextField valueBinding="App.name"}}')
  944. });
  945. appendView();
  946. equal(view.$('input').attr('value'), "myApp", "evaluates attributes bound to global paths");
  947. Ember.run(function() {
  948. lookup.App.destroy();
  949. });
  950. });
  951. test("{{view}} should evaluate other attributes bindings set in the current context", function() {
  952. view = Ember.View.create({
  953. name: "myView",
  954. template: Ember.Handlebars.compile('{{view Ember.TextField valueBinding="view.name"}}')
  955. });
  956. appendView();
  957. equal(view.$('input').attr('value'), "myView", "evaluates attributes bound in the current context");
  958. });
  959. test("{{view}} should be able to bind class names to truthy properties", function() {
  960. container.register('template', 'template', Ember.Handlebars.compile('{{#view "TemplateTests.classBindingView" classBinding="view.number:is-truthy"}}foo{{/view}}'));
  961. TemplateTests.classBindingView = Ember.View.extend();
  962. view = Ember.View.create({
  963. container: container,
  964. number: 5,
  965. templateName: 'template'
  966. });
  967. appendView();
  968. equal(view.$('.is-truthy').length, 1, "sets class name");
  969. Ember.run(function() {
  970. set(view, 'number', 0);
  971. });
  972. equal(view.$('.is-truthy').length, 0, "removes class name if bound property is set to falsey");
  973. });
  974. test("{{view}} should be able to bind class names to truthy or falsy properties", function() {
  975. container.register('template', 'template', Ember.Handlebars.compile('{{#view "TemplateTests.classBindingView" classBinding="view.number:is-truthy:is-falsy"}}foo{{/view}}'));
  976. TemplateTests.classBindingView = Ember.View.extend();
  977. view = Ember.View.create({
  978. container: container,
  979. number: 5,
  980. templateName: 'template'
  981. });
  982. appendView();
  983. equal(view.$('.is-truthy').length, 1, "sets class name to truthy value");
  984. equal(view.$('.is-falsy').length, 0, "doesn't set class name to falsy value");
  985. Ember.run(function() {
  986. set(view, 'number', 0);
  987. });
  988. equal(view.$('.is-truthy').length, 0, "doesn't set class name to truthy value");
  989. equal(view.$('.is-falsy').length, 1, "sets class name to falsy value");
  990. });
  991. test("should be able to bind element attributes using {{bindAttr}}", function() {
  992. var template = Ember.Handlebars.compile('<img {{bindAttr src="view.content.url" alt="view.content.title"}}>');
  993. view = Ember.View.create({
  994. template: template,
  995. content: Ember.Object.create({
  996. url: "http://www.emberjs.com/assets/images/logo.png",
  997. title: "The SproutCore Logo"
  998. })
  999. });
  1000. appendView();
  1001. equal(view.$('img').attr('src'), "http://www.emberjs.com/assets/images/logo.png", "sets src attribute");
  1002. equal(view.$('img').attr('alt'), "The SproutCore Logo", "sets alt attribute");
  1003. Ember.run(function() {
  1004. set(view, 'content.title', "El logo de Eember");
  1005. });
  1006. equal(view.$('img').attr('alt'), "El logo de Eember", "updates alt attribute when content's title attribute changes");
  1007. Ember.run(function() {
  1008. set(view, 'content', Ember.Object.create({
  1009. url: "http://www.thegooglez.com/theydonnothing",
  1010. title: "I CAN HAZ SEARCH"
  1011. }));
  1012. });
  1013. equal(view.$('img').attr('alt'), "I CAN HAZ SEARCH", "updates alt attribute when content object changes");
  1014. Ember.run(function() {
  1015. set(view, 'content', {
  1016. url: "http://www.emberjs.com/assets/images/logo.png",
  1017. title: "The SproutCore Logo"
  1018. });
  1019. });
  1020. equal(view.$('img').attr('alt'), "The SproutCore Logo", "updates alt attribute when content object is a hash");
  1021. Ember.run(function() {
  1022. set(view, 'content', Ember.Object.createWithMixins({
  1023. url: "http://www.emberjs.com/assets/images/logo.png",
  1024. title: Ember.computed(function() {
  1025. return "Nanananana Ember!";
  1026. })
  1027. }));
  1028. });
  1029. equal(view.$('img').attr('alt'), "Nanananana Ember!", "updates alt attribute when title property is computed");
  1030. });
  1031. test("should be able to bind to view attributes with {{bindAttr}}", function() {
  1032. view = Ember.View.create({
  1033. value: 'Test',
  1034. template: Ember.Handlebars.compile('<img src="test.jpg" {{bindAttr alt="view.value"}}>')
  1035. });
  1036. appendView();
  1037. equal(view.$('img').attr('alt'), "Test", "renders initial value");
  1038. Ember.run(function() {
  1039. view.set('value', 'Updated');
  1040. });
  1041. equal(view.$('img').attr('alt'), "Updated", "updates value");
  1042. });
  1043. test("should be able to bind to globals with {{bindAttr}}", function() {
  1044. TemplateTests.set('value', 'Test');
  1045. view = Ember.View.create({
  1046. template: Ember.Handlebars.compile('<img src="test.jpg" {{bindAttr alt="TemplateTests.value"}}>')
  1047. });
  1048. appendView();
  1049. equal(view.$('img').attr('alt'), "Test", "renders initial value");
  1050. Ember.run(function() {
  1051. TemplateTests.set('value', 'Updated');
  1052. });
  1053. equal(view.$('img').attr('alt'), "Updated", "updates value");
  1054. });
  1055. test("should not allow XSS injection via {{bindAttr}}", function() {
  1056. view = Ember.View.create({
  1057. template: Ember.Handlebars.compile('<img src="test.jpg" {{bindAttr alt="view.content.value"}}>'),
  1058. content: {
  1059. value: 'Trololol" onmouseover="alert(\'HAX!\');'
  1060. }
  1061. });
  1062. appendView();
  1063. equal(view.$('img').attr('onmouseover'), undefined);
  1064. // If the whole string is here, then it means we got properly escaped
  1065. equal(view.$('img').attr('alt'), 'Trololol" onmouseover="alert(\'HAX!\');');
  1066. });
  1067. test("should be able to bind use {{bindAttr}} more than once on an element", function() {
  1068. var template = Ember.Handlebars.compile('<img {{bindAttr src="view.content.url"}} {{bindAttr alt="view.content.title"}}>');
  1069. view = Ember.View.create({
  1070. template: template,
  1071. content: Ember.Object.create({
  1072. url: "http://www.emberjs.com/assets/images/logo.png",
  1073. title: "The SproutCore Logo"
  1074. })
  1075. });
  1076. appendView();
  1077. equal(view.$('img').attr('src'), "http://www.emberjs.com/assets/images/logo.png", "sets src attribute");
  1078. equal(view.$('img').attr('alt'), "The SproutCore Logo", "sets alt attribute");
  1079. Ember.run(function() {
  1080. set(view, 'content.title', "El logo de Eember");
  1081. });
  1082. equal(view.$('img').attr('alt'), "El logo de Eember", "updates alt attribute when content's title attribute changes");
  1083. Ember.run(function() {
  1084. set(view, 'content', Ember.Object.create({
  1085. url: "http://www.thegooglez.com/theydonnothing",
  1086. title: "I CAN HAZ SEARCH"
  1087. }));
  1088. });
  1089. equal(view.$('img').attr('alt'), "I CAN HAZ SEARCH", "updates alt attribute when content object changes");
  1090. Ember.run(function() {
  1091. set(view, 'content', {
  1092. url: "http://www.emberjs.com/assets/images/logo.png",
  1093. title: "The SproutCore Logo"
  1094. });
  1095. });
  1096. equal(view.$('img').attr('alt'), "The SproutCore Logo", "updates alt attribute when content object is a hash");
  1097. Ember.run(function() {
  1098. set(view, 'content', Ember.Object.createWithMixins({
  1099. url: "http://www.emberjs.com/assets/images/logo.png",
  1100. title: Ember.computed(function() {
  1101. return "Nanananana Ember!";
  1102. })
  1103. }));
  1104. });
  1105. equal(view.$('img').attr('alt'), "Nanananana Ember!", "updates alt attribute when title property is computed");
  1106. });
  1107. test("should not reset cursor position when text field receives keyUp event", function() {
  1108. view = Ember.TextField.create({
  1109. value: "Broseidon, King of the Brocean"
  1110. });
  1111. Ember.run(function() {
  1112. view.append();
  1113. });
  1114. view.$().val('Brosiedoon, King of the Brocean');
  1115. view.$().setCaretPosition(5);
  1116. Ember.run(function() {
  1117. view.trigger('keyUp', {});
  1118. });
  1119. equal(view.$().caretPosition(), 5, "The keyUp event should not result in the cursor being reset due to the bindAttr observers");
  1120. Ember.run(function() {
  1121. view.destroy();
  1122. });
  1123. });
  1124. test("should be able to bind element attributes using {{bindAttr}} inside a block", function() {
  1125. var template = Ember.Handlebars.compile('{{#with view.content}}<img {{bindAttr src="url" alt="title"}}>{{/with}}');
  1126. view = Ember.View.create({
  1127. template: template,
  1128. content: Ember.Object.create({
  1129. url: "http://www.emberjs.com/assets/images/logo.png",
  1130. title: "The SproutCore Logo"
  1131. })
  1132. });
  1133. appendView();
  1134. equal(view.$('img').attr('src'), "http://www.emberjs.com/assets/images/logo.png", "sets src attribute");
  1135. equal(view.$('img').attr('alt'), "The SproutCore Logo", "sets alt attribute");
  1136. Ember.run(function() {
  1137. set(view, 'content.title', "El logo de Eember");
  1138. });
  1139. equal(view.$('img').attr('alt'), "El logo de Eember", "updates alt attribute when content's title attribute changes");
  1140. });
  1141. test("should be able to bind class attribute with {{bindAttr}}", function() {
  1142. var template = Ember.Handlebars.compile('<img {{bindAttr class="view.foo"}}>');
  1143. view = Ember.View.create({
  1144. template: template,
  1145. foo: 'bar'
  1146. });
  1147. appendView();
  1148. equal(view.$('img').attr('class'), 'bar', "renders class");
  1149. Ember.run(function() {
  1150. set(view, 'foo', 'baz');
  1151. });
  1152. equal(view.$('img').attr('class'), 'baz', "updates class");
  1153. });
  1154. test("should be able to bind class attribute via a truthy property with {{bindAttr}}", function() {
  1155. var template = Ember.Handlebars.compile('<img {{bindAttr class="view.isNumber:is-truthy"}}>');
  1156. view = Ember.View.create({
  1157. template: template,
  1158. isNumber: 5
  1159. });
  1160. appendView();
  1161. equal(view.$('.is-truthy').length, 1, "sets class name");
  1162. Ember.run(function() {
  1163. set(view, 'isNumber', 0);
  1164. });
  1165. equal(view.$('.is-truthy').length, 0, "removes class name if bound property is set to something non-truthy");
  1166. });
  1167. test("should be able to bind class to view attribute with {{bindAttr}}", function() {
  1168. var template = Ember.Handlebars.compile('<img {{bindAttr class="view.foo"}}>');
  1169. view = Ember.View.create({
  1170. template: template,
  1171. foo: 'bar'
  1172. });
  1173. appendView();
  1174. equal(view.$('img').attr('class'), 'bar', "renders class");
  1175. Ember.run(function() {
  1176. set(view, 'foo', 'baz');
  1177. });
  1178. equal(view.$('img').attr('class'), 'baz', "updates class");
  1179. });
  1180. test("should not allow XSS injection via {{bindAttr}} with class", function() {
  1181. view = Ember.View.create({
  1182. template: Ember.Handlebars.compile('<img {{bindAttr class="view.foo"}}>'),
  1183. foo: '" onmouseover="alert(\'I am in your classes hacking your app\');'
  1184. });
  1185. appendView();
  1186. equal(view.$('img').attr('onmouseover'), undefined);
  1187. // If the whole string is here, then it means we got properly escaped
  1188. equal(view.$('img').attr('class'), '" onmouseover="alert(\'I am in your classes hacking your app\');');
  1189. });
  1190. test("should be able to bind class attribute using ternary operator in {{bindAttr}}", function() {
  1191. var template = Ember.Handlebars.compile('<img {{bindAttr class="view.content.isDisabled:disabled:enabled"}} />');
  1192. var content = Ember.Object.create({
  1193. isDisabled: true
  1194. });
  1195. view = Ember.View.create({
  1196. template: template,
  1197. content: content
  1198. });
  1199. appendView();
  1200. ok(view.$('img').hasClass('disabled'), 'disabled class is rendered');
  1201. ok(!view.$('img').hasClass('enabled'), 'enabled class is not rendered');
  1202. Ember.run(function() {
  1203. set(content, 'isDisabled', false);
  1204. });
  1205. ok(!view.$('img').hasClass('disabled'), 'disabled class is not rendered');
  1206. ok(view.$('img').hasClass('enabled'), 'enabled class is rendered');
  1207. });
  1208. test("should be able to add multiple classes using {{bindAttr class}}", function() {
  1209. var template = Ember.Handlebars.compile('<div {{bindAttr class="view.content.isAwesomeSauce view.content.isAlsoCool view.content.isAmazing:amazing :is-super-duper view.content.isEnabled:enabled:disabled"}}></div>');
  1210. var content = Ember.Object.create({
  1211. isAwesomeSauce: true,
  1212. isAlsoCool: true,
  1213. isAmazing: true,
  1214. isEnabled: true
  1215. });
  1216. view = Ember.View.create({
  1217. template: template,
  1218. content: content
  1219. });
  1220. appendView();
  1221. ok(view.$('div').hasClass('is-awesome-sauce'), "dasherizes first property and sets classname");
  1222. ok(view.$('div').hasClass('is-also-cool'), "dasherizes second property and sets classname");
  1223. ok(view.$('div').hasClass('amazing'), "uses alias for third property and sets classname");
  1224. ok(view.$('div').hasClass('is-super-duper'), "static class is present");
  1225. ok(view.$('div').hasClass('enabled'), "truthy class in ternary classname definition is rendered");
  1226. ok(!view.$('div').hasClass('disabled'), "falsy class in ternary classname definition is not rendered");
  1227. Ember.run(function() {
  1228. set(content, 'isAwesomeSauce', false);
  1229. set(content, 'isAmazing', false);
  1230. set(content, 'isEnabled', false);
  1231. });
  1232. ok(!view.$('div').hasClass('is-awesome-sauce'), "removes dasherized class when property is set to false");
  1233. ok(!view.$('div').hasClass('amazing'), "removes aliased class when property is set to false");
  1234. ok(view.$('div').hasClass('is-super-duper'), "static class is still present");
  1235. ok(!view.$('div').hasClass('enabled'), "truthy class in ternary classname definition is not rendered");
  1236. ok(view.$('div').hasClass('disabled'), "falsy class in ternary classname definition is rendered");
  1237. });
  1238. test("should be able to bind classes to globals with {{bindAttr class}}", function() {
  1239. TemplateTests.set('isOpen', true);
  1240. view = Ember.View.create({
  1241. template: Ember.Handlebars.compile('<img src="test.jpg" {{bindAttr class="TemplateTests.isOpen"}}>')
  1242. });
  1243. appendView();
  1244. ok(view.$('img').hasClass('is-open'), "sets classname to the dasherized value of the global property");
  1245. Ember.run(function() {
  1246. TemplateTests.set('isOpen', false);
  1247. });
  1248. ok(!view.$('img').hasClass('is-open'), "removes the classname when the global property has changed");
  1249. });
  1250. test("should be able to bindAttr to 'this' in an {{#each}} block", function() {
  1251. view = Ember.View.create({
  1252. template: Ember.Handlebars.compile('{{#each view.images}}<img {{bindAttr src="this"}}>{{/each}}'),
  1253. images: Ember.A(['one.png', 'two.jpg', 'three.gif'])
  1254. });
  1255. appendView();
  1256. var images = view.$('img');
  1257. ok(/one\.png$/.test(images[0].src));
  1258. ok(/two\.jpg$/.test(images[1].src));
  1259. ok(/three\.gif$/.test(images[2].src));
  1260. });
  1261. test("should be able to bind classes to 'this' in an {{#each}} block with {{bindAttr class}}", function() {
  1262. view = Ember.View.create({
  1263. template: Ember.Handlebars.compile('{{#each view.items}}<li {{bindAttr class="this"}}>Item</li>{{/each}}'),
  1264. items: Ember.A(['a', 'b', 'c'])
  1265. });
  1266. appendView();
  1267. ok(view.$('li').eq(0).hasClass('a'), "sets classname to the value of the first item");
  1268. ok(view.$('li').eq(1).hasClass('b'), "sets classname to the value of the second item");
  1269. ok(view.$('li').eq(2).hasClass('c'), "sets classname to the value of the third item");
  1270. });
  1271. test("should be able to bindAttr to var in {{#each var in list}} block", function() {
  1272. view = Ember.View.create({
  1273. template: Ember.Handlebars.compile('{{#each image in view.images}}<img {{bindAttr src="image"}}>{{/each}}'),
  1274. images: Ember.A(['one.png', 'two.jpg', 'three.gif'])
  1275. });
  1276. appendView();
  1277. var images = view.$('img');
  1278. ok(/one\.png$/.test(images[0].src));
  1279. ok(/two\.jpg$/.test(images[1].src));
  1280. ok(/three\.gif$/.test(images[2].src));
  1281. Ember.run(function() {
  1282. var imagesArray = view.get('images');
  1283. imagesArray.removeAt(0);
  1284. });
  1285. images = view.$('img');
  1286. ok(images.length === 2, "");
  1287. ok(/two\.jpg$/.test(images[0].src));
  1288. ok(/three\.gif$/.test(images[1].src));
  1289. });
  1290. test("should be able to output a property without binding", function(){
  1291. var context = {
  1292. content: Ember.Object.create({
  1293. anUnboundString: "No spans here, son."
  1294. }),
  1295. anotherUnboundString: "Not here, either."
  1296. };
  1297. view = Ember.View.create({
  1298. context: context,
  1299. template: Ember.Handlebars.compile(
  1300. '<div id="first">{{unbound content.anUnboundString}}</div>'+
  1301. '{{#with content}}<div id="second">{{unbound ../anotherUnboundString}}</div>{{/with}}'
  1302. )
  1303. });
  1304. appendView();
  1305. equal(view.$('#first').html(), "No spans here, son.");
  1306. equal(view.$('#second').html(), "Not here, either.");
  1307. });
  1308. test("should allow standard Handlebars template usage", function() {
  1309. view = Ember.View.create({
  1310. context: { name: "Erik" },
  1311. template: Handlebars.compile("Hello, {{name}}")
  1312. });
  1313. Ember.run(function() {
  1314. view.appendTo('#qunit-fixture');
  1315. });
  1316. equal(view.$().text(), "Hello, Erik");
  1317. });
  1318. test("should be able to use standard Handlebars #each helper", function() {
  1319. view = Ember.View.create({
  1320. context: { items: ['a', 'b', 'c'] },
  1321. template: Handlebars.compile("{{#each items}}{{this}}{{/each}}")
  1322. });
  1323. Ember.run(function() {
  1324. view.appendTo('#qunit-fixture');
  1325. });
  1326. equal(view.$().html(), "abc");
  1327. });
  1328. test("should be able to use unbound helper in #each helper", function() {
  1329. view = Ember.View.create({
  1330. items: Ember.A(['a', 'b', 'c', 1, 2, 3]),
  1331. template: Ember.Handlebars.compile(
  1332. "<ul>{{#each view.items}}<li>{{unbound this}}</li>{{/each}}</ul>")
  1333. });
  1334. appendView();
  1335. equal(view.$().text(), "abc123");
  1336. equal(view.$('li').children().length, 0, "No markers");
  1337. });
  1338. test("should be able to use unbound helper in #each helper (with objects)", function() {
  1339. view = Ember.View.create({
  1340. items: Ember.A([{wham: 'bam'}, {wham: 1}]),
  1341. template: Ember.Handlebars.compile(
  1342. "<ul>{{#each view.items}}<li>{{unbound wham}}</li>{{/each}}</ul>")
  1343. });
  1344. appendView();
  1345. equal(view.$().text(), "bam1");
  1346. equal(view.$('li').children().length, 0, "No markers");
  1347. });
  1348. test("should work with precompiled templates", function() {
  1349. var templateString = Ember.Handlebars.precompile("{{view.value}}"),
  1350. compiledTemplate = Ember.Handlebars.template(eval(templateString));
  1351. view = Ember.View.create({
  1352. value: "rendered",
  1353. template: compiledTemplate
  1354. });
  1355. appendView();
  1356. equal(view.$().text(), "rendered", "the precompiled template was rendered");
  1357. Ember.run(function() { view.set('value', 'updated'); });
  1358. equal(view.$().text(), "updated", "the precompiled template was updated");
  1359. });
  1360. test("should expose a controller keyword when present on the view", function() {
  1361. var templateString = "{{controller.foo}}{{#view}}{{controller.baz}}{{/view}}";
  1362. view = Ember.View.create({
  1363. controller: Ember.Object.create({
  1364. foo: "bar",
  1365. baz: "bang"
  1366. }),
  1367. template: Ember.Handlebars.compile(templateString)
  1368. });
  1369. Ember.run(function() {
  1370. view.appendTo("#qunit-fixture");
  1371. });
  1372. equal(view.$().text(), "barbang", "renders values from controller and parent controller");
  1373. var controller = get(view, 'controller');
  1374. Ember.run(function() {
  1375. controller.set('foo', "BAR");
  1376. controller.set('baz', "BLARGH");
  1377. });
  1378. equal(view.$().text(), "BARBLARGH", "updates the DOM when a bound value is updated");
  1379. Ember.run(function() {
  1380. view.destroy();
  1381. });
  1382. view = Ember.View.create({
  1383. controller: "aString",
  1384. template: Ember.Handlebars.compile("{{controller}}")
  1385. });
  1386. Ember.run(function() {
  1387. view.appendTo('#qunit-fixture');
  1388. });
  1389. equal(view.$().text(), "aString", "renders the controller itself if no additional path is specified");
  1390. });
  1391. test("should expose a controller keyword that can be used in conditionals", function() {
  1392. var templateString = "{{#view}}{{#if controller}}{{controller.foo}}{{/if}}{{/view}}";
  1393. view = Ember.View.create({
  1394. controller: Ember.Object.create({
  1395. foo: "bar"
  1396. }),
  1397. template: Ember.Handlebars.compile(templateString)
  1398. });
  1399. Ember.run(function() {
  1400. view.appendTo("#qunit-fixture");
  1401. });
  1402. equal(view.$().text(), "bar", "renders values from controller and parent controller");
  1403. Ember.run(function() {
  1404. view.set('controller', null);
  1405. });
  1406. equal(view.$().text(), "", "updates the DOM when the controller is changed");
  1407. });
  1408. test("should expose a controller keyword that persists through Ember.ContainerView", function() {
  1409. var templateString = "{{view Ember.ContainerView}}";
  1410. view = Ember.View.create({
  1411. controller: Ember.Object.create({
  1412. foo: "bar"
  1413. }),
  1414. template: Ember.Handlebars.compile(templateString)
  1415. });
  1416. Ember.run(function() {
  1417. view.appendTo("#qunit-fixture");
  1418. });
  1419. var containerView = get(view, 'childViews.firstObject');
  1420. var viewInstanceToBeInserted = Ember.View.create({
  1421. template: Ember.Handlebars.compile('{{controller.foo}}')
  1422. });
  1423. Ember.run(function() {
  1424. containerView.pushObject(viewInstanceToBeInserted);
  1425. });
  1426. equal(trim(viewInstanceToBeInserted.$().text()), "bar", "renders value from parent's controller");
  1427. });
  1428. test("should expose a view keyword", function() {
  1429. var templateString = '{{#with view.differentContent}}{{view.foo}}{{#view baz="bang"}}{{view.baz}}{{/view}}{{/with}}';
  1430. view = Ember.View.create({
  1431. differentContent: {
  1432. view: {
  1433. foo: "WRONG",
  1434. baz: "WRONG"
  1435. }
  1436. },
  1437. foo: "bar",
  1438. template: Ember.Handlebars.compile(templateString)
  1439. });
  1440. Ember.run(function() {
  1441. view.appendTo("#qunit-fixture");
  1442. });
  1443. equal(view.$().text(), "barbang", "renders values from view and child view");
  1444. });
  1445. test("Ember.Button targets should respect keywords", function() {
  1446. Ember.TESTING_DEPRECATION = true;
  1447. try {
  1448. var templateString = '{{#with view.anObject}}{{view Ember.Button target="controller.foo"}}{{/with}}';
  1449. view = Ember.View.create({
  1450. template: Ember.Handlebars.compile(templateString),
  1451. anObject: {},
  1452. controller: {
  1453. foo: "bar"
  1454. }
  1455. });
  1456. Ember.run(function() {
  1457. view.appendTo('#qunit-fixture');
  1458. });
  1459. var button = view.get('childViews').objectAt(0);
  1460. equal(button.get('targetObject'), "bar", "resolves the target");
  1461. } finally {
  1462. Ember.TESTING_DEPRECATION = false;
  1463. }
  1464. });
  1465. test("should be able to explicitly set a view's context", function() {
  1466. var context = Ember.Object.create({
  1467. test: 'test'
  1468. });
  1469. TemplateTests.CustomContextView = Ember.View.extend({
  1470. context: context,
  1471. template: Ember.Handlebars.compile("{{test}}")
  1472. });
  1473. view = Ember.View.create({
  1474. template: Ember.Handlebars.compile("{{view TemplateTests.CustomContextView}}")
  1475. });
  1476. appendView();
  1477. equal(view.$().text(), "test");
  1478. });
  1479. module("Ember.View - handlebars integration", {
  1480. setup: function() {
  1481. Ember.lookup = lookup = { Ember: Ember };
  1482. originalLog = Ember.Logger.log;
  1483. logCalls = [];
  1484. Ember.Logger.log = function(arg) { logCalls.push(arg); };
  1485. },
  1486. teardown: function() {
  1487. if (view) {
  1488. Ember.run(function() {
  1489. view.destroy();
  1490. });
  1491. view = null;
  1492. }
  1493. Ember.Logger.log = originalLog;
  1494. Ember.lookup = originalLookup;
  1495. }
  1496. });
  1497. test("should be able to log a property", function(){
  1498. var context = {
  1499. value: 'one',
  1500. valueTwo: 'two',
  1501. content: Ember.Object.create({})
  1502. };
  1503. view = Ember.View.create({
  1504. context: context,
  1505. template: Ember.Handlebars.compile('{{log value}}{{#with content}}{{log ../valueTwo}}{{/with}}')
  1506. });
  1507. appendView();
  1508. equal(view.$().text(), "", "shouldn't render any text");
  1509. equal(logCalls[0], 'one', "should call log with value");
  1510. equal(logCalls[1], 'two', "should call log with valueTwo");
  1511. });
  1512. test("should be able to log a view property", function() {
  1513. view = Ember.View.create({
  1514. template: Ember.Handlebars.compile('{{log view.value}}'),
  1515. value: 'one'
  1516. });
  1517. appendView();
  1518. equal(view.$().text(), "", "shouldn't render any text");
  1519. equal(logCalls[0], 'one', "should call log with value");
  1520. });
  1521. test("should be able to log `this`", function() {
  1522. view = Ember.View.create({
  1523. template: Ember.Handlebars.compile('{{#each view.items}}{{log this}}{{/each}}'),
  1524. items: Ember.A(['one', 'two'])
  1525. });
  1526. appendView();
  1527. equal(view.$().text(), "", "shouldn't render any text");
  1528. equal(logCalls[0], 'one', "should call log with item one");
  1529. equal(logCalls[1], 'two', "should call log with item two");
  1530. });
  1531. var MyApp;
  1532. module("Templates redrawing and bindings", {
  1533. setup: function(){
  1534. Ember.lookup = lookup = { Ember: Ember };
  1535. MyApp = lookup.MyApp = Ember.Object.create({});
  1536. },
  1537. teardown: function(){
  1538. Ember.run(function() {
  1539. if (view) view.destroy();
  1540. });
  1541. Ember.lookup = originalLookup;
  1542. }
  1543. });
  1544. test("should be able to update when bound property updates", function(){
  1545. MyApp.set('controller', Ember.Object.create({name: 'first'}));
  1546. var View = Ember.View.extend({
  1547. template: Ember.Handlebars.compile('<i>{{view.value.name}}, {{view.computed}}</i>'),
  1548. valueBinding: 'MyApp.controller',
  1549. computed: Ember.computed(function(){
  1550. return this.get('value.name') + ' - computed';
  1551. }).property('value').volatile()
  1552. });
  1553. Ember.run(function(){
  1554. view = View.create();
  1555. });
  1556. appendView();
  1557. Ember.run(function(){
  1558. MyApp.set('controller', Ember.Object.create({
  1559. name: 'second'
  1560. }));
  1561. });
  1562. equal(view.get('computed'), "second - computed", "view computed properties correctly update");
  1563. equal(view.$('i').text(), 'second, second - computed', "view rerenders when bound properties change");
  1564. });
  1565. test("properties within an if statement should not fail on re-render", function(){
  1566. view = Ember.View.create({
  1567. template: Ember.Handlebars.compile('{{#if view.value}}{{view.value}}{{/if}}'),
  1568. value: null
  1569. });
  1570. appendView();
  1571. equal(view.$().text(), '');
  1572. Ember.run(function(){
  1573. view.set('value', 'test');
  1574. });
  1575. equal(view.$().text(), 'test');
  1576. Ember.run(function(){
  1577. view.set('value', null);
  1578. });
  1579. equal(view.$().text(), '');
  1580. });
  1581. test("views within an if statement should be sane on re-render", function(){
  1582. view = Ember.View.create({
  1583. template: Ember.Handlebars.compile('{{#if view.display}}{{view Ember.TextField}}{{/if}}'),
  1584. display: false
  1585. });
  1586. appendView();
  1587. equal(view.$('input').length, 0);
  1588. Ember.run(function(){
  1589. // Setting twice will trigger the observer twice, this is intentional
  1590. view.set('display', true);
  1591. view.set('display', 'yes');
  1592. });
  1593. var textfield = view.$('input');
  1594. equal(textfield.length, 1);
  1595. // Make sure the view is still registered in Ember.View.views
  1596. ok(Ember.View.views[textfield.attr('id')]);
  1597. });
  1598. test("the {{this}} helper should not fail on removal", function(){
  1599. view = Ember.View.create({
  1600. template: Ember.Handlebars.compile('{{#if view.show}}{{#each view.list}}{{this}}{{/each}}{{/if}}'),
  1601. show: true,
  1602. list: Ember.A(['a', 'b', 'c'])
  1603. });
  1604. appendView();
  1605. equal(view.$().text(), 'abc', "should start property - precond");
  1606. Ember.run(function(){
  1607. view.set('show', false);
  1608. });
  1609. equal(view.$().text(), '');
  1610. });
  1611. test("bindings should be relative to the current context", function() {
  1612. view = Ember.View.create({
  1613. museumOpen: true,
  1614. museumDetails: Ember.Object.create({
  1615. name: "SFMoMA",
  1616. price: 20
  1617. }),
  1618. museumView: Ember.View.extend({
  1619. template: Ember.Handlebars.compile('Name: {{view.name}} Price: ${{view.dollars}}')
  1620. }),
  1621. template: Ember.Handlebars.compile('{{#if view.museumOpen}} {{view view.museumView nameBinding="view.museumDetails.name" dollarsBinding="view.museumDetails.price"}} {{/if}}')
  1622. });
  1623. Ember.run(function() {
  1624. view.appendTo('#qunit-fixture');
  1625. });
  1626. equal(Ember.$.trim(view.$().text()), "Name: SFMoMA Price: $20", "should print baz twice");
  1627. });
  1628. test("bindings should respect keywords", function() {
  1629. view = Ember.View.create({
  1630. museumOpen: true,
  1631. controller: {
  1632. museumOpen: true,
  1633. museumDetails: Ember.Object.create({
  1634. name: "SFMoMA",
  1635. price: 20
  1636. })
  1637. },
  1638. museumView: Ember.View.extend({
  1639. template: Ember.Handlebars.compile('Name: {{view.name}} Price: ${{view.dollars}}')
  1640. }),
  1641. template: Ember.Handlebars.compile('{{#if view.museumOpen}}{{view view.museumView nameBinding="controller.museumDetails.name" dollarsBinding="controller.museumDetails.price"}}{{/if}}')
  1642. });
  1643. Ember.run(function() {
  1644. view.appendTo('#qunit-fixture');
  1645. });
  1646. equal(Ember.$.trim(view.$().text()), "Name: SFMoMA Price: $20", "should print baz twice");
  1647. });
  1648. test("bindings can be 'this', in which case they *are* the current context", function() {
  1649. view = Ember.View.create({
  1650. museumOpen: true,
  1651. museumDetails: Ember.Object.create({
  1652. name: "SFMoMA",
  1653. price: 20,
  1654. museumView: Ember.View.extend({
  1655. template: Ember.Handlebars.compile('Name: {{view.museum.name}} Price: ${{view.museum.price}}')
  1656. })
  1657. }),
  1658. template: Ember.Handlebars.compile('{{#if view.museumOpen}} {{#with view.museumDetails}}{{view museumView museumBinding="this"}} {{/with}}{{/if}}')
  1659. });
  1660. Ember.run(function() {
  1661. view.appendTo('#qunit-fixture');
  1662. });
  1663. equal(Ember.$.trim(view.$().text()), "Name: SFMoMA Price: $20", "should print baz twice");
  1664. });
  1665. // https://github.com/emberjs/ember.js/issues/120
  1666. test("should not enter an infinite loop when binding an attribute in Handlebars", function() {
  1667. expect(0);
  1668. var App;
  1669. Ember.run(function() {
  1670. lookup.App = App = Ember.Application.create();
  1671. });
  1672. App.test = Ember.Object.create({ href: 'test' });
  1673. App.Link = Ember.View.extend({
  1674. classNames: ['app-link'],
  1675. tagName: 'a',
  1676. attributeBindings: ['href'],
  1677. href: '#none',
  1678. click: function() {
  1679. return false;
  1680. }
  1681. });
  1682. var parentView = Ember.View.create({
  1683. template: Ember.Handlebars.compile('{{#view App.Link hrefBinding="App.test.href"}} Test {{/view}}')
  1684. });
  1685. Ember.run(function() {
  1686. parentView.appendTo('#qunit-fixture');
  1687. // App.Link.create().appendTo('#qunit-fixture');
  1688. });
  1689. // equal(view.$().attr('href'), 'test');
  1690. Ember.run(function() {
  1691. parentView.destroy();
  1692. });
  1693. Ember.run(function() {
  1694. lookup.App.destroy();
  1695. });
  1696. });
  1697. test("should render other templates using the {{template}} helper", function() {
  1698. Ember.TEMPLATES.sub_template = Ember.Handlebars.compile("sub-template");
  1699. view = Ember.View.create({
  1700. template: Ember.Handlebars.compile('This {{template "sub_template"}} is pretty great.')
  1701. });
  1702. Ember.run(function() {
  1703. view.appendTo('#qunit-fixture');
  1704. });
  1705. equal(Ember.$.trim(view.$().text()), "This sub-template is pretty great.");
  1706. });
  1707. test("should render other templates using the {{partial}} helper", function() {
  1708. Ember.TEMPLATES._subTemplate = Ember.Handlebars.compile("sub-template");
  1709. view = Ember.View.create({
  1710. template: Ember.Handlebars.compile('This {{partial "subTemplate"}} is pretty great.')
  1711. });
  1712. Ember.run(function() {
  1713. view.appendTo('#qunit-fixture');
  1714. });
  1715. equal(Ember.$.trim(view.$().text()), "This sub-template is pretty great.");
  1716. });
  1717. test("should render other slash-separated templates using the {{partial}} helper", function() {
  1718. Ember.TEMPLATES["child/_subTemplate"] = Ember.Handlebars.compile("sub-template");
  1719. view = Ember.View.create({
  1720. template: Ember.Handlebars.compile('This {{partial "child/subTemplate"}} is pretty great.')
  1721. });
  1722. Ember.run(function() {
  1723. view.appendTo('#qunit-fixture');
  1724. });
  1725. equal(Ember.$.trim(view.$().text()), "This sub-template is pretty great.");
  1726. });
  1727. test("should update bound values after the view is removed and then re-appended", function() {
  1728. view = Ember.View.create({
  1729. template: Ember.Handlebars.compile("{{#if view.showStuff}}{{view.boundValue}}{{else}}Not true.{{/if}}"),
  1730. showStuff: true,
  1731. boundValue: "foo"
  1732. });
  1733. Ember.run(function() {
  1734. view.appendTo('#qunit-fixture');
  1735. });
  1736. equal(Ember.$.trim(view.$().text()), "foo");
  1737. Ember.run(function() {
  1738. set(view, 'showStuff', false);
  1739. });
  1740. equal(Ember.$.trim(view.$().text()), "Not true.");
  1741. Ember.run(function() {
  1742. set(view, 'showStuff', true);
  1743. });
  1744. equal(Ember.$.trim(view.$().text()), "foo");
  1745. Ember.run(function() {
  1746. view.remove();
  1747. set(view, 'showStuff', false);
  1748. });
  1749. Ember.run(function() {
  1750. set(view, 'showStuff', true);
  1751. });
  1752. Ember.run(function() {
  1753. view.appendTo('#qunit-fixture');
  1754. });
  1755. Ember.run(function() {
  1756. set(view, 'boundValue', "bar");
  1757. });
  1758. equal(Ember.$.trim(view.$().text()), "bar");
  1759. });
  1760. test("should update bound values after view's parent is removed and then re-appended", function() {
  1761. var controller = Ember.Object.create();
  1762. var parentView = Ember.ContainerView.create({
  1763. childViews: ['testView'],
  1764. controller: controller,
  1765. testView: Ember.View.create({
  1766. template: Ember.Handlebars.compile("{{#if showStuff}}{{boundValue}}{{else}}Not true.{{/if}}")
  1767. })
  1768. });
  1769. controller.setProperties({
  1770. showStuff: true,
  1771. boundValue: "foo"
  1772. });
  1773. Ember.run(function() {
  1774. parentView.appendTo('#qunit-fixture');
  1775. });
  1776. view = parentView.get('testView');
  1777. equal(Ember.$.trim(view.$().text()), "foo");
  1778. Ember.run(function() {
  1779. set(controller, 'showStuff', false);
  1780. });
  1781. equal(Ember.$.trim(view.$().text()), "Not true.");
  1782. Ember.run(function() {
  1783. set(controller, 'showStuff', true);
  1784. });
  1785. equal(Ember.$.trim(view.$().text()), "foo");
  1786. Ember.run(function() {
  1787. parentView.remove();
  1788. set(controller, 'showStuff', false);
  1789. });
  1790. Ember.run(function() {
  1791. set(controller, 'showStuff', true);
  1792. });
  1793. Ember.run(function() {
  1794. parentView.appendTo('#qunit-fixture');
  1795. });
  1796. Ember.run(function() {
  1797. set(controller, 'boundValue', "bar");
  1798. });
  1799. equal(Ember.$.trim(view.$().text()), "bar");
  1800. Ember.run(function() {
  1801. parentView.destroy();
  1802. });
  1803. });
  1804. test("should call a registered helper for mustache without parameters", function() {
  1805. Ember.Handlebars.registerHelper('foobar', function() {
  1806. return 'foobar';
  1807. });
  1808. view = Ember.View.create({
  1809. template: Ember.Handlebars.compile("{{foobar}}")
  1810. });
  1811. appendView();
  1812. ok(view.$().text() === 'foobar', "Regular helper was invoked correctly");
  1813. });
  1814. test("should bind to the property if no registered helper found for a mustache without parameters", function() {
  1815. view = Ember.View.createWithMixins({
  1816. template: Ember.Handlebars.compile("{{view.foobarProperty}}"),
  1817. foobarProperty: Ember.computed(function() {
  1818. return 'foobarProperty';
  1819. })
  1820. });
  1821. appendView();
  1822. ok(view.$().text() === 'foobarProperty', "Property was bound to correctly");
  1823. });
  1824. test("should accept bindings as a string or an Ember.Binding", function() {
  1825. var viewClass = Ember.View.extend({
  1826. template: Ember.Handlebars.compile("binding: {{view.bindingTest}}, string: {{view.stringTest}}")
  1827. });
  1828. Ember.Handlebars.registerHelper('boogie', function(id, options) {
  1829. options.hash = options.hash || {};
  1830. options.hash.bindingTestBinding = Ember.Binding.oneWay('context.' + id);
  1831. options.hash.stringTestBinding = id;
  1832. return Ember.Handlebars.ViewHelper.helper(this, viewClass, options);
  1833. });
  1834. view = Ember.View.create({
  1835. context: Ember.Object.create({
  1836. direction: 'down'
  1837. }),
  1838. template: Ember.Handlebars.compile("{{boogie direction}}")
  1839. });
  1840. appendView();
  1841. equal(Ember.$.trim(view.$().text()), "binding: down, string: down");
  1842. });
  1843. test("should teardown observers from bound properties on rerender", function() {
  1844. view = Ember.View.create({
  1845. template: Ember.Handlebars.compile("{{view.foo}}"),
  1846. foo: 'bar'
  1847. });
  1848. appendView();
  1849. equal(Ember.observersFor(view, 'foo').length, 1);
  1850. Ember.run(function() {
  1851. view.rerender();
  1852. });
  1853. equal(Ember.observersFor(view, 'foo').length, 1);
  1854. });
  1855. test("should teardown observers from bindAttr on rerender", function() {
  1856. view = Ember.View.create({
  1857. template: Ember.Handlebars.compile('<span {{bindAttr class="view.foo" name="view.foo"}}>wat</span>'),
  1858. foo: 'bar'
  1859. });
  1860. appendView();
  1861. equal(Ember.observersFor(view, 'foo').length, 2);
  1862. Ember.run(function() {
  1863. view.rerender();
  1864. });
  1865. equal(Ember.observersFor(view, 'foo').length, 2);
  1866. });