/* jshint mocha: true */ /** * Module dependencies. */ var ejs = require('..') , fs = require('fs') , read = fs.readFileSync , assert = require('assert') , path = require('path') , LRU = require('lru-cache'); try { fs.mkdirSync(__dirname + '/tmp'); } catch (ex) { if (ex.code !== 'EEXIST') { throw ex; } } // From https://gist.github.com/pguillory/729616 function hook_stdio(stream, callback) { var old_write = stream.write; stream.write = (function() { return function(string, encoding, fd) { callback(string, encoding, fd); }; })(stream.write); return function() { stream.write = old_write; }; } /** * Load fixture `name`. */ function fixture(name) { return read('test/fixtures/' + name, 'utf8'); } /** * User fixtures. */ var users = []; users.push({name: 'geddy'}); users.push({name: 'neil'}); users.push({name: 'alex'}); suite('ejs.compile(str, options)', function () { test('compile to a function', function () { var fn = ejs.compile('
yay
'); assert.equal(fn(), 'yay
'); }); test('empty input works', function () { var fn = ejs.compile(''); assert.equal(fn(), ''); }); test('throw if there are syntax errors', function () { try { ejs.compile(fixture('fail.ejs')); } catch (err) { assert.ok(err.message.indexOf('compiling ejs') > -1); try { ejs.compile(fixture('fail.ejs'), {filename: 'fail.ejs'}); } catch (err) { assert.ok(err.message.indexOf('fail.ejs') > -1); return; } } throw new Error('no error reported when there should be'); }); test('allow customizing delimiter local var', function () { var fn; fn = ejs.compile('= name ?>
', {delimiter: '?'}); assert.equal(fn({name: 'geddy'}), 'geddy
'); fn = ejs.compile('<:= name :>
', {delimiter: ':'}); assert.equal(fn({name: 'geddy'}), 'geddy
'); fn = ejs.compile('<$= name $>
', {delimiter: '$'}); assert.equal(fn({name: 'geddy'}), 'geddy
'); }); test('default to using ejs.delimiter', function () { var fn; ejs.delimiter = '&'; fn = ejs.compile('<&= name &>
'); assert.equal(fn({name: 'geddy'}), 'geddy
'); fn = ejs.compile('<|= name |>
', {delimiter: '|'}); assert.equal(fn({name: 'geddy'}), 'geddy
'); delete ejs.delimiter; }); test('have a working client option', function () { var fn , str , preFn; fn = ejs.compile('<%= foo %>
', {client: true}); str = fn.toString(); if (!process.env.running_under_istanbul) { eval('var preFn = ' + str); assert.equal(preFn({foo: 'bar'}), 'bar
'); } }); test('support client mode without locals', function () { var fn , str , preFn; fn = ejs.compile('<%= "foo" %>
', {client: true}); str = fn.toString(); if (!process.env.running_under_istanbul) { eval('var preFn = ' + str); assert.equal(preFn(), 'foo
'); } }); test('not include rethrow() in client mode if compileDebug is false', function () { var fn = ejs.compile('<%= "foo" %>
', { client: true , compileDebug: false }); // There could be a `rethrow` in the function declaration assert((fn.toString().match(/rethrow/g) || []).length <= 1); }); test('support custom escape function', function () { var customEscape , fn; customEscape = function customEscape(str) { return !str ? '' : str.toUpperCase(); }; fn = ejs.compile('HELLO <%= name %>', {escape: customEscape}); assert.equal(fn({name: 'world'}), 'HELLO WORLD'); }); test('support custom escape function in client mode', function () { var customEscape , fn , str; customEscape = function customEscape(str) { return !str ? '' : str.toUpperCase(); }; fn = ejs.compile('HELLO <%= name %>', {escape: customEscape, client: true}); str = fn.toString(); if (!process.env.running_under_istanbul) { eval('var preFn = ' + str); assert.equal(preFn({name: 'world'}), 'HELLO WORLD'); } }); test('strict mode works', function () { assert.equal(ejs.render(fixture('strict.ejs'), {}, {strict: true}), 'true'); }); }); suite('ejs.render(str, data, opts)', function () { test('render the template', function () { assert.equal(ejs.render('yay
'), 'yay
'); }); test('empty input works', function () { assert.equal(ejs.render(''), ''); }); test('undefined renders nothing escaped', function () { assert.equal(ejs.render('<%= undefined %>'), ''); }); test('undefined renders nothing raw', function () { assert.equal(ejs.render('<%- undefined %>'), ''); }); test('null renders nothing escaped', function () { assert.equal(ejs.render('<%= null %>'), ''); }); test('null renders nothing raw', function () { assert.equal(ejs.render('<%- null %>'), ''); }); test('zero-value data item renders something escaped', function () { assert.equal(ejs.render('<%= 0 %>'), '0'); }); test('zero-value data object renders something raw', function () { assert.equal(ejs.render('<%- 0 %>'), '0'); }); test('accept locals', function () { assert.equal(ejs.render('<%= name %>
', {name: 'geddy'}), 'geddy
'); }); test('accept locals without using with() {}', function () { assert.equal(ejs.render('<%= locals.name %>
', {name: 'geddy'}, {_with: false}), 'geddy
'); assert.throws(function() { ejs.render('<%= name %>
', {name: 'geddy'}, {_with: false}); }, /name is not defined/); }); test('accept custom name for locals', function () { ejs.localsName = 'it'; assert.equal(ejs.render('<%= it.name %>
', {name: 'geddy'}, {_with: false}), 'geddy
'); assert.throws(function() { ejs.render('<%= name %>
', {name: 'geddy'}, {_with: false}); }, /name is not defined/); ejs.localsName = 'locals'; }); test('support caching', function () { var file = __dirname + '/tmp/render.ejs' , options = {cache: true, filename: file} , out = ejs.render('Old
', {}, options) , expected = 'Old
'; assert.equal(out, expected); // Assert no change, still in cache out = ejs.render('New
', {}, options); assert.equal(out, expected); }); test('support LRU caching', function () { var oldCache = ejs.cache , file = __dirname + '/tmp/render.ejs' , options = {cache: true, filename: file} , out , expected = 'Old
'; // Switch to LRU ejs.cache = LRU(); out = ejs.render('Old
', {}, options); assert.equal(out, expected); // Assert no change, still in cache out = ejs.render('New
', {}, options); assert.equal(out, expected); // Restore system cache ejs.cache = oldCache; }); test('opts.context', function () { var ctxt = {foo: 'FOO'} , out = ejs.render('<%= this.foo %>', {}, {context: ctxt}); assert.equal(out, ctxt.foo); }); }); suite('ejs.renderFile(path, [data], [options], fn)', function () { test('render a file', function(done) { ejs.renderFile('test/fixtures/para.ejs', function(err, html) { if (err) { return done(err); } assert.equal(html, 'hey
\n'); done(); }); }); test('accept locals', function(done) { var data = {name: 'fonebone'} , options = {delimiter: '$'}; ejs.renderFile('test/fixtures/user.ejs', data, options, function(err, html) { if (err) { return done(err); } assert.equal(html, 'Old
' , file = __dirname + '/tmp/renderFile.ejs' , options = {cache: true}; fs.writeFileSync(file, 'Old
'); ejs.renderFile(file, {}, options, function (err, out) { if (err) { done(err); } fs.writeFileSync(file, 'New
'); assert.equal(out, expected); ejs.renderFile(file, {}, options, function (err, out) { if (err) { done(err); } // Assert no change, still in cache assert.equal(out, expected); done(); }); }); }); test('opts.context', function (done) { var ctxt = {foo: 'FOO'}; ejs.renderFile('test/fixtures/with-context.ejs', {}, {context: ctxt}, function(err, html) { if (err) { return done(err); } assert.equal(html, ctxt.foo + '\n'); done(); }); }); }); suite('cache specific', function () { test('`clearCache` work properly', function () { var expected = 'Old
' , file = __dirname + '/tmp/clearCache.ejs' , options = {cache: true, filename: file} , out = ejs.render('Old
', {}, options); assert.equal(out, expected); ejs.clearCache(); expected = 'New
'; out = ejs.render('New
', {}, options); assert.equal(out, expected); }); test('`clearCache` work properly, LRU', function () { var expected = 'Old
' , oldCache = ejs.cache , file = __dirname + '/tmp/clearCache.ejs' , options = {cache: true, filename: file} , out; ejs.cache = LRU(); out = ejs.render('Old
', {}, options); assert.equal(out, expected); ejs.clearCache(); expected = 'New
'; out = ejs.render('New
', {}, options); assert.equal(out, expected); ejs.cache = oldCache; }); test('LRU with cache-size 1', function () { var oldCache = ejs.cache , options , out , expected , file; ejs.cache = LRU(1); file = __dirname + '/tmp/render1.ejs'; options = {cache: true, filename: file}; out = ejs.render('File1
', {}, options); expected = 'File1
'; assert.equal(out, expected); // Same filename, different template, but output // should be the same because cache file = __dirname + '/tmp/render1.ejs'; options = {cache: true, filename: file}; out = ejs.render('ChangedFile1
', {}, options); expected = 'File1
'; assert.equal(out, expected); // Different filiename -- output should be different, // and previous cache-entry should be evicted file = __dirname + '/tmp/render2.ejs'; options = {cache: true, filename: file}; out = ejs.render('File2
', {}, options); expected = 'File2
'; assert.equal(out, expected); // Entry with first filename should now be out of cache, // results should be different file = __dirname + '/tmp/render1.ejs'; options = {cache: true, filename: file}; out = ejs.render('ChangedFile1
', {}, options); expected = 'ChangedFile1
'; assert.equal(out, expected); ejs.cache = oldCache; }); }); suite('<%', function () { test('without semicolons', function () { assert.equal(ejs.render(fixture('no.semicolons.ejs')), fixture('no.semicolons.html')); }); }); suite('<%=', function () { test('escape &