UNPKG

28.2 kBJavaScriptView Raw
1/* jshint mocha: true */
2/* eslint-env node, mocha */
3
4/**
5 * Module dependencies.
6 */
7
8var ejs = require('..');
9var fs = require('fs');
10var read = fs.readFileSync;
11var assert = require('assert');
12var path = require('path');
13var LRU = require('lru-cache');
14
15try {
16 fs.mkdirSync(__dirname + '/tmp');
17} catch (ex) {
18 if (ex.code !== 'EEXIST') {
19 throw ex;
20 }
21}
22
23// From https://gist.github.com/pguillory/729616
24function hook_stdio(stream, callback) {
25 var old_write = stream.write;
26
27 stream.write = (function() {
28 return function(string, encoding, fd) {
29 callback(string, encoding, fd);
30 };
31 })(stream.write);
32
33 return function() {
34 stream.write = old_write;
35 };
36}
37
38/**
39 * Load fixture `name`.
40 */
41
42function fixture(name) {
43 return read('test/fixtures/' + name, 'utf8');
44}
45
46/**
47 * User fixtures.
48 */
49
50var users = [];
51users.push({name: 'geddy'});
52users.push({name: 'neil'});
53users.push({name: 'alex'});
54
55suite('ejs.compile(str, options)', function () {
56 test('compile to a function', function () {
57 var fn = ejs.compile('<p>yay</p>');
58 assert.equal(fn(), '<p>yay</p>');
59 });
60
61 test('empty input works', function () {
62 var fn = ejs.compile('');
63 assert.equal(fn(), '');
64 });
65
66 test('throw if there are syntax errors', function () {
67 try {
68 ejs.compile(fixture('fail.ejs'));
69 }
70 catch (err) {
71 assert.ok(err.message.indexOf('compiling ejs') > -1);
72
73 try {
74 ejs.compile(fixture('fail.ejs'), {filename: 'fail.ejs'});
75 }
76 catch (err) {
77 assert.ok(err.message.indexOf('fail.ejs') > -1);
78 return;
79 }
80 }
81 throw new Error('no error reported when there should be');
82 });
83
84 test('allow customizing delimiter local var', function () {
85 var fn;
86 fn = ejs.compile('<p><?= name ?></p>', {delimiter: '?'});
87 assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');
88
89 fn = ejs.compile('<p><:= name :></p>', {delimiter: ':'});
90 assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');
91
92 fn = ejs.compile('<p><$= name $></p>', {delimiter: '$'});
93 assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');
94 });
95
96 test('default to using ejs.delimiter', function () {
97 var fn;
98 ejs.delimiter = '&';
99 fn = ejs.compile('<p><&= name &></p>');
100 assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');
101
102 fn = ejs.compile('<p><|= name |></p>', {delimiter: '|'});
103 assert.equal(fn({name: 'geddy'}), '<p>geddy</p>');
104 delete ejs.delimiter;
105 });
106
107 test('have a working client option', function () {
108 var fn;
109 var str;
110 var preFn;
111 fn = ejs.compile('<p><%= foo %></p>', {client: true});
112 str = fn.toString();
113 if (!process.env.running_under_istanbul) {
114 eval('var preFn = ' + str);
115 assert.equal(preFn({foo: 'bar'}), '<p>bar</p>');
116 }
117 });
118
119 test('support client mode without locals', function () {
120 var fn;
121 var str;
122 var preFn;
123 fn = ejs.compile('<p><%= "foo" %></p>', {client: true});
124 str = fn.toString();
125 if (!process.env.running_under_istanbul) {
126 eval('var preFn = ' + str);
127 assert.equal(preFn(), '<p>foo</p>');
128 }
129 });
130
131 test('not include rethrow() in client mode if compileDebug is false', function () {
132 var fn = ejs.compile('<p><%= "foo" %></p>', {
133 client: true,
134 compileDebug: false
135 });
136 // There could be a `rethrow` in the function declaration
137 assert((fn.toString().match(/rethrow/g) || []).length <= 1);
138 });
139
140 test('support custom escape function', function () {
141 var customEscape;
142 var fn;
143 customEscape = function customEscape(str) {
144 return !str ? '' : str.toUpperCase();
145 };
146 fn = ejs.compile('HELLO <%= name %>', {escape: customEscape});
147 assert.equal(fn({name: 'world'}), 'HELLO WORLD');
148 });
149
150 test('support custom escape function in client mode', function () {
151 var customEscape;
152 var fn;
153 var str;
154 customEscape = function customEscape(str) {
155 return !str ? '' : str.toUpperCase();
156 };
157 fn = ejs.compile('HELLO <%= name %>', {escape: customEscape, client: true});
158 str = fn.toString();
159 if (!process.env.running_under_istanbul) {
160 eval('var preFn = ' + str);
161 assert.equal(preFn({name: 'world'}), 'HELLO WORLD'); // eslint-disable-line no-undef
162 }
163 });
164
165 test('strict mode works', function () {
166 assert.equal(ejs.render(fixture('strict.ejs'), {}, {strict: true}), 'true');
167 });
168
169});
170
171suite('ejs.render(str, data, opts)', function () {
172 test('render the template', function () {
173 assert.equal(ejs.render('<p>yay</p>'), '<p>yay</p>');
174 });
175
176 test('empty input works', function () {
177 assert.equal(ejs.render(''), '');
178 });
179
180 test('undefined renders nothing escaped', function () {
181 assert.equal(ejs.render('<%= undefined %>'), '');
182 });
183
184 test('undefined renders nothing raw', function () {
185 assert.equal(ejs.render('<%- undefined %>'), '');
186 });
187
188 test('null renders nothing escaped', function () {
189 assert.equal(ejs.render('<%= null %>'), '');
190 });
191
192 test('null renders nothing raw', function () {
193 assert.equal(ejs.render('<%- null %>'), '');
194 });
195
196 test('zero-value data item renders something escaped', function () {
197 assert.equal(ejs.render('<%= 0 %>'), '0');
198 });
199
200 test('zero-value data object renders something raw', function () {
201 assert.equal(ejs.render('<%- 0 %>'), '0');
202 });
203
204 test('accept locals', function () {
205 assert.equal(ejs.render('<p><%= name %></p>', {name: 'geddy'}),
206 '<p>geddy</p>');
207 });
208
209 test('accept locals without using with() {}', function () {
210 assert.equal(ejs.render('<p><%= locals.name %></p>', {name: 'geddy'},
211 {_with: false}),
212 '<p>geddy</p>');
213 assert.throws(function() {
214 ejs.render('<p><%= name %></p>', {name: 'geddy'},
215 {_with: false});
216 }, /name is not defined/);
217 });
218
219 test('accept custom name for locals', function () {
220 ejs.localsName = 'it';
221 assert.equal(ejs.render('<p><%= it.name %></p>', {name: 'geddy'},
222 {_with: false}),
223 '<p>geddy</p>');
224 assert.throws(function() {
225 ejs.render('<p><%= name %></p>', {name: 'geddy'},
226 {_with: false});
227 }, /name is not defined/);
228 ejs.localsName = 'locals';
229 });
230
231 test('support caching', function () {
232 var file = __dirname + '/tmp/render.ejs';
233 var options = {cache: true, filename: file};
234 var out = ejs.render('<p>Old</p>', {}, options);
235 var expected = '<p>Old</p>';
236 assert.equal(out, expected);
237 // Assert no change, still in cache
238 out = ejs.render('<p>New</p>', {}, options);
239 assert.equal(out, expected);
240 });
241
242 test('support LRU caching', function () {
243 var oldCache = ejs.cache;
244 var file = __dirname + '/tmp/render.ejs';
245 var options = {cache: true, filename: file};
246 var out;
247 var expected = '<p>Old</p>';
248
249 // Switch to LRU
250 ejs.cache = LRU();
251
252 out = ejs.render('<p>Old</p>', {}, options);
253 assert.equal(out, expected);
254 // Assert no change, still in cache
255 out = ejs.render('<p>New</p>', {}, options);
256 assert.equal(out, expected);
257
258 // Restore system cache
259 ejs.cache = oldCache;
260 });
261
262 test('opts.context', function () {
263 var ctxt = {foo: 'FOO'};
264 var out = ejs.render('<%= this.foo %>', {}, {context: ctxt});
265 assert.equal(out, ctxt.foo);
266 });
267});
268
269suite('ejs.renderFile(path, [data], [options], fn)', function () {
270 test('render a file', function(done) {
271 ejs.renderFile('test/fixtures/para.ejs', function(err, html) {
272 if (err) {
273 return done(err);
274 }
275 assert.equal(html, '<p>hey</p>\n');
276 done();
277 });
278 });
279
280 test('accept locals', function(done) {
281 var data = {name: 'fonebone'};
282 var options = {delimiter: '$'};
283 ejs.renderFile('test/fixtures/user.ejs', data, options, function(err, html) {
284 if (err) {
285 return done(err);
286 }
287 assert.equal(html, '<h1>fonebone</h1>\n');
288 done();
289 });
290 });
291
292 test('accept locals without using with() {}', function(done) {
293 var data = {name: 'fonebone'};
294 var options = {delimiter: '$', _with: false};
295 var doneCount = 0;
296 ejs.renderFile('test/fixtures/user-no-with.ejs', data, options, function(err, html) {
297 if (err) {
298 if (doneCount === 2) {
299 return;
300 }
301 doneCount = 2;
302 return done(err);
303 }
304 assert.equal(html, '<h1>fonebone</h1>\n');
305 doneCount++;
306 if (doneCount === 2) {
307 done();
308 }
309 });
310 ejs.renderFile('test/fixtures/user.ejs', data, options, function(err) {
311 if (!err) {
312 if (doneCount === 2) {
313 return;
314 }
315 doneCount = 2;
316 return done(new Error('error not thrown'));
317 }
318 doneCount++;
319 if (doneCount === 2) {
320 done();
321 }
322 });
323 });
324
325 test('not catch err thrown by callback', function(done) {
326 var data = {name: 'fonebone'};
327 var options = {delimiter: '$'};
328 var counter = 0;
329
330 var d = require('domain').create();
331 d.on('error', function (err) {
332 assert.equal(counter, 1);
333 assert.equal(err.message, 'Exception in callback');
334 done();
335 });
336 d.run(function () {
337 // process.nextTick() needed to work around mochajs/mocha#513
338 //
339 // tl;dr: mocha doesn't support synchronous exception throwing in
340 // domains. Have to make it async. Ticket closed because: "domains are
341 // deprecated :D"
342 process.nextTick(function () {
343 ejs.renderFile('test/fixtures/user.ejs', data, options, function(err) {
344 counter++;
345 if (err) {
346 assert.notEqual(err.message, 'Exception in callback');
347 return done(err);
348 }
349 throw new Error('Exception in callback');
350 });
351 });
352 });
353 });
354
355 test('support caching', function (done) {
356 var expected = '<p>Old</p>';
357 var file = __dirname + '/tmp/renderFile.ejs';
358 var options = {cache: true};
359 fs.writeFileSync(file, '<p>Old</p>');
360
361 ejs.renderFile(file, {}, options, function (err, out) {
362 if (err) {
363 done(err);
364 }
365 fs.writeFileSync(file, '<p>New</p>');
366 assert.equal(out, expected);
367
368 ejs.renderFile(file, {}, options, function (err, out) {
369 if (err) {
370 done(err);
371 }
372 // Assert no change, still in cache
373 assert.equal(out, expected);
374 done();
375 });
376 });
377 });
378
379 test('opts.context', function (done) {
380 var ctxt = {foo: 'FOO'};
381 ejs.renderFile('test/fixtures/with-context.ejs', {}, {context: ctxt}, function(err, html) {
382 if (err) {
383 return done(err);
384 }
385 assert.equal(html, ctxt.foo + '\n');
386 done();
387 });
388
389 });
390});
391
392suite('cache specific', function () {
393 test('`clearCache` work properly', function () {
394 var expected = '<p>Old</p>';
395 var file = __dirname + '/tmp/clearCache.ejs';
396 var options = {cache: true, filename: file};
397 var out = ejs.render('<p>Old</p>', {}, options);
398 assert.equal(out, expected);
399
400 ejs.clearCache();
401
402 expected = '<p>New</p>';
403 out = ejs.render('<p>New</p>', {}, options);
404 assert.equal(out, expected);
405 });
406
407 test('`clearCache` work properly, LRU', function () {
408 var expected = '<p>Old</p>';
409 var oldCache = ejs.cache;
410 var file = __dirname + '/tmp/clearCache.ejs';
411 var options = {cache: true, filename: file};
412 var out;
413
414 ejs.cache = LRU();
415
416 out = ejs.render('<p>Old</p>', {}, options);
417 assert.equal(out, expected);
418 ejs.clearCache();
419 expected = '<p>New</p>';
420 out = ejs.render('<p>New</p>', {}, options);
421 assert.equal(out, expected);
422
423 ejs.cache = oldCache;
424 });
425
426 test('LRU with cache-size 1', function () {
427 var oldCache = ejs.cache;
428 var options;
429 var out;
430 var expected;
431 var file;
432
433 ejs.cache = LRU(1);
434
435 file = __dirname + '/tmp/render1.ejs';
436 options = {cache: true, filename: file};
437 out = ejs.render('<p>File1</p>', {}, options);
438 expected = '<p>File1</p>';
439 assert.equal(out, expected);
440
441 // Same filename, different template, but output
442 // should be the same because cache
443 file = __dirname + '/tmp/render1.ejs';
444 options = {cache: true, filename: file};
445 out = ejs.render('<p>ChangedFile1</p>', {}, options);
446 expected = '<p>File1</p>';
447 assert.equal(out, expected);
448
449 // Different filiename -- output should be different,
450 // and previous cache-entry should be evicted
451 file = __dirname + '/tmp/render2.ejs';
452 options = {cache: true, filename: file};
453 out = ejs.render('<p>File2</p>', {}, options);
454 expected = '<p>File2</p>';
455 assert.equal(out, expected);
456
457 // Entry with first filename should now be out of cache,
458 // results should be different
459 file = __dirname + '/tmp/render1.ejs';
460 options = {cache: true, filename: file};
461 out = ejs.render('<p>ChangedFile1</p>', {}, options);
462 expected = '<p>ChangedFile1</p>';
463 assert.equal(out, expected);
464
465 ejs.cache = oldCache;
466 });
467});
468
469suite('<%', function () {
470 test('without semicolons', function () {
471 assert.equal(ejs.render(fixture('no.semicolons.ejs')),
472 fixture('no.semicolons.html'));
473 });
474});
475
476suite('<%=', function () {
477 test('escape &amp;<script>', function () {
478 assert.equal(ejs.render('<%= name %>', {name: '&nbsp;<script>'}),
479 '&amp;nbsp;&lt;script&gt;');
480 });
481
482 test('should escape \'', function () {
483 assert.equal(ejs.render('<%= name %>', {name: 'The Jones\'s'}),
484 'The Jones&#39;s');
485 });
486
487 test('should escape &foo_bar;', function () {
488 assert.equal(ejs.render('<%= name %>', {name: '&foo_bar;'}),
489 '&amp;foo_bar;');
490 });
491
492 test('should accept custom function', function() {
493
494 var customEscape = function customEscape(str) {
495 return !str ? '' : str.toUpperCase();
496 };
497
498 assert.equal(
499 ejs.render('<%= name %>', {name: 'The Jones\'s'}, {escape: customEscape}),
500 'THE JONES\'S'
501 );
502 });
503});
504
505suite('<%-', function () {
506 test('not escape', function () {
507 assert.equal(ejs.render('<%- name %>', {name: '<script>'}),
508 '<script>');
509 });
510
511 test('terminate gracefully if no close tag is found', function () {
512 try {
513 ejs.compile('<h1>oops</h1><%- name ->');
514 throw new Error('Expected parse failure');
515 }
516 catch (err) {
517 assert.ok(err.message.indexOf('Could not find matching close tag for') > -1);
518 }
519 });
520});
521
522suite('%>', function () {
523 test('produce newlines', function () {
524 assert.equal(ejs.render(fixture('newlines.ejs'), {users: users}),
525 fixture('newlines.html'));
526 });
527 test('works with `-%>` interspersed', function () {
528 assert.equal(ejs.render(fixture('newlines.mixed.ejs'), {users: users}),
529 fixture('newlines.mixed.html'));
530 });
531 test('consecutive tags work', function () {
532 assert.equal(ejs.render(fixture('consecutive-tags.ejs')),
533 fixture('consecutive-tags.html'));
534 });
535});
536
537suite('-%>', function () {
538 test('not produce newlines', function () {
539 assert.equal(ejs.render(fixture('no.newlines.ejs'), {users: users}),
540 fixture('no.newlines.html'));
541 });
542 test('stack traces work', function () {
543 try {
544 ejs.render(fixture('no.newlines.error.ejs'));
545 }
546 catch (e) {
547 if (e.message.indexOf('>> 4| <%= qdata %>') > -1) {
548 return;
549 }
550 throw e;
551 }
552 throw new Error('Expected ReferenceError');
553 });
554
555 test('works with unix style', function () {
556 var content = '<ul><% -%>\n'
557 + '<% users.forEach(function(user){ -%>\n'
558 + '<li><%= user.name -%></li>\n'
559 + '<% }) -%>\n'
560 + '</ul><% -%>\n';
561
562 var expectedResult = '<ul><li>geddy</li>\n<li>neil</li>\n<li>alex</li>\n</ul>';
563 var fn;
564 fn = ejs.compile(content);
565 assert.equal(fn({users: users}),
566 expectedResult);
567 });
568
569 test('works with windows style', function () {
570 var content = '<ul><% -%>\r\n'
571 + '<% users.forEach(function(user){ -%>\r\n'
572 + '<li><%= user.name -%></li>\r\n'
573 + '<% }) -%>\r\n'
574 + '</ul><% -%>\r\n';
575
576 var expectedResult = '<ul><li>geddy</li>\r\n<li>neil</li>\r\n<li>alex</li>\r\n</ul>';
577 var fn;
578 fn = ejs.compile(content);
579 assert.equal(fn({users: users}),
580 expectedResult);
581 });
582});
583
584suite('<%%', function () {
585 test('produce literals', function () {
586 assert.equal(ejs.render('<%%- "foo" %>'),
587 '<%- "foo" %>');
588 });
589 test('work without an end tag', function () {
590 assert.equal(ejs.render('<%%'), '<%');
591 assert.equal(ejs.render(fixture('literal.ejs'), {}, {delimiter: ' '}),
592 fixture('literal.html'));
593 });
594});
595
596suite('%%>', function () {
597 test('produce literal', function () {
598 assert.equal(ejs.render('%%>'),
599 '%>');
600 assert.equal(ejs.render(' >', {}, {delimiter: ' '}),
601 ' >');
602 });
603});
604
605suite('<%_ and _%>', function () {
606 test('slurps spaces and tabs', function () {
607 assert.equal(ejs.render(fixture('space-and-tab-slurp.ejs'), {users: users}),
608 fixture('space-and-tab-slurp.html'));
609 });
610});
611
612suite('single quotes', function () {
613 test('not mess up the constructed function', function () {
614 assert.equal(ejs.render(fixture('single-quote.ejs')),
615 fixture('single-quote.html'));
616 });
617});
618
619suite('double quotes', function () {
620 test('not mess up the constructed function', function () {
621 assert.equal(ejs.render(fixture('double-quote.ejs')),
622 fixture('double-quote.html'));
623 });
624});
625
626suite('backslashes', function () {
627 test('escape', function () {
628 assert.equal(ejs.render(fixture('backslash.ejs')),
629 fixture('backslash.html'));
630 });
631});
632
633suite('messed up whitespace', function () {
634 test('work', function () {
635 assert.equal(ejs.render(fixture('messed.ejs'), {users: users}),
636 fixture('messed.html'));
637 });
638});
639
640suite('exceptions', function () {
641 test('produce useful stack traces', function () {
642 try {
643 ejs.render(fixture('error.ejs'), {}, {filename: 'error.ejs'});
644 }
645 catch (err) {
646 assert.equal(err.path, 'error.ejs');
647 assert.equal(err.stack.split('\n').slice(0, 8).join('\n'), fixture('error.out'));
648 return;
649 }
650 throw new Error('no error reported when there should be');
651 });
652
653 test('not include fancy stack info if compileDebug is false', function () {
654 try {
655 ejs.render(fixture('error.ejs'), {}, {
656 filename: 'error.ejs',
657 compileDebug: false
658 });
659 }
660 catch (err) {
661 assert.ok(!err.path);
662 assert.notEqual(err.stack.split('\n').slice(0, 8).join('\n'), fixture('error.out'));
663 return;
664 }
665 throw new Error('no error reported when there should be');
666 });
667
668 var unhook = null;
669 test('log JS source when debug is set', function (done) {
670 var out = '';
671 var needToExit = false;
672 unhook = hook_stdio(process.stdout, function (str) {
673 out += str;
674 if (needToExit) {
675 return;
676 }
677 if (out.indexOf('__output')) {
678 needToExit = true;
679 unhook();
680 unhook = null;
681 return done();
682 }
683 });
684 ejs.render(fixture('hello-world.ejs'), {}, {debug: true});
685 });
686 teardown(function() {
687 if (!unhook) {
688 return;
689 }
690 unhook();
691 unhook = null;
692 });
693});
694
695suite('rmWhitespace', function () {
696 test('works', function () {
697 assert.equal(ejs.render(fixture('rmWhitespace.ejs'), {}, {rmWhitespace: true}),
698 fixture('rmWhitespace.html'));
699 });
700});
701
702suite('include()', function () {
703 test('include ejs', function () {
704 var file = 'test/fixtures/include-simple.ejs';
705 assert.equal(ejs.render(fixture('include-simple.ejs'), {}, {filename: file}),
706 fixture('include-simple.html'));
707 });
708
709 test('include ejs fails without `filename`', function () {
710 try {
711 ejs.render(fixture('include-simple.ejs'));
712 }
713 catch (err) {
714 assert.ok(err.message.indexOf('requires the \'filename\' option') > -1);
715 return;
716 }
717 throw new Error('expected inclusion error');
718 });
719
720 test('strips BOM', function () {
721 assert.equal(
722 ejs.render('<%- include("fixtures/includes/bom.ejs") %>',
723 {}, {filename: path.join(__dirname, 'f.ejs')}),
724 '<p>This is a file with BOM.</p>\n');
725 });
726
727 test('include ejs with locals', function () {
728 var file = 'test/fixtures/include.ejs';
729 assert.equal(ejs.render(fixture('include.ejs'), {pets: users}, {filename: file, delimiter: '@'}),
730 fixture('include.html'));
731 });
732
733 test('include ejs with absolute path and locals', function () {
734 var file = 'test/fixtures/include-abspath.ejs';
735 assert.equal(ejs.render(fixture('include-abspath.ejs'),
736 {dir: path.join(__dirname, 'fixtures'), pets: users, path: path},
737 {filename: file, delimiter: '@'}),
738 fixture('include.html'));
739 });
740
741 test('include ejs with set root path', function () {
742 var file = 'test/fixtures/include-root.ejs';
743 var viewsPath = path.join(__dirname, 'fixtures');
744 assert.equal(ejs.render(fixture('include-root.ejs'), {pets: users}, {filename: file, delimiter: '@',root:viewsPath}),
745 fixture('include.html'));
746
747 });
748
749 test('work when nested', function () {
750 var file = 'test/fixtures/menu.ejs';
751 assert.equal(ejs.render(fixture('menu.ejs'), {pets: users}, {filename: file}),
752 fixture('menu.html'));
753 });
754
755 test('work with a variable path', function () {
756 var file = 'test/fixtures/menu_var.ejs';
757 var includePath = 'includes/menu-item';
758 assert.equal(ejs.render(fixture('menu.ejs'), {pets: users, varPath: includePath}, {filename: file}),
759 fixture('menu.html'));
760 });
761
762 test('include arbitrary files as-is', function () {
763 var file = 'test/fixtures/include.css.ejs';
764 assert.equal(ejs.render(fixture('include.css.ejs'), {pets: users}, {filename: file}),
765 fixture('include.css.html'));
766 });
767
768 test('pass compileDebug to include', function () {
769 var file = 'test/fixtures/include.ejs';
770 var fn;
771 fn = ejs.compile(fixture('include.ejs'), {
772 filename: file,
773 delimiter: '@',
774 compileDebug: false
775 });
776 try {
777 // Render without a required variable reference
778 fn({foo: 'asdf'});
779 }
780 catch(e) {
781 assert.equal(e.message, 'pets is not defined');
782 assert.ok(!e.path);
783 return;
784 }
785 throw new Error('no error reported when there should be');
786 });
787
788 test('is dynamic', function () {
789 fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>');
790 var file = 'test/fixtures/include_cache.ejs';
791 var options = {filename: file};
792 var out = ejs.compile(fixture('include_cache.ejs'), options);
793 assert.equal(out(), '<p>Old</p>\n');
794
795 fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>New</p>');
796 assert.equal(out(), '<p>New</p>\n');
797 });
798
799 test('support caching', function () {
800 fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>Old</p>');
801 var file = 'test/fixtures/include_cache.ejs';
802 var options = {cache: true, filename: file};
803 var out = ejs.render(fixture('include_cache.ejs'), {}, options);
804 var expected = fixture('include_cache.html');
805 assert.equal(out, expected);
806 out = ejs.render(fixture('include_cache.ejs'), {}, options);
807 // No change, still in cache
808 assert.equal(out, expected);
809 fs.writeFileSync(__dirname + '/tmp/include.ejs', '<p>New</p>');
810 out = ejs.render(fixture('include_cache.ejs'), {}, options);
811 assert.equal(out, expected);
812 });
813
814});
815
816suite('preprocessor include', function () {
817 test('work', function () {
818 var file = 'test/fixtures/include_preprocessor.ejs';
819 assert.equal(ejs.render(fixture('include_preprocessor.ejs'), {pets: users}, {filename: file, delimiter: '@'}),
820 fixture('include_preprocessor.html'));
821 });
822
823 test('no false positives', function () {
824 assert.equal(ejs.render('<% %> include foo <% %>'), ' include foo ');
825 });
826
827 test('fails without `filename`', function () {
828 try {
829 ejs.render(fixture('include_preprocessor.ejs'), {pets: users}, {delimiter: '@'});
830 }
831 catch (err) {
832 assert.ok(err.message.indexOf('requires the \'filename\' option') > -1);
833 return;
834 }
835 throw new Error('expected inclusion error');
836 });
837
838 test('strips BOM', function () {
839 assert.equal(
840 ejs.render('<% include fixtures/includes/bom.ejs %>',
841 {}, {filename: path.join(__dirname, 'f.ejs')}),
842 '<p>This is a file with BOM.</p>\n');
843 });
844
845 test('work when nested', function () {
846 var file = 'test/fixtures/menu_preprocessor.ejs';
847 assert.equal(ejs.render(fixture('menu_preprocessor.ejs'), {pets: users}, {filename: file}),
848 fixture('menu_preprocessor.html'));
849 });
850
851 test('tracks dependency correctly', function () {
852 var file = 'test/fixtures/menu_preprocessor.ejs';
853 var fn = ejs.compile(fixture('menu_preprocessor.ejs'), {filename: file});
854 assert(fn.dependencies.length);
855 });
856
857 test('include arbitrary files as-is', function () {
858 var file = 'test/fixtures/include_preprocessor.css.ejs';
859 assert.equal(ejs.render(fixture('include_preprocessor.css.ejs'), {pets: users}, {filename: file}),
860 fixture('include_preprocessor.css.html'));
861 });
862
863 test('pass compileDebug to include', function () {
864 var file = 'test/fixtures/include_preprocessor.ejs';
865 var fn;
866 fn = ejs.compile(fixture('include_preprocessor.ejs'), {
867 filename: file,
868 delimiter: '@',
869 compileDebug: false
870 });
871 try {
872 // Render without a required variable reference
873 fn({foo: 'asdf'});
874 }
875 catch(e) {
876 assert.equal(e.message, 'pets is not defined');
877 assert.ok(!e.path);
878 return;
879 }
880 throw new Error('no error reported when there should be');
881 });
882
883 test('is static', function () {
884 fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>Old</p>');
885 var file = 'test/fixtures/include_preprocessor_cache.ejs';
886 var options = {filename: file};
887 var out = ejs.compile(fixture('include_preprocessor_cache.ejs'), options);
888 assert.equal(out(), '<p>Old</p>\n');
889
890 fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>New</p>');
891 assert.equal(out(), '<p>Old</p>\n');
892 });
893
894 test('support caching', function () {
895 fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>Old</p>');
896 var file = 'test/fixtures/include_preprocessor_cache.ejs';
897 var options = {cache: true, filename: file};
898 var out = ejs.render(fixture('include_preprocessor_cache.ejs'), {}, options);
899 var expected = fixture('include_preprocessor_cache.html');
900 assert.equal(out, expected);
901 fs.writeFileSync(__dirname + '/tmp/include_preprocessor.ejs', '<p>New</p>');
902 out = ejs.render(fixture('include_preprocessor_cache.ejs'), {}, options);
903 assert.equal(out, expected);
904 });
905
906 test('whitespace slurp and rmWhitespace work', function() {
907 var file = 'test/fixtures/include_preprocessor_line_slurp.ejs';
908 var template = fixture('include_preprocessor_line_slurp.ejs');
909 var expected = fixture('include_preprocessor_line_slurp.html');
910 var options = {rmWhitespace: true, filename: file};
911 assert.equal(ejs.render(template, options),
912 expected);
913 });
914
915});
916
917suite('comments', function () {
918 test('fully render with comments removed', function () {
919 assert.equal(ejs.render(fixture('comments.ejs')),
920 fixture('comments.html'));
921 });
922});
923
924suite('require', function () {
925
926 // Only works with inline/preprocessor includes
927 test('allow ejs templates to be required as node modules', function () {
928 var file = 'test/fixtures/include_preprocessor.ejs';
929 var template = require(__dirname + '/fixtures/menu_preprocessor.ejs');
930 if (!process.env.running_under_istanbul) {
931 assert.equal(template({filename: file, pets: users}),
932 fixture('menu_preprocessor.html'));
933 }
934 });
935});
936
937suite('examples', function () {
938 function noop () {}
939 fs.readdirSync('examples').forEach(function (f) {
940 if (!/\.js$/.test(f)) {
941 return;
942 }
943 suite(f, function () {
944 test('doesn\'t throw any errors', function () {
945 var stderr = hook_stdio(process.stderr, noop);
946 var stdout = hook_stdio(process.stdout, noop);
947 try {
948 require('../examples/' + f);
949 }
950 catch (ex) {
951 stdout();
952 stderr();
953 throw ex;
954 }
955 stdout();
956 stderr();
957 });
958 });
959 });
960});