No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

689 líneas
15KB

  1. // the compiler tests are the exact same as the interpreter tests
  2. // so instead of writing all the tests twice, override the to_html
  3. // method
  4. module('Compiler', {
  5. setup: function() {
  6. this._oldToHtml = Mustache.to_html;
  7. Mustache.to_html = function(template, view, partials) {
  8. var compiler = Mustache.compile(template, partials);
  9. return compiler(view);
  10. }
  11. },
  12. teardown: function() {
  13. Mustache.to_html = this._oldToHtml;
  14. }
  15. });
  16. test("Argument validation", function() {
  17. expect(4);
  18. equals(Mustache.to_html(undefined), '', 'No parameters');
  19. equals(Mustache.to_html('{{hi}}'), '', ' No View or Partials');
  20. equals(Mustache.to_html('{{hi}}', {hi:'Hi.'}), 'Hi.', 'No Partials');
  21. equals(Mustache.to_html('{{>hi}}', undefined, {hi:'{{p}}'}), '', 'Partial but no view');
  22. });
  23. test("Parser", function() {
  24. expect(4);
  25. // matches whitespace_partial.html
  26. equals(
  27. Mustache.to_html(
  28. '<h1>{{ greeting }}</h1>\n{{> partial }}\n<h3>{{ farewell }}</h3>',
  29. {
  30. greeting: function() {
  31. return "Welcome";
  32. },
  33. farewell: function() {
  34. return "Fair enough, right?";
  35. },
  36. partial: {
  37. name: "Chris",
  38. value: 10000,
  39. taxed_value: function() {
  40. return this.value - (this.value * 0.4);
  41. },
  42. in_ca: true
  43. }
  44. },
  45. {partial:'Hello {{ name}}\nYou have just won ${{value }}!\n{{# in_ca }}\nWell, ${{ taxed_value }}, after taxes.\n{{/ in_ca }}\n'}
  46. ),
  47. '<h1>Welcome</h1>\nHello Chris\nYou have just won $10000!\n\nWell, $6000, after taxes.\n\n\n<h3>Fair enough, right?</h3>',
  48. 'Whitespace in Tag names'
  49. );
  50. equals(
  51. Mustache.to_html(
  52. '{{tag1}}\n\n\n{{tag2}}\n',
  53. { tag1: 'Hello', tag2: 'World' },
  54. {}
  55. ),
  56. 'Hello\n\n\nWorld\n',
  57. 'Preservation of white space'
  58. );
  59. try {
  60. Mustache.to_html(
  61. '{{=tag1}}',
  62. { tag1: 'Hello' },
  63. {}
  64. );
  65. ok(false);
  66. } catch (e) {
  67. equals(e.message, 'Unexpected end of document.');
  68. }
  69. var partials = { 'partial' : '{{key}}' };
  70. Mustache.compile('{{>partial}}', partials );
  71. equals(partials['partial'], '{{key}}', 'Partials compiler must be non-destructive');
  72. });
  73. test("Basic Variables", function() {
  74. expect(4);
  75. // matches escaped.html
  76. equals(
  77. Mustache.to_html(
  78. '<h1>{{title}}</h1>\nBut not {{entities}}.\n',
  79. {
  80. title: function() {
  81. return "Bear > Shark";
  82. },
  83. entities: "&quot;"
  84. },
  85. {}
  86. ),
  87. '<h1>Bear &gt; Shark</h1>\nBut not &amp;quot;.\n',
  88. 'HTML Escaping'
  89. );
  90. // matches apostrophe.html (except in this implementation, apostrophes are not escaped.
  91. equals(
  92. Mustache.to_html(
  93. '{{apos}}{{control}}',
  94. { apos: '\'', control: 'X' },
  95. {}
  96. ),
  97. '\'X',
  98. 'Apostrophe escaping'
  99. );
  100. // matches null_string.html
  101. equals(
  102. Mustache.to_html(
  103. 'Hello {{name}}\nglytch {{glytch}}\nbinary {{binary}}\nvalue {{value}}\nnumeric {{numeric}}',
  104. {
  105. name: "Elise",
  106. glytch: true,
  107. binary: false,
  108. value: null,
  109. numeric: function() {
  110. return NaN;
  111. }
  112. },
  113. {}
  114. ),
  115. 'Hello Elise\nglytch true\nbinary false\nvalue \nnumeric NaN',
  116. 'Different variable types'
  117. );
  118. // matches two_in_a_row.html
  119. equals(
  120. Mustache.to_html(
  121. '{{greeting}}, {{name}}!',
  122. {
  123. name: "Joe",
  124. greeting: "Welcome"
  125. },
  126. {}
  127. ),
  128. 'Welcome, Joe!'
  129. );
  130. });
  131. test("'{' or '&' (Unescaped Variable)", function() {
  132. expect(2);
  133. // matches unescaped.html
  134. equals(
  135. Mustache.to_html(
  136. '<h1>{{{title}}}</h1>',
  137. {
  138. title: function() {
  139. return "Bear > Shark";
  140. }
  141. },
  142. {}
  143. ),
  144. '<h1>Bear > Shark</h1>',
  145. '{ character'
  146. );
  147. equals(
  148. Mustache.to_html(
  149. '<h1>{{&title}}</h1>',
  150. {
  151. title: function() {
  152. return "Bear > Shark";
  153. }
  154. },
  155. {}
  156. ),
  157. '<h1>Bear > Shark</h1>',
  158. '& character'
  159. );
  160. });
  161. test("'#' (Sections)", function() {
  162. expect(8);
  163. // matches array_of_partials_implicit_partial.html
  164. equals(
  165. Mustache.to_html(
  166. 'Here is some stuff!\n{{#numbers}}\n{{>partial}}\n{{/numbers}}',
  167. { numbers: ['1', '2', '3', '4'] },
  168. { partial: '{{.}}' }
  169. ),
  170. 'Here is some stuff!\n\n1\n\n2\n\n3\n\n4\n',
  171. 'Array of Partials (Implicit)'
  172. );
  173. // matches array_of_partials_partial.html
  174. equals(
  175. Mustache.to_html(
  176. 'Here is some stuff!\n{{#numbers}}\n{{>partial}}\n{{/numbers}}',
  177. { numbers: [{i: '1'}, {i: '2'}, {i: '3'}, {i: '4'}] },
  178. { partial: '{{i}}' }
  179. ),
  180. 'Here is some stuff!\n\n1\n\n2\n\n3\n\n4\n',
  181. 'Array of Partials (Explicit)'
  182. );
  183. // matches array_of_strings.html
  184. equals(
  185. Mustache.to_html(
  186. '{{#array_of_strings}}{{.}} {{/array_of_strings}}',
  187. {array_of_strings: ['hello', 'world']},
  188. {}
  189. ),
  190. 'hello world ',
  191. 'Array of Strings'
  192. );
  193. // mathces higher_order_sections.html
  194. equals(
  195. Mustache.to_html(
  196. '{{#bolder}}Hi {{name}}.{{/bolder}}\n',
  197. {
  198. "name": "Tater",
  199. "helper": "To tinker?",
  200. "bolder": function() {
  201. return function(text, render) {
  202. return "<b>" + render(text) + '</b> ' + this.helper;
  203. }
  204. }
  205. },
  206. {}
  207. ),
  208. '<b>Hi Tater.</b> To tinker?\n'
  209. );
  210. // matches recursion_with_same_names.html
  211. equals(
  212. Mustache.to_html(
  213. '{{ name }}\n{{ description }}\n\n{{#terms}}\n {{name}}\n {{index}}\n{{/terms}}\n',
  214. {
  215. name: 'name',
  216. description: 'desc',
  217. terms: [
  218. {name: 't1', index: 0},
  219. {name: 't2', index: 1}
  220. ]
  221. },
  222. {}
  223. ),
  224. 'name\ndesc\n\n\n t1\n 0\n\n t2\n 1\n\n'
  225. );
  226. // matches reuse_of_enumerables.html
  227. equals(
  228. Mustache.to_html(
  229. '{{#terms}}\n {{name}}\n {{index}}\n{{/terms}}\n{{#terms}}\n {{name}}\n {{index}}\n{{/terms}}\n',
  230. {
  231. terms: [
  232. {name: 't1', index: 0},
  233. {name: 't2', index: 1}
  234. ]
  235. },
  236. {}
  237. ),
  238. '\n t1\n 0\n\n t2\n 1\n\n\n t1\n 0\n\n t2\n 1\n\n',
  239. 'Lazy match of Section and Inverted Section'
  240. );
  241. // matches section_as_context.html
  242. equals(
  243. Mustache.to_html(
  244. '{{#a_object}}\n <h1>{{title}}</h1>\n <p>{{description}}</p>\n <ul>\n {{#a_list}}\n <li>{{label}}</li>\n {{/a_list}}\n </ul>\n{{/a_object}}\n',
  245. {
  246. a_object: {
  247. title: 'this is an object',
  248. description: 'one of its attributes is a list',
  249. a_list: [{label: 'listitem1'}, {label: 'listitem2'}]
  250. }
  251. },
  252. {}
  253. ),
  254. '\n <h1>this is an object</h1>\n <p>one of its attributes is a list</p>\n <ul>\n \n <li>listitem1</li>\n \n <li>listitem2</li>\n \n </ul>\n\n',
  255. 'Lazy match of Section and Inverted Section'
  256. );
  257. // matches nesting.html
  258. equals(
  259. Mustache.to_html(
  260. '{{#foo}}\n {{#a}}\n {{b}}\n {{/a}}\n{{/foo}}',
  261. {
  262. foo: [
  263. {a: {b: 1}},
  264. {a: {b: 2}},
  265. {a: {b: 3}}
  266. ]
  267. },
  268. {}
  269. ),
  270. '\n \n 1\n \n\n \n 2\n \n\n \n 3\n \n',
  271. 'Context Nesting'
  272. );
  273. });
  274. test("'^' (Inverted Section)", function() {
  275. expect(1);
  276. // matches inverted_section.html
  277. equals(
  278. Mustache.to_html(
  279. '{{#repo}}<b>{{name}}</b>{{/repo}}\n{{^repo}}No repos :({{/repo}}\n',
  280. {
  281. "repo": []
  282. },
  283. {}
  284. ),
  285. '\nNo repos :(\n'
  286. );
  287. });
  288. test("'>' (Partials)", function() {
  289. expect(5);
  290. // matches view_partial.html
  291. equals(
  292. Mustache.to_html(
  293. '<h1>{{greeting}}</h1>\n{{>partial}}\n<h3>{{farewell}}</h3>',
  294. {
  295. greeting: function() {
  296. return "Welcome";
  297. },
  298. farewell: function() {
  299. return "Fair enough, right?";
  300. },
  301. partial: {
  302. name: "Chris",
  303. value: 10000,
  304. taxed_value: function() {
  305. return this.value - (this.value * 0.4);
  306. },
  307. in_ca: true
  308. }
  309. },
  310. {partial: 'Hello {{name}}\nYou have just won ${{value}}!\n{{#in_ca}}\nWell, ${{ taxed_value }}, after taxes.\n{{/in_ca}}\n'}
  311. ),
  312. '<h1>Welcome</h1>\nHello Chris\nYou have just won $10000!\n\nWell, $6000, after taxes.\n\n\n<h3>Fair enough, right?</h3>'
  313. );
  314. // matches array_partial.html
  315. equals(
  316. Mustache.to_html(
  317. '{{>partial}}',
  318. {
  319. partial: {
  320. array: ['1', '2', '3', '4']
  321. }
  322. },
  323. { partial: 'Here\'s a non-sense array of values\n{{#array}}\n {{.}}\n{{/array}}' }
  324. ),
  325. 'Here\'s a non-sense array of values\n\n 1\n\n 2\n\n 3\n\n 4\n'
  326. );
  327. // matches template_partial.html
  328. equals(
  329. Mustache.to_html(
  330. '<h1>{{title}}</h1>\n{{>partial}}',
  331. {
  332. title: function() {
  333. return "Welcome";
  334. },
  335. partial: {
  336. again: "Goodbye"
  337. }
  338. },
  339. {partial:'Again, {{again}}!'}
  340. ),
  341. '<h1>Welcome</h1>\nAgain, Goodbye!'
  342. );
  343. // matches partial_recursion.html
  344. equals(
  345. Mustache.to_html(
  346. '{{name}}\n{{#kids}}\n{{>partial}}\n{{/kids}}',
  347. {
  348. name: '1',
  349. kids: [
  350. {
  351. name: '1.1',
  352. children: [
  353. {name: '1.1.1'}
  354. ]
  355. }
  356. ]
  357. },
  358. {partial:'{{name}}\n{{#children}}\n{{>partial}}\n{{/children}}'}
  359. ),
  360. '1\n\n1.1\n\n1.1.1\n\n\n'
  361. );
  362. try {
  363. Mustache.to_html(
  364. '{{>partial}}',
  365. {},
  366. {partal: ''}
  367. );
  368. ok(false);
  369. } catch(e) {
  370. equals(e.message, "Unknown partial 'partial'");
  371. }
  372. });
  373. test("'=' (Set Delimiter)", function() {
  374. expect(1);
  375. // matches delimiter.html
  376. equals(
  377. Mustache.to_html(
  378. '{{=<% %>=}}*\n<% first %>\n* <% second %>\n<%=| |=%>\n* | third |\n|={{ }}=|\n* {{ fourth }}',
  379. {
  380. first: "It worked the first time.",
  381. second: "And it worked the second time.",
  382. third: "Then, surprisingly, it worked the third time.",
  383. fourth: "Fourth time also fine!."
  384. },
  385. {}
  386. ),
  387. '*\nIt worked the first time.\n* And it worked the second time.\n\n* Then, surprisingly, it worked the third time.\n\n* Fourth time also fine!.',
  388. 'Simple Set Delimiter'
  389. );
  390. });
  391. test("'!' (Comments)", function() {
  392. expect(4);
  393. equals(
  394. Mustache.to_html('{{! this is a single line comment !}}'),
  395. '',
  396. 'Single Line Comments');
  397. equals(
  398. Mustache.to_html('{{!this is a multiline comment\ni said this is a multiline comment!}}'),
  399. '',
  400. 'Multiline Comments');
  401. equals(
  402. Mustache.to_html('{{!this {{is}} {{#a}} {{/multiline}} comment\ni {{^said}} ! hello !! bye!}}'),
  403. '',
  404. 'Correct tokenization');
  405. // matches comments.html
  406. equals(
  407. Mustache.to_html(
  408. '<h1>{{title}}{{! just something interesting... or not... !}}</h1>\n',
  409. {
  410. title: function() {
  411. return "A Comedy of Errors";
  412. }
  413. },
  414. {}
  415. ),
  416. '<h1>A Comedy of Errors</h1>\n'
  417. );
  418. });
  419. test("'%' (Pragmas)", function() {
  420. expect(3);
  421. // matches array_of_strings_options.html
  422. equals(
  423. Mustache.to_html(
  424. '{{%IMPLICIT-ITERATOR iterator=rob}}\n{{#array_of_strings_options}}{{rob}} {{/array_of_strings_options}}',
  425. {array_of_strings_options: ['hello', 'world']},
  426. {}
  427. ),
  428. '\nhello world ',
  429. 'IMPLICIT-ITERATOR pragma'
  430. );
  431. // matches unknown_pragma.txt
  432. try {
  433. equals(
  434. Mustache.to_html(
  435. '{{%I-HAVE-THE-GREATEST-MUSTACHE}}\n',
  436. {},
  437. {}
  438. ),
  439. 'hello world ',
  440. 'IMPLICIT-ITERATOR pragma'
  441. );
  442. ok(false);
  443. } catch (e) {
  444. equals(e.message, 'This implementation of mustache doesn\'t understand the \'I-HAVE-THE-GREATEST-MUSTACHE\' pragma');
  445. }
  446. equals(
  447. Mustache.to_html(
  448. '{{%IMPLICIT-ITERATOR}}{{#dataSet}}{{.}}:{{/dataSet}}',
  449. { dataSet: [ 'Object 1', 'Object 2', 'Object 3' ] },
  450. {}
  451. ),
  452. "Object 1:Object 2:Object 3:"
  453. );
  454. });
  455. test("Empty", function() {
  456. expect(2);
  457. // matches empty_template.html
  458. equals(
  459. Mustache.to_html(
  460. '<html><head></head><body><h1>Test</h1></body></html>',
  461. {},
  462. {}
  463. ),
  464. '<html><head></head><body><h1>Test</h1></body></html>',
  465. 'Empty Template'
  466. );
  467. // matches empty_partial.html
  468. equals(
  469. Mustache.to_html(
  470. 'hey {{foo}}\n{{>partial}}\n',
  471. {
  472. foo: 1
  473. },
  474. {partial: 'yo'}
  475. ),
  476. 'hey 1\nyo\n',
  477. 'Empty Partial'
  478. );
  479. });
  480. test("Demo", function() {
  481. expect(2);
  482. // matches simple.html
  483. equals(
  484. Mustache.to_html(
  485. 'Hello {{name}}\nYou have just won ${{value}}!\n{{#in_ca}}\nWell, ${{ taxed_value }}, after taxes.\n{{/in_ca}}',
  486. {
  487. name: "Chris",
  488. value: 10000,
  489. taxed_value: function() {
  490. return this.value - (this.value * 0.4);
  491. },
  492. in_ca: true
  493. },
  494. {}
  495. ),
  496. 'Hello Chris\nYou have just won $10000!\n\nWell, $6000, after taxes.\n',
  497. 'A simple template'
  498. );
  499. // matches complex.html
  500. var template = [
  501. '<h1>{{header}}</h1>',
  502. '{{#list}}',
  503. ' <ul>',
  504. ' {{#item}}',
  505. ' {{#current}}',
  506. ' <li><strong>{{name}}</strong></li>',
  507. ' {{/current}}',
  508. ' {{#link}}',
  509. ' <li><a href="{{url}}">{{name}}</a></li>',
  510. ' {{/link}}',
  511. ' {{/item}}',
  512. ' </ul>',
  513. '{{/list}}',
  514. '{{#empty}}',
  515. ' <p>The list is empty.</p>',
  516. '{{/empty}}'
  517. ].join('\n');
  518. var view = {
  519. header: function() {
  520. return "Colors";
  521. },
  522. item: [
  523. {name: "red", current: true, url: "#Red"},
  524. {name: "green", current: false, url: "#Green"},
  525. {name: "blue", current: false, url: "#Blue"}
  526. ],
  527. link: function() {
  528. return this["current"] !== true;
  529. },
  530. list: function() {
  531. return this.item.length !== 0;
  532. },
  533. empty: function() {
  534. return this.item.length === 0;
  535. }
  536. };
  537. var expected_result = '<h1>Colors</h1>\n\n <ul>\n \n \n <li><strong>red</strong></li>\n \n \n <li><a href=\"#Red\">red</a></li>\n \n \n \n \n <li><a href=\"#Green\">green</a></li>\n \n \n \n \n <li><a href=\"#Blue\">blue</a></li>\n \n \n </ul>\n\n';
  538. equals(
  539. Mustache.to_html(
  540. template,
  541. view,
  542. {}
  543. ),
  544. expected_result,
  545. 'A complex template'
  546. );
  547. });
  548. test("Performance", function() {
  549. expect(1);
  550. var start, end;
  551. // This performance test is copied form skymustache
  552. // (https://github.com/schuyler1d/handlebars.js/blob/sky_test/test/perf.js)
  553. // set up the templates
  554. var template = "This is the story of guys who work on a project\n" +
  555. "called {{project}}. Their names were {{#people}}{{firstName}} and {{/people}}\n" +
  556. "they both enjoyed working on {{project}}.\n\n" +
  557. "{{#people}}\n" +
  558. "{{>personPet}}\n" +
  559. "{{/people}}";
  560. var partials = {
  561. personPet: "{{firstName}} {{lastName}} {{#pet}} owned a {{species}}. Its name was {{name}}.{{/pet}}{{^pet}}didn't own a pet.{{/pet}}"
  562. };
  563. var view = {
  564. project: "Handlebars",
  565. people: [
  566. { firstName: "Yehuda", lastName: "Katz" },
  567. { firstName: "Alan", lastName: "Johnson", pet: { species: "cat", name: "Luke" } }
  568. ]
  569. }
  570. start = new Date();
  571. for (var j=0;j<1000;++j) {
  572. this._oldToHtml(template, view, partials);
  573. }
  574. end = new Date();
  575. var interpreter_time = end.getTime() - start.getTime();
  576. start = new Date();
  577. var compiler = Mustache.compile(template, partials);
  578. for (var k=0;k<1000;++k) {
  579. compiler(view);
  580. }
  581. end = new Date();
  582. var compiler_time = end.getTime() - start.getTime();
  583. ok(compiler_time<interpreter_time, 'Compiler is faster (' + compiler_time + ' vs ' + interpreter_time + ').');
  584. });
  585. test("Regression Suite", function() {
  586. expect(3);
  587. // matches bug_11_eating_whitespace.html
  588. equals(
  589. Mustache.to_html(
  590. '{{tag}} foo',
  591. { tag: "yo" },
  592. {}
  593. ),
  594. 'yo foo',
  595. 'Issue 11'
  596. );
  597. // matches delimiters_partial.html
  598. equals(
  599. Mustache.to_html(
  600. '{{#enumerate}}\n{{>partial}}\n{{/enumerate}}',
  601. { enumerate: [ { text: 'A' }, { text: 'B' } ] },
  602. { partial: '{{=[[ ]]=}}\n{{text}}\n[[={{ }}=]]' }
  603. ),
  604. '\n\n{{text}}\n\n\n\n{{text}}\n\n',
  605. 'Issue 44'
  606. );
  607. // matches bug_46_set_delimiter.html
  608. equals(
  609. Mustache.to_html(
  610. '{{=[[ ]]=}}[[#IsMustacheAwesome]]mustache is awesome![[/IsMustacheAwesome]]',
  611. {IsMustacheAwesome: true},
  612. {}
  613. ),
  614. 'mustache is awesome!',
  615. 'Issue 46'
  616. );
  617. });