您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

729 行
16KB

  1. test("Argument validation", function() {
  2. equals(Mustache.to_html(undefined), '', 'No parameters');
  3. equals(Mustache.to_html('{{hi}}'), '', ' No View or Partials');
  4. equals(Mustache.to_html('{{hi}}', {hi:'Hi.'}), 'Hi.', 'No Partials');
  5. equals(Mustache.to_html('{{>hi}}', undefined, {hi:'{{p}}'}), '', 'Partial but no view');
  6. });
  7. test("Parser", function() {
  8. // matches whitespace_partial.html
  9. equals(
  10. Mustache.to_html(
  11. '<h1>{{ greeting }}</h1>\n{{> partial }}\n<h3>{{ farewell }}</h3>',
  12. {
  13. greeting: function() {
  14. return "Welcome";
  15. },
  16. farewell: function() {
  17. return "Fair enough, right?";
  18. },
  19. partial: {
  20. name: "Chris",
  21. value: 10000,
  22. taxed_value: function() {
  23. return this.value - (this.value * 0.4);
  24. },
  25. in_ca: true
  26. }
  27. },
  28. {partial:'Hello {{ name}}\nYou have just won ${{value }}!\n{{# in_ca }}\nWell, ${{ taxed_value }}, after taxes.\n{{/ in_ca }}\n'}
  29. ),
  30. '<h1>Welcome</h1>\nHello Chris\nYou have just won $10000!\n\nWell, $6000, after taxes.\n\n\n<h3>Fair enough, right?</h3>',
  31. 'Whitespace in Tag names'
  32. );
  33. equals(
  34. Mustache.to_html(
  35. '{{tag1}}\n\n\n{{tag2}}\n',
  36. { tag1: 'Hello', tag2: 'World' },
  37. {}
  38. ),
  39. 'Hello\n\n\nWorld\n',
  40. 'Preservation of white space'
  41. );
  42. raises(
  43. function() {
  44. Mustache.to_html(
  45. '{{=tag1}}',
  46. { tag1: 'Hello' },
  47. {}
  48. );
  49. }, function(e) {
  50. return e.message === '(1,1): Malformed change delimiter token "{{=tag1}}".';
  51. },
  52. 'Malformed tags should be handled correctly.'
  53. );
  54. raises(
  55. function() {
  56. Mustache.to_html(
  57. '{{tag blah}}'
  58. )
  59. }, function(e) {
  60. return e.message === '(1,1): Malformed variable name "tag blah".';
  61. },
  62. 'Malformed tags should be handled correctly.'
  63. );
  64. var partials = { 'partial' : '{{key}}' };
  65. Mustache.compile('{{>partial}}', partials );
  66. equals(partials['partial'], '{{key}}', 'Partials compiler must be non-destructive');
  67. });
  68. test("Basic Variables", function() {
  69. // matches escaped.html
  70. equals(
  71. Mustache.to_html(
  72. '<h1>{{title}}</h1>\nBut not {{entities}}.\n',
  73. {
  74. title: function() {
  75. return "Bear > Shark";
  76. },
  77. entities: "&quot;"
  78. },
  79. {}
  80. ),
  81. '<h1>Bear &gt; Shark</h1>\nBut not &amp;quot;.\n',
  82. 'HTML Escaping'
  83. );
  84. // matches apostrophe.html (except in this implementation, apostrophes are not escaped.
  85. equals(
  86. Mustache.to_html(
  87. '{{apos}}{{control}}',
  88. { apos: '\'', control: 'X' },
  89. {}
  90. ),
  91. '\'X',
  92. 'Apostrophe escaping'
  93. );
  94. // matches null_string.html
  95. equals(
  96. Mustache.to_html(
  97. 'Hello {{name}}\nglytch {{glytch}}\nbinary {{binary}}\nvalue {{value}}\nnumeric {{numeric}}',
  98. {
  99. name: "Elise",
  100. glytch: true,
  101. binary: false,
  102. value: null,
  103. numeric: function() {
  104. return NaN;
  105. }
  106. },
  107. {}
  108. ),
  109. 'Hello Elise\nglytch true\nbinary false\nvalue \nnumeric NaN',
  110. 'Different variable types'
  111. );
  112. // matches two_in_a_row.html
  113. equals(
  114. Mustache.to_html(
  115. '{{greeting}}, {{name}}!',
  116. {
  117. name: "Joe",
  118. greeting: "Welcome"
  119. },
  120. {}
  121. ),
  122. 'Welcome, Joe!'
  123. );
  124. });
  125. test("'{' or '&' (Unescaped Variable)", function() {
  126. // matches unescaped.html
  127. equals(
  128. Mustache.to_html(
  129. '<h1>{{{title}}}</h1>',
  130. {
  131. title: function() {
  132. return "Bear > Shark";
  133. }
  134. },
  135. {}
  136. ),
  137. '<h1>Bear > Shark</h1>',
  138. '{ character'
  139. );
  140. equals(
  141. Mustache.to_html(
  142. '<h1>{{&title}}</h1>',
  143. {
  144. title: function() {
  145. return "Bear > Shark";
  146. }
  147. },
  148. {}
  149. ),
  150. '<h1>Bear > Shark</h1>',
  151. '& character'
  152. );
  153. equals(
  154. Mustache.to_html(
  155. '<h1>{{title}}}</h1>',
  156. { title: 'Bear > Shark' }
  157. , {}
  158. ),
  159. '<h1>Bear &gt; Shark}</h1>'
  160. , 'Potential false positive'
  161. );
  162. });
  163. test("'#' (Sections)", function() {
  164. // matches array_of_partials_implicit_partial.html
  165. equals(
  166. Mustache.to_html(
  167. 'Here is some stuff!\n{{#numbers}}\n{{>partial}}\n{{/numbers}}',
  168. { numbers: ['1', '2', '3', '4'] },
  169. { partial: '{{.}}' }
  170. ),
  171. 'Here is some stuff!\n\n1\n\n2\n\n3\n\n4\n',
  172. 'Array of Partials (Implicit)'
  173. );
  174. // matches array_of_partials_partial.html
  175. equals(
  176. Mustache.to_html(
  177. 'Here is some stuff!\n{{#numbers}}\n{{>partial}}\n{{/numbers}}',
  178. { numbers: [{i: '1'}, {i: '2'}, {i: '3'}, {i: '4'}] },
  179. { partial: '{{i}}' }
  180. ),
  181. 'Here is some stuff!\n\n1\n\n2\n\n3\n\n4\n',
  182. 'Array of Partials (Explicit)'
  183. );
  184. // matches array_of_strings.html
  185. equals(
  186. Mustache.to_html(
  187. '{{#array_of_strings}}{{.}} {{/array_of_strings}}',
  188. {array_of_strings: ['hello', 'world']},
  189. {}
  190. ),
  191. 'hello world ',
  192. 'Array of Strings'
  193. );
  194. // mathces higher_order_sections.html
  195. equals(
  196. Mustache.to_html(
  197. '{{#bolder}}Hi {{name}}.{{/bolder}}\n',
  198. {
  199. "name": "Tater",
  200. "helper": "To tinker?",
  201. "bolder": function() {
  202. return function(text, render) {
  203. return "<b>" + render(text) + '</b> ' + this.helper;
  204. }
  205. }
  206. },
  207. {}
  208. ),
  209. '<b>Hi Tater.</b> To tinker?\n'
  210. );
  211. // matches recursion_with_same_names.html
  212. equals(
  213. Mustache.to_html(
  214. '{{ name }}\n{{ description }}\n\n{{#terms}}\n {{name}}\n {{index}}\n{{/terms}}\n',
  215. {
  216. name: 'name',
  217. description: 'desc',
  218. terms: [
  219. {name: 't1', index: 0},
  220. {name: 't2', index: 1}
  221. ]
  222. },
  223. {}
  224. ),
  225. 'name\ndesc\n\n\n t1\n 0\n\n t2\n 1\n\n'
  226. );
  227. // matches reuse_of_enumerables.html
  228. equals(
  229. Mustache.to_html(
  230. '{{#terms}}\n {{name}}\n {{index}}\n{{/terms}}\n{{#terms}}\n {{name}}\n {{index}}\n{{/terms}}\n',
  231. {
  232. terms: [
  233. {name: 't1', index: 0},
  234. {name: 't2', index: 1}
  235. ]
  236. },
  237. {}
  238. ),
  239. '\n t1\n 0\n\n t2\n 1\n\n\n t1\n 0\n\n t2\n 1\n\n',
  240. 'Lazy match of Section and Inverted Section'
  241. );
  242. // matches section_as_context.html
  243. equals(
  244. Mustache.to_html(
  245. '{{#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',
  246. {
  247. a_object: {
  248. title: 'this is an object',
  249. description: 'one of its attributes is a list',
  250. a_list: [{label: 'listitem1'}, {label: 'listitem2'}]
  251. }
  252. },
  253. {}
  254. ),
  255. '\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',
  256. 'Lazy match of Section and Inverted Section'
  257. );
  258. // matches nesting.html
  259. equals(
  260. Mustache.to_html(
  261. '{{#foo}}\n {{#a}}\n {{b}}\n {{/a}}\n{{/foo}}',
  262. {
  263. foo: [
  264. {a: {b: 1}},
  265. {a: {b: 2}},
  266. {a: {b: 3}}
  267. ]
  268. },
  269. {}
  270. ),
  271. '\n \n 1\n \n\n \n 2\n \n\n \n 3\n \n',
  272. 'Context Nesting'
  273. );
  274. });
  275. test("'^' (Inverted Section)", function() {
  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. // matches view_partial.html
  290. equals(
  291. Mustache.to_html(
  292. '<h1>{{greeting}}</h1>\n{{>partial}}\n<h3>{{farewell}}</h3>',
  293. {
  294. greeting: function() {
  295. return "Welcome";
  296. },
  297. farewell: function() {
  298. return "Fair enough, right?";
  299. },
  300. partial: {
  301. name: "Chris",
  302. value: 10000,
  303. taxed_value: function() {
  304. return this.value - (this.value * 0.4);
  305. },
  306. in_ca: true
  307. }
  308. },
  309. {partial: 'Hello {{name}}\nYou have just won ${{value}}!\n{{#in_ca}}\nWell, ${{ taxed_value }}, after taxes.\n{{/in_ca}}\n'}
  310. ),
  311. '<h1>Welcome</h1>\nHello Chris\nYou have just won $10000!\n\nWell, $6000, after taxes.\n\n\n<h3>Fair enough, right?</h3>'
  312. );
  313. // matches array_partial.html
  314. equals(
  315. Mustache.to_html(
  316. '{{>partial}}',
  317. {
  318. partial: {
  319. array: ['1', '2', '3', '4']
  320. }
  321. },
  322. { partial: 'Here\'s a non-sense array of values\n{{#array}}\n {{.}}\n{{/array}}' }
  323. ),
  324. 'Here\'s a non-sense array of values\n\n 1\n\n 2\n\n 3\n\n 4\n'
  325. );
  326. // matches template_partial.html
  327. equals(
  328. Mustache.to_html(
  329. '<h1>{{title}}</h1>\n{{>partial}}',
  330. {
  331. title: function() {
  332. return "Welcome";
  333. },
  334. partial: {
  335. again: "Goodbye"
  336. }
  337. },
  338. {partial:'Again, {{again}}!'}
  339. ),
  340. '<h1>Welcome</h1>\nAgain, Goodbye!'
  341. );
  342. // matches partial_recursion.html
  343. equals(
  344. Mustache.to_html(
  345. '{{name}}\n{{#kids}}\n{{>partial}}\n{{/kids}}',
  346. {
  347. name: '1',
  348. kids: [
  349. {
  350. name: '1.1',
  351. children: [
  352. {name: '1.1.1'}
  353. ]
  354. }
  355. ]
  356. },
  357. {partial:'{{name}}\n{{#children}}\n{{>partial}}\n{{/children}}'}
  358. ),
  359. '1\n\n1.1\n\n1.1.1\n\n\n'
  360. );
  361. raises(
  362. function() {
  363. Mustache.to_html(
  364. '{{>partial}}',
  365. {},
  366. {partal: ''}
  367. );
  368. }, function(e) {
  369. return e.message === '(1,1): Unknown partial "partial".';
  370. },
  371. 'Missing partials should be handled correctly.'
  372. );
  373. });
  374. test("'=' (Set Delimiter)", function() {
  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. equals(
  391. Mustache.to_html(
  392. '{{#noData}}{{=~~ ~~=}}Set Change Delimiter ~~data~~ ~~={{ }}=~~{{/noData}}'
  393. , {
  394. noData: true
  395. , data: false
  396. }
  397. , {}
  398. )
  399. , 'Set Change Delimiter false '
  400. , 'Change Delimiter inside Section');
  401. });
  402. test("'!' (Comments)", function() {
  403. equals(
  404. Mustache.to_html('{{! this is a single line comment !}}'),
  405. '',
  406. 'Single Line Comments');
  407. equals(
  408. Mustache.to_html('{{!this is a multiline comment\ni said this is a multiline comment!}}'),
  409. '',
  410. 'Multiline Comments');
  411. equals(
  412. Mustache.to_html('{{!this {{is}} {{#a}} {{/multiline}} comment\ni {{^said}} ! hello !! bye!}}'),
  413. '',
  414. 'Correct tokenization');
  415. // matches comments.html
  416. equals(
  417. Mustache.to_html(
  418. '<h1>{{title}}{{! just something interesting... or not... !}}</h1>\n',
  419. {
  420. title: function() {
  421. return "A Comedy of Errors";
  422. }
  423. },
  424. {}
  425. ),
  426. '<h1>A Comedy of Errors</h1>\n'
  427. );
  428. });
  429. test("'%' (Pragmas)", function() {
  430. // matches array_of_strings_options.html
  431. equals(
  432. Mustache.to_html(
  433. '{{%IMPLICIT-ITERATOR iterator=rob}}\n{{#array_of_strings_options}}{{rob}} {{/array_of_strings_options}}',
  434. {array_of_strings_options: ['hello', 'world']},
  435. {}
  436. ),
  437. '\nhello world ',
  438. 'IMPLICIT-ITERATOR pragma'
  439. );
  440. // matches unknown_pragma.txt
  441. raises(
  442. function() {
  443. Mustache.to_html(
  444. '{{%I-HAVE-THE-GREATEST-MUSTACHE}}\n',
  445. {},
  446. {}
  447. );
  448. },
  449. function(e) {
  450. return e.message === 'This implementation of mustache does not implement the "I-HAVE-THE-GREATEST-MUSTACHE" pragma.';
  451. },
  452. 'Notification of unimplemented pragmas'
  453. );
  454. equals(
  455. Mustache.to_html(
  456. '{{%IMPLICIT-ITERATOR}}{{#dataSet}}{{.}}:{{/dataSet}}',
  457. { dataSet: [ 'Object 1', 'Object 2', 'Object 3' ] },
  458. {}
  459. ),
  460. "Object 1:Object 2:Object 3:",
  461. 'Default behaviour for IMPLICIT ITERATOR'
  462. );
  463. equals(
  464. Mustache.to_html(
  465. '{{%IMPLICIT-ITERATOR iterator=rob}}{{=<% %>=}}<%#dataSet%><%rob%>:<%/dataSet%>',
  466. { dataSet: [ 'Object 1', 'Object 2', 'Object 3' ] },
  467. {}
  468. ),
  469. "Object 1:Object 2:Object 3:",
  470. 'Change Delimiter and Pragma mixes'
  471. );
  472. });
  473. test("Empty", function() {
  474. // matches empty_template.html
  475. equals(
  476. Mustache.to_html(
  477. '<html><head></head><body><h1>Test</h1></body></html>',
  478. {},
  479. {}
  480. ),
  481. '<html><head></head><body><h1>Test</h1></body></html>',
  482. 'Empty Template'
  483. );
  484. // matches empty_partial.html
  485. equals(
  486. Mustache.to_html(
  487. 'hey {{foo}}\n{{>partial}}\n',
  488. {
  489. foo: 1
  490. },
  491. {partial: 'yo'}
  492. ),
  493. 'hey 1\nyo\n',
  494. 'Empty Partial'
  495. );
  496. });
  497. test("Demo", function() {
  498. // matches simple.html
  499. equals(
  500. Mustache.to_html(
  501. 'Hello {{name}}\nYou have just won ${{value}}!\n{{#in_ca}}\nWell, ${{ taxed_value }}, after taxes.\n{{/in_ca}}',
  502. {
  503. name: "Chris",
  504. value: 10000,
  505. taxed_value: function() {
  506. return this.value - (this.value * 0.4);
  507. },
  508. in_ca: true
  509. },
  510. {}
  511. ),
  512. 'Hello Chris\nYou have just won $10000!\n\nWell, $6000, after taxes.\n',
  513. 'A simple template'
  514. );
  515. // matches complex.html
  516. var template = [
  517. '<h1>{{header}}</h1>',
  518. '{{#list}}',
  519. ' <ul>',
  520. ' {{#item}}',
  521. ' {{#current}}',
  522. ' <li><strong>{{name}}</strong></li>',
  523. ' {{/current}}',
  524. ' {{#link}}',
  525. ' <li><a href="{{url}}">{{name}}</a></li>',
  526. ' {{/link}}',
  527. ' {{/item}}',
  528. ' </ul>',
  529. '{{/list}}',
  530. '{{#empty}}',
  531. ' <p>The list is empty.</p>',
  532. '{{/empty}}'
  533. ].join('\n');
  534. var view = {
  535. header: function() {
  536. return "Colors";
  537. },
  538. item: [
  539. {name: "red", current: true, url: "#Red"},
  540. {name: "green", current: false, url: "#Green"},
  541. {name: "blue", current: false, url: "#Blue"}
  542. ],
  543. link: function() {
  544. return this["current"] !== true;
  545. },
  546. list: function() {
  547. return this.item.length !== 0;
  548. },
  549. empty: function() {
  550. return this.item.length === 0;
  551. }
  552. };
  553. 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';
  554. equals(
  555. Mustache.to_html(
  556. template,
  557. view,
  558. {}
  559. ),
  560. expected_result,
  561. 'A complex template'
  562. );
  563. });
  564. test("Error Handling", function() {
  565. raises(
  566. function() {
  567. Mustache.to_html(
  568. 'this is a partial\nyes it is. {{>partial}}',
  569. {},
  570. {partal: ''}
  571. );
  572. }, function(e) {
  573. return e.line === 2 && e.character === 12;
  574. },
  575. 'Missing partial line and character correctness.'
  576. );
  577. raises(
  578. function() {
  579. Mustache.to_html(
  580. 'this is a partial\nyes it is. {{>partial}}',
  581. {},
  582. {partial: 'error in {{#foobar}}'}
  583. );
  584. }, function(e) {
  585. return e.message === '[partial](1,21): Closing section tag "foobar" expected.'
  586. },
  587. 'Unbalanced section correctness (Part 1).'
  588. );
  589. raises(
  590. function() {
  591. Mustache.to_html(
  592. 'something something something {{/darkside}}.',
  593. {},
  594. {}
  595. );
  596. }, function(e) {
  597. return e.message === '(1,31): Unbalanced End Section tag "{{/darkside}}".'
  598. },
  599. 'Unbalanced section correctness (Part 2).'
  600. );
  601. raises(
  602. function() {
  603. Mustache.to_html(
  604. 'this is a partial\nyes it is. {{>maria}}',
  605. {},
  606. {maria: 'error in {{#foobar}}this is the most aw\ns\nme think {{#evar}}hello joe{{/foobar}}{{/evar}}'}
  607. );
  608. }, function(e) {
  609. return e.message === '[maria](3,28): Unexpected section end tag "foobar", expected "evar".'
  610. },
  611. 'Partials metric correctness.'
  612. );
  613. });
  614. test("Regression Suite", function() {
  615. // matches bug_11_eating_whitespace.html
  616. equals(
  617. Mustache.to_html(
  618. '{{tag}} foo',
  619. { tag: "yo" },
  620. {}
  621. ),
  622. 'yo foo',
  623. 'Issue 11'
  624. );
  625. // matches delimiters_partial.html
  626. equals(
  627. Mustache.to_html(
  628. '{{#enumerate}}\n{{>partial}}\n{{/enumerate}}',
  629. { enumerate: [ { text: 'A' }, { text: 'B' } ] },
  630. { partial: '{{=[[ ]]=}}\n{{text}}\n[[={{ }}=]]' }
  631. ),
  632. '\n\n{{text}}\n\n\n\n{{text}}\n\n',
  633. 'Issue 44'
  634. );
  635. // matches bug_46_set_delimiter.html
  636. equals(
  637. Mustache.to_html(
  638. '{{=[[ ]]=}}[[#IsMustacheAwesome]]mustache is awesome![[/IsMustacheAwesome]]',
  639. {IsMustacheAwesome: true},
  640. {}
  641. ),
  642. 'mustache is awesome!',
  643. 'Issue 46'
  644. );
  645. // matches Issue #79
  646. equals(
  647. Mustache.to_html(
  648. '{{#inner}}{{f}}{{#inner}}{{b}}{{/inner}}{{/inner}}'
  649. , {
  650. inner: [{
  651. f: 'foo'
  652. , inner: [{
  653. b: 'bar'
  654. }]
  655. }]
  656. }
  657. , {}
  658. )
  659. , 'foobar'
  660. , 'Nested Sections with the same name'
  661. );
  662. equals(
  663. Mustache.to_html(
  664. '{{=~~ ~~=}} ~~>staticInfoPanel~~ ~~={{ }}=~~'
  665. , {}
  666. , { staticInfoPanel: 'Hello' }
  667. )
  668. , ' Hello '
  669. , 'Change Delimiter + Partial');
  670. });