1 | import Patternlibrary from '..'; |
||
2 | |||
3 | import equal from 'assert-dir-equal'; |
||
4 | import rimraf from 'rimraf'; |
||
5 | import mkdirp from 'mkdirp'; |
||
6 | |||
7 | var expect = require('chai').expect; |
||
8 | var $handlebars = require('../lib/vendor/handlebars'); |
||
9 | var stripHtml = require('striptags'); |
||
10 | |||
11 | /** |
||
12 | * Compares the output of a $handlebars template to an expected string. Data can also be passed to the template function. |
||
13 | * @param {string} input - String to compile into a $handlebars template. |
||
14 | * @param {string} expected - Expected output of the template. |
||
15 | * @param {object} data - Data to pass to the $handlebars context. |
||
16 | * @throws {AssertionError} Throws an error if the $handlebars output and expected output are not equal. |
||
17 | */ |
||
18 | function compare(input, expected, data) { |
||
19 | var template = $handlebars.compile(input); |
||
20 | expect(template(data || {})).to.equal(expected); |
||
21 | } |
||
22 | |||
23 | // A note about assertions: most of these tests use the `compare()` function |
||
24 | // above. However, in cases where the output needs to be modified in some way |
||
25 | // before it's compared, or in cases where a substring is being searched for, |
||
26 | // a $handlebars template is created manually inside of the test. |
||
27 | |||
28 | describe('Patternlibrary built-in Handlebars helpers', () => { |
||
29 | |||
30 | const FIXTURES = 'test/fixtures.staticpages/'; |
||
31 | |||
32 | const CLEAN_UP = !true; |
||
33 | |||
34 | var patternlibraryOptions = { |
||
35 | verbose: false, |
||
36 | dest : FIXTURES + 'build', |
||
37 | root : FIXTURES + 'pages/', |
||
38 | layouts : FIXTURES + 'layouts', |
||
39 | partials: FIXTURES + 'partials' |
||
40 | } |
||
41 | |||
42 | describe('Structural', () => { |
||
43 | |||
44 | describe('{{#repeat}}{{/repeat}}', () => { |
||
45 | it('prints content multiple times', function (done) { |
||
46 | |||
47 | patternlibraryOptions = { |
||
48 | verbose : false, |
||
49 | dest : FIXTURES + 'helper-repeat/build', |
||
50 | root : FIXTURES + 'helper-repeat/pages/', |
||
51 | layouts : FIXTURES + 'helper-repeat/layouts/', |
||
52 | partials: FIXTURES + 'helper-repeat/partials/', |
||
53 | nogui : true, |
||
54 | testing : true |
||
55 | }; |
||
56 | rimraf.sync(FIXTURES + 'helper-repeat/build'); mkdirp(FIXTURES + 'helper-repeat/build'); |
||
57 | var p = new Patternlibrary.Patternlibrary(patternlibraryOptions); |
||
58 | |||
59 | p.run(); |
||
60 | |||
61 | setTimeout( function () { |
||
62 | equal(FIXTURES + 'helper-repeat/expected/index.html', FIXTURES + 'helper-repeat/build/index.html'); |
||
63 | if (CLEAN_UP) rimraf.sync(FIXTURES + 'helper-repeat/build'); |
||
64 | done(); |
||
65 | }, 250); |
||
66 | |||
67 | }); |
||
68 | }); |
||
69 | |||
70 | describe('{{#ifEqual}}{{/ifEqual}}', () => { |
||
71 | it('compares two values', function (done) { |
||
72 | |||
73 | patternlibraryOptions = { |
||
74 | verbose : false, |
||
75 | dest : FIXTURES + 'helper-ifequal/build', |
||
76 | root : FIXTURES + 'helper-ifequal/pages/', |
||
77 | layouts : FIXTURES + 'helper-ifequal/layouts/', |
||
78 | partials: FIXTURES + 'helper-ifequal/partials/', |
||
79 | nogui : true, |
||
80 | testing : true |
||
81 | }; |
||
82 | rimraf.sync(FIXTURES + 'helper-ifequal/build'); mkdirp(FIXTURES + 'helper-ifequal/build'); |
||
83 | var p = new Patternlibrary.Patternlibrary(patternlibraryOptions); |
||
84 | |||
85 | p.run(); |
||
86 | |||
87 | setTimeout( function () { |
||
88 | equal(FIXTURES + 'helper-ifequal/expected/index.html', FIXTURES + 'helper-ifequal/build/index.html'); |
||
89 | if (CLEAN_UP) rimraf.sync(FIXTURES + 'helper-ifequal/build'); |
||
90 | done(); |
||
91 | }, 250); |
||
92 | |||
93 | }); |
||
94 | }); |
||
95 | |||
96 | View Code Duplication | describe('{{#ifpage}}{{/ifpage}}', () => { |
|
0 ignored issues
–
show
Duplication
introduced
by
Loading history...
|
|||
97 | it('checks the current page', function (done) { |
||
98 | |||
99 | patternlibraryOptions = { |
||
100 | verbose : false, |
||
101 | dest : FIXTURES + 'helper-ifpage/build', |
||
102 | root : FIXTURES + 'helper-ifpage/pages/', |
||
103 | layouts : FIXTURES + 'helper-ifpage/layouts/', |
||
104 | partials: FIXTURES + 'helper-ifpage/partials/', |
||
105 | nogui : true, |
||
106 | testing : true |
||
107 | }; |
||
108 | rimraf.sync(FIXTURES + 'helper-ifpage/build'); mkdirp(FIXTURES + 'helper-ifpage/build'); |
||
109 | var p = new Patternlibrary.Patternlibrary(patternlibraryOptions); |
||
110 | |||
111 | p.run(); |
||
112 | |||
113 | setTimeout( function () { |
||
114 | equal(FIXTURES + 'helper-ifpage/expected/index.html', FIXTURES + 'helper-ifpage/build/index.html'); |
||
115 | equal(FIXTURES + 'helper-ifpage/expected/about.html', FIXTURES + 'helper-ifpage/build/about.html'); |
||
116 | if (CLEAN_UP) rimraf.sync(FIXTURES + 'helper-ifpage/build'); |
||
117 | done(); |
||
118 | }, 250); |
||
119 | |||
120 | }); |
||
121 | }); |
||
122 | |||
123 | View Code Duplication | describe('{{#unlesspage}}{{/unlesspage}}', () => { |
|
0 ignored issues
–
show
|
|||
124 | it('checks the current page (negation of ifpage)', function (done) { |
||
125 | |||
126 | patternlibraryOptions = { |
||
127 | verbose : false, |
||
128 | dest : FIXTURES + 'helper-unlesspage/build', |
||
129 | root : FIXTURES + 'helper-unlesspage/pages/', |
||
130 | layouts : FIXTURES + 'helper-unlesspage/layouts/', |
||
131 | partials: FIXTURES + 'helper-unlesspage/partials/', |
||
132 | nogui : true, |
||
133 | testing : true |
||
134 | }; |
||
135 | rimraf.sync(FIXTURES + 'helper-unlesspage/build'); mkdirp(FIXTURES + 'helper-unlesspage/build'); |
||
136 | var p = new Patternlibrary.Patternlibrary(patternlibraryOptions); |
||
137 | |||
138 | p.run(); |
||
139 | |||
140 | setTimeout( function () { |
||
141 | equal(FIXTURES + 'helper-unlesspage/expected/index.html', FIXTURES + 'helper-unlesspage/build/index.html'); |
||
142 | equal(FIXTURES + 'helper-unlesspage/expected/about.html', FIXTURES + 'helper-unlesspage/build/about.html'); |
||
143 | if (CLEAN_UP) rimraf.sync(FIXTURES + 'helper-unlesspage/build'); |
||
144 | done(); |
||
145 | }, 250); |
||
146 | |||
147 | }); |
||
148 | }); |
||
149 | |||
150 | }); |
||
151 | |||
152 | describe('Formatting', () => { |
||
153 | |||
154 | describe('{{#code}}{{/code}}', () => { |
||
155 | it('renders code blocks', function (done) { |
||
156 | |||
157 | patternlibraryOptions = { |
||
158 | verbose : false, |
||
159 | dest : FIXTURES + 'helper-code/build', |
||
160 | root : FIXTURES + 'helper-code/pages/', |
||
161 | layouts : FIXTURES + 'helper-code/layouts/', |
||
162 | partials: FIXTURES + 'helper-code/partials/', |
||
163 | nogui : true, |
||
164 | testing : true |
||
165 | }; |
||
166 | rimraf.sync(FIXTURES + 'helper-code/build'); mkdirp(FIXTURES + 'helper-code/build'); |
||
167 | var p = new Patternlibrary.Patternlibrary(patternlibraryOptions); |
||
168 | |||
169 | p.run(); |
||
170 | |||
171 | setTimeout( function () { |
||
172 | equal(FIXTURES + 'helper-code/expected/index.html', FIXTURES + 'helper-code/build/index.html'); |
||
173 | if (CLEAN_UP) rimraf.sync(FIXTURES + 'helper-code/build'); |
||
174 | done(); |
||
175 | }, 250); |
||
176 | |||
177 | }); |
||
178 | }); |
||
179 | |||
180 | describe('{{#markdown}}{{/markdown}}', () => { |
||
181 | it('converts Markdown to HTML (block-helper)', function (done) { |
||
182 | |||
183 | patternlibraryOptions = { |
||
184 | verbose : false, |
||
185 | dest : FIXTURES + 'helper-markdown/build', |
||
186 | root : FIXTURES + 'helper-markdown/pages/', |
||
187 | layouts : FIXTURES + 'helper-markdown/layouts/', |
||
188 | partials: FIXTURES + 'helper-markdown/partials/', |
||
189 | nogui : true, |
||
190 | testing : true |
||
191 | }; |
||
192 | rimraf.sync(FIXTURES + 'helper-markdown/build'); mkdirp(FIXTURES + 'helper-markdown/build'); |
||
193 | var p = new Patternlibrary.Patternlibrary(patternlibraryOptions); |
||
194 | |||
195 | p.run(); |
||
196 | |||
197 | setTimeout( function () { |
||
198 | equal(FIXTURES + 'helper-markdown/expected/index.html', FIXTURES + 'helper-markdown/build/index.html'); |
||
199 | if (CLEAN_UP) rimraf.sync(FIXTURES + 'helper-markdown/build'); |
||
200 | done(); |
||
201 | }, 250); |
||
202 | |||
203 | }); |
||
204 | }); |
||
205 | |||
206 | describe('{{md}}', () => { |
||
207 | it('converts Markdown to HTML', () => { |
||
208 | compare('{{md "**Bold**"}}', '<p><strong>Bold</strong></p>\n'); |
||
209 | }); |
||
210 | }); |
||
211 | |||
212 | describe('{{#heading}}{{/heading}}', () => { |
||
213 | it('creates a heading of a specific level', () => { |
||
214 | var expected = '<h1 id="title" class="docs-heading">Title<a class="docs-heading-icon" href="#title"></a></h1>'; |
||
215 | |||
216 | compare('{{#heading 1}}Title{{/heading}}', expected); |
||
217 | }); |
||
218 | |||
219 | it('creates a heading with a custom ID', () => { |
||
220 | var expected = '<h1 id="custom" class="docs-heading">Title<a class="docs-heading-icon" href="#custom"></a></h1>'; |
||
221 | |||
222 | compare('{{#heading 1 "custom"}}Title{{/heading}}', expected); |
||
223 | }); |
||
224 | }); |
||
225 | |||
226 | describe('{{escape}}', () => { |
||
227 | it('escapes text for use in a URL hash', () => { |
||
228 | compare('{{escape "this text"}}', 'this-text'); |
||
229 | }); |
||
230 | }); |
||
231 | |||
232 | describe('{{toUpper}}', () => { |
||
233 | it('capitalizes the first letter of a string', () => { |
||
234 | compare('{{toUpper "kittens"}}', 'Kittens'); |
||
235 | }); |
||
236 | }); |
||
237 | |||
238 | describe('{{toLower}}', () => { |
||
239 | it('converts a string to lowercase', () => { |
||
240 | compare('{{toLower "SHOUT"}}', 'shout') |
||
241 | }); |
||
242 | }); |
||
243 | |||
244 | describe('{{raw}}{{/raw}}', () => { |
||
245 | it('ignores $handlebars', () => { |
||
246 | compare('{{{{raw}}}}{{ignore}}{{{{/raw}}}}', '{{ignore}}'); |
||
247 | }); |
||
248 | }); |
||
249 | |||
250 | describe('{{#filter}}{{/filter}}', () => { |
||
251 | it('filters private SassDoc and JSDoc objects', () => { |
||
252 | var data = { |
||
253 | item: { access: 'private' } |
||
254 | }; |
||
255 | |||
256 | compare('{{#filter item}}Private{{/filter}}', '', data); |
||
257 | }); |
||
258 | |||
259 | it('displays public SassDoc and JSDoc objects', () => { |
||
260 | var data = { |
||
261 | item: { access: 'public' } |
||
262 | }; |
||
263 | |||
264 | compare('{{#filter item}}Public{{/filter}}', 'Public', data); |
||
265 | }); |
||
266 | |||
267 | it('filters SassDoc aliases', () => { |
||
268 | var data = { |
||
269 | item: { alias: true } |
||
270 | }; |
||
271 | |||
272 | compare('{{#filter item}}Alias{{/filter}}', '', data); |
||
273 | }); |
||
274 | }); |
||
275 | }); |
||
276 | |||
277 | describe('JavaScript', () => { |
||
278 | describe('{{writeJsConstructor}}', () => { |
||
279 | it('prints formatted JavaScript code to initialize a Patternlibrary/Siteapp plugin', () => { |
||
280 | var template = $handlebars.compile('{{writeJsConstructor "Plugin"}}'); |
||
281 | var output = template(); |
||
282 | |||
283 | expect(output, 'Should be formatted by Highlight.js').to.contain('hljs'); |
||
284 | expect(stripHtml(output), 'Should include Patternlibrary/Siteapp code').to.equal('var elem = new YourApp.Plugin(element, options);'); |
||
285 | }); |
||
286 | }); |
||
287 | |||
288 | describe('{{writeJsFunction}}', () => { |
||
289 | it('prints a JavaScript function with no parameters', () => { |
||
290 | var data = { |
||
291 | method: { |
||
292 | name: 'petKitty', |
||
293 | params: [] |
||
294 | } |
||
295 | }; |
||
296 | |||
297 | var template = $handlebars.compile('{{writeJsFunction method}}'); |
||
298 | var output = template(data); |
||
299 | |||
300 | expect(stripHtml(output)).to.equal(`$('#element').your_app('petKitty');`); |
||
301 | }); |
||
302 | |||
303 | it('prints a JavaScript function with parameters', () => { |
||
304 | var data = { |
||
305 | method: { |
||
306 | name: 'petKitty', |
||
307 | params: [ |
||
308 | { name: 'param1' }, |
||
309 | { name: 'param2' } |
||
310 | ] |
||
311 | } |
||
312 | }; |
||
313 | |||
314 | var template = $handlebars.compile('{{writeJsFunction method}}'); |
||
315 | var output = template(data); |
||
316 | |||
317 | expect(stripHtml(output)).to.equal(`$('#element').your_app('petKitty', param1, param2);`); |
||
318 | }); |
||
319 | }); |
||
320 | |||
321 | describe('{{formatJsModule}}', () => { |
||
322 | it('converts a JSDoc module definition to a filename', () => { |
||
323 | compare('{{formatJsModule "module:foundation.toggler"}}', 'foundation.toggler.js'); |
||
324 | }); |
||
325 | }); |
||
326 | |||
327 | describe('{{formatJsOptionName}}', () => { |
||
328 | it('converts a plugin option name to an HTML data attribute', () => { |
||
329 | compare('{{formatJsOptionName "optionName"}}', 'data-option-name'); |
||
330 | }); |
||
331 | }); |
||
332 | |||
333 | describe('{{formatJsOptionValue}}', () => { |
||
334 | it('prints non-String values as-is', () => { |
||
335 | var data = { |
||
336 | value: '0' |
||
337 | }; |
||
338 | |||
339 | compare('{{formatJsOptionValue value}}', '0', data); |
||
340 | }); |
||
341 | |||
342 | it('prints String values without the quotes on either side', () => { |
||
343 | var data = { |
||
344 | singleQuotes: "'value'", |
||
345 | multiQuotes: '"value"' |
||
346 | }; |
||
347 | |||
348 | compare(`{{formatJsOptionValue singleQuotes}}`, 'value', data); |
||
349 | compare(`{{formatJsOptionValue multiQuotes}}`, 'value', data); |
||
350 | }); |
||
351 | |||
352 | it('returns an empty string if an option is missing a value', () => { |
||
353 | compare('{{formatJsOptionValue undef}}', ''); |
||
354 | }); |
||
355 | }); |
||
356 | |||
357 | describe('{{formatJsEventName}}', () => { |
||
358 | it('formats a JSDoc event to look like "YourApp"-namespaced events', () => { |
||
359 | var data = { |
||
360 | name: 'event', |
||
361 | title: 'Plugin' |
||
362 | }; |
||
363 | |||
364 | compare('{{formatJsEventName name title}}', 'event.zf.plugin', data); |
||
365 | }); |
||
366 | |||
367 | it('handles plugin names that are intercapped', () => { |
||
368 | var data = { |
||
369 | name: 'event', |
||
370 | title: 'PluginName' |
||
371 | }; |
||
372 | |||
373 | compare('{{formatJsEventName name title}}', 'event.zf.pluginName', data); |
||
374 | }); |
||
375 | }); |
||
376 | }); |
||
377 | |||
378 | describe('Links', () => { |
||
379 | describe('{{editLink}}', () => { |
||
380 | it('generates a GitHub edit link point to a repository, branch, and file', () => { |
||
381 | compare('{{editLink "foundation-sites" "master" "docs/pages/index.html"}}', 'https://github.com/zurb/foundation-sites/edit/master/docs/pages/index.md'); |
||
382 | }); |
||
383 | }); |
||
384 | |||
385 | describe('{{issueLink}}', () => { |
||
386 | it('generates a GitHub link to open a new issue, with a preset title', () => { |
||
387 | var template = $handlebars.compile('{{issueLink "foundation-sites" "Plugin"}}'); |
||
388 | var output = template(); |
||
389 | |||
390 | expect(output, 'links to GitHub issue tracker').to.contain('https://github.com/zurb/foundation-sites/issues/new?title'); |
||
391 | expect(output, 'includes tag in preset title').to.contain('Plugin'); |
||
392 | expect(output, 'includes super loud title that you should replace').to.contain('ISSUE%20NAME%20HERE'); |
||
393 | }); |
||
394 | }); |
||
395 | }); |
||
396 | |||
397 | describe('Sass', () => { |
||
398 | describe('{{writeSassMixin}}', () => { |
||
399 | it('formats a Sass mixin with no parameters', () => { |
||
400 | var data = { |
||
401 | mixin: { |
||
402 | context: { name: 'name' } |
||
403 | } |
||
404 | }; |
||
405 | |||
406 | var template = $handlebars.compile('{{writeSassMixin mixin}}'); |
||
407 | var output = stripHtml(template(data)); |
||
408 | |||
409 | expect(output).to.equal('@include name;'); |
||
410 | }); |
||
411 | |||
412 | it('formats a Sass mixin with parameters', () => { |
||
413 | var data = { |
||
414 | mixin: { |
||
415 | context: { name: 'name' }, |
||
416 | parameter: [ |
||
417 | { name: 'param1' }, |
||
418 | { name: 'param2' } |
||
419 | ] |
||
420 | } |
||
421 | }; |
||
422 | |||
423 | var template = $handlebars.compile('{{writeSassMixin mixin}}'); |
||
424 | var output = stripHtml(template(data)); |
||
425 | |||
426 | expect(output).to.equal('@include name($param1, $param2);'); |
||
427 | }); |
||
428 | |||
429 | it('formats a Sass mixin with a @content directive', () => { |
||
430 | var data = { |
||
431 | mixin: { |
||
432 | context: { name: 'name' }, |
||
433 | content: 'Content' |
||
434 | } |
||
435 | }; |
||
436 | |||
437 | var template = $handlebars.compile('{{writeSassMixin mixin}}'); |
||
438 | var output = stripHtml(template(data)); |
||
439 | |||
440 | expect(output).to.equal('@include name { }'); |
||
441 | }); |
||
442 | }); |
||
443 | |||
444 | describe('{{writeSassFunction}}', () => { |
||
445 | it('formats a Sass function with no parameters', () => { |
||
446 | var data = { |
||
447 | func: { |
||
448 | context: { name: 'name' } |
||
449 | } |
||
450 | }; |
||
451 | |||
452 | var template = $handlebars.compile('{{writeSassFunction func}}'); |
||
453 | var output = stripHtml(template(data)); |
||
454 | |||
455 | expect(output).to.equal('name()'); |
||
456 | }); |
||
457 | |||
458 | it('formats a Sass function with parameters', () => { |
||
459 | var data = { |
||
460 | func: { |
||
461 | context: { name: 'name' }, |
||
462 | parameter: [ |
||
463 | { name: 'param1' }, |
||
464 | { name: 'param2' } |
||
465 | ] |
||
466 | } |
||
467 | }; |
||
468 | |||
469 | var template = $handlebars.compile('{{writeSassFunction func}}'); |
||
470 | var output = stripHtml(template(data)); |
||
471 | |||
472 | expect(output).to.equal('name($param1, $param2)'); |
||
473 | }); |
||
474 | }); |
||
475 | |||
476 | describe('{{writeSassVariable}}', () => { |
||
477 | it('formats a Sass variable', () => { |
||
478 | var data = { |
||
479 | variable: { |
||
480 | context: { |
||
481 | name: 'name', |
||
482 | value: 'value' |
||
483 | } |
||
484 | } |
||
485 | }; |
||
486 | |||
487 | var template = $handlebars.compile('{{writeSassVariable variable}}'); |
||
488 | var output = stripHtml(template(data)); |
||
489 | |||
490 | expect(output).to.equal('$name: value;'); |
||
491 | }); |
||
492 | }); |
||
493 | |||
494 | describe('{{formatSassTypes}}', () => { |
||
495 | it('formats a SassDoc @type annotation with one value', () => { |
||
496 | compare('{{formatSassTypes "String"}}', 'String'); |
||
497 | }); |
||
498 | |||
499 | it('formats a SassDoc @type annotation with multiple values', () => { |
||
500 | compare('{{formatSassTypes "String|List"}}', 'String or List'); |
||
501 | }); |
||
502 | |||
503 | it('prints an empty string if no value is defined', () => { |
||
504 | compare('{{formatSassTypes undef}}', ''); |
||
505 | }); |
||
506 | }); |
||
507 | |||
508 | describe('{{formatSassValue}}', () => { |
||
509 | it('formats basic values as-is', () => { |
||
510 | compare('{{formatSassValue "value"}}', 'value'); |
||
511 | }); |
||
512 | |||
513 | it('formats maps with each value on a separate line', () => { |
||
514 | var template = $handlebars.compile(`{{formatSassValue '(one: one,two: two)'}}`); |
||
515 | var output = template(); |
||
516 | |||
517 | expect(output).to.equal('one: one<br>two: two'); |
||
518 | }); |
||
519 | |||
520 | it('returns the word "None" for undefined values', () => { |
||
521 | var template = $handlebars.compile('{{formatSassValue undef}}'); |
||
522 | var output = template(); |
||
523 | |||
524 | expect(output).to.contain('None'); |
||
525 | }); |
||
526 | }); |
||
527 | |||
528 | describe('{{writeSassLink}}', () => { |
||
529 | it('formats a SassDoc @link annotation', () => { |
||
530 | var data = { |
||
531 | link: [{ |
||
532 | url: '#', |
||
533 | caption: 'Caption' |
||
534 | }] |
||
535 | }; |
||
536 | |||
537 | compare('{{writeSassLink link}}', '<p><strong>Learn more:</strong> <a href="#">Caption</a></p>', data); |
||
538 | }); |
||
539 | |||
540 | it('prints an empty string for an undefined link', () => { |
||
541 | compare('{{writeSassLink undef}}', ''); |
||
542 | }); |
||
543 | }); |
||
544 | }); |
||
545 | }); |
||
546 |