PageRenderTime 77ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/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

Large files files are truncated, but you can click here to view the full 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

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