/* 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('

', {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); }); }); 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, '

fonebone

\n'); done(); }); }); test('accept locals without using with() {}', function(done) { var data = {name: 'fonebone'} , options = {delimiter: '$', _with: false} , doneCount = 0; ejs.renderFile('test/fixtures/user-no-with.ejs', data, options, function(err, html) { if (err) { if (doneCount === 2) { return; } doneCount = 2; return done(err); } assert.equal(html, '

fonebone

\n'); doneCount++; if (doneCount === 2) { done(); } }); ejs.renderFile('test/fixtures/user.ejs', data, options, function(err) { if (!err) { if (doneCount === 2) { return; } doneCount = 2; return done(new Error('error not thrown')); } doneCount++; if (doneCount === 2) { done(); } }); }); test('not catch err thrown by callback', function(done) { var data = {name: 'fonebone'} , options = {delimiter: '$'} , counter = 0; var d = require('domain').create(); d.on('error', function (err) { assert.equal(counter, 1); assert.equal(err.message, 'Exception in callback'); done(); }); d.run(function () { // process.nextTick() needed to work around mochajs/mocha#513 // // tl;dr: mocha doesn't support synchronous exception throwing in // domains. Have to make it async. Ticket closed because: "domains are // deprecated :D" process.nextTick(function () { ejs.renderFile('test/fixtures/user.ejs', data, options, function(err) { counter++; if (err) { assert.notEqual(err.message, 'Exception in callback'); return done(err); } throw new Error('Exception in callback'); }); }); }); }); test('support caching', function (done) { var expected = '

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 &