| @@ -1,3 +1,7 @@ | |||||
| = HEAD | |||||
| * Converted tests to use mocha instead of vows. | |||||
| = 0.7.1 / 6 Dec 2012 | = 0.7.1 / 6 Dec 2012 | ||||
| * Handle empty templates gracefully. Fixes #265, #267, and #270. | * Handle empty templates gracefully. Fixes #265, #267, and #270. | ||||
| @@ -6,12 +6,12 @@ | |||||
| "keywords": ["mustache", "template", "templates", "ejs"], | "keywords": ["mustache", "template", "templates", "ejs"], | ||||
| "main": "./mustache.js", | "main": "./mustache.js", | ||||
| "devDependencies": { | "devDependencies": { | ||||
| "vows": "0.6.x" | |||||
| "mocha": "1.5.0" | |||||
| }, | }, | ||||
| "volo": { | "volo": { | ||||
| "url": "https://raw.github.com/janl/mustache.js/0.7.1/mustache.js" | "url": "https://raw.github.com/janl/mustache.js/0.7.1/mustache.js" | ||||
| }, | }, | ||||
| "scripts": { | "scripts": { | ||||
| "test": "vows --spec" | |||||
| "test": "mocha test" | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,51 @@ | |||||
| require('./helper'); | |||||
| var Context = Mustache.Context; | |||||
| describe('A new Mustache.Context', function () { | |||||
| var context; | |||||
| beforeEach(function () { | |||||
| context = new Context({ name: 'parent', message: 'hi', a: { b: 'b' } }); | |||||
| }); | |||||
| it('is able to lookup properties of its own view', function () { | |||||
| assert.equal(context.lookup('name'), 'parent'); | |||||
| }); | |||||
| it('is able to lookup nested properties of its own view', function () { | |||||
| assert.equal(context.lookup('a.b'), 'b'); | |||||
| }); | |||||
| describe('when pushed', function () { | |||||
| beforeEach(function () { | |||||
| context = context.push({ name: 'child', c: { d: 'd' } }); | |||||
| }); | |||||
| it('returns the child context', function () { | |||||
| assert.equal(context.view.name, 'child'); | |||||
| assert.equal(context.parent.view.name, 'parent'); | |||||
| }); | |||||
| it('is able to lookup properties of its own view', function () { | |||||
| assert.equal(context.lookup('name'), 'child'); | |||||
| }); | |||||
| it("is able to lookup properties of the parent context's view", function () { | |||||
| assert.equal(context.lookup('message'), 'hi'); | |||||
| }); | |||||
| it('is able to lookup nested properties of its own view', function () { | |||||
| assert.equal(context.lookup('c.d'), 'd'); | |||||
| }); | |||||
| it('is able to lookup nested properties of its parent view', function () { | |||||
| assert.equal(context.lookup('a.b'), 'b'); | |||||
| }); | |||||
| }); | |||||
| }); | |||||
| describe('Mustache.Context.make', function () { | |||||
| it('returns the same object when given a Context', function () { | |||||
| var context = new Context; | |||||
| assert.strictEqual(Context.make(context), context); | |||||
| }); | |||||
| }); | |||||
| @@ -1,47 +0,0 @@ | |||||
| var assert = require("assert"); | |||||
| var vows = require("vows"); | |||||
| var Context = require("./../mustache").Context; | |||||
| vows.describe("Mustache.Context").addBatch({ | |||||
| "A Context": { | |||||
| topic: function () { | |||||
| var view = { name: 'parent', message: 'hi', a: { b: 'b' } }; | |||||
| var context = new Context(view); | |||||
| return context; | |||||
| }, | |||||
| "is able to lookup properties of its own view": function (context) { | |||||
| assert.equal(context.lookup("name"), "parent"); | |||||
| }, | |||||
| "is able to lookup nested properties of its own view": function (context) { | |||||
| assert.equal(context.lookup("a.b"), "b"); | |||||
| }, | |||||
| "when pushed": { | |||||
| topic: function (context) { | |||||
| var view = { name: 'child', c: { d: 'd' } }; | |||||
| return context.push(view); | |||||
| }, | |||||
| "returns the child context": function (context) { | |||||
| assert.equal(context.view.name, "child"); | |||||
| assert.equal(context.parent.view.name, "parent"); | |||||
| }, | |||||
| "is able to lookup properties of its own view": function (context) { | |||||
| assert.equal(context.lookup("name"), "child"); | |||||
| }, | |||||
| "is able to lookup properties of the parent context's view": function (context) { | |||||
| assert.equal(context.lookup("message"), "hi"); | |||||
| }, | |||||
| "is able to lookup nested properties of its own view": function (context) { | |||||
| assert.equal(context.lookup("c.d"), "d"); | |||||
| }, | |||||
| "is able to lookup nested properties of its parent view": function (context) { | |||||
| assert.equal(context.lookup("a.b"), "b"); | |||||
| } | |||||
| } // when pushed | |||||
| }, // A Context | |||||
| "make": { | |||||
| "returns the same object when given a Context": function () { | |||||
| var context = new Context; | |||||
| assert.strictEqual(Context.make(context), context); | |||||
| } | |||||
| } | |||||
| }).export(module); | |||||
| @@ -0,0 +1,2 @@ | |||||
| assert = require('assert'); | |||||
| Mustache = require('../mustache'); | |||||
| @@ -1,9 +1,7 @@ | |||||
| var assert = require('assert'); | |||||
| var vows = require('vows'); | |||||
| var Mustache = require('./../mustache'); | |||||
| require('./helper'); | |||||
| // A map of templates to their expected token output. Tokens are in the format: | // A map of templates to their expected token output. Tokens are in the format: | ||||
| // [type, value, startIndex, endIndex]. | |||||
| // [type, value, startIndex, endIndex, subTokens]. | |||||
| var expectations = { | var expectations = { | ||||
| '' : [], | '' : [], | ||||
| '{{hi}}' : [ [ 'name', 'hi', 0, 6 ] ], | '{{hi}}' : [ [ 'name', 'hi', 0, 6 ] ], | ||||
| @@ -55,16 +53,14 @@ var expectations = { | |||||
| : [ [ '#', 'foo', 0, 8, [ [ '#', 'a', 11, 17, [ [ 'text', ' ', 18, 22 ], [ 'name', 'b', 22, 27 ], [ 'text', '\n', 27, 28 ] ] ] ] ] ] | : [ [ '#', 'foo', 0, 8, [ [ '#', 'a', 11, 17, [ [ 'text', ' ', 18, 22 ], [ 'name', 'b', 22, 27 ], [ 'text', '\n', 27, 28 ] ] ] ] ] ] | ||||
| }; | }; | ||||
| var spec = {}; | |||||
| describe('Mustache.parse', function () { | |||||
| for (var template in expectations) { | |||||
| (function (template, tokens) { | |||||
| spec['knows how to parse ' + JSON.stringify(template)] = function () { | |||||
| assert.deepEqual(Mustache.parse(template), tokens); | |||||
| }; | |||||
| })(template, expectations[template]); | |||||
| } | |||||
| for (var template in expectations) { | |||||
| (function (template, tokens) { | |||||
| it('knows how to parse ' + JSON.stringify(template), function () { | |||||
| assert.deepEqual(Mustache.parse(template), tokens); | |||||
| }); | |||||
| })(template, expectations[template]); | |||||
| } | |||||
| vows.describe('Mustache.parse').addBatch({ | |||||
| 'parse': spec | |||||
| }).export(module); | |||||
| }); | |||||
| @@ -0,0 +1,68 @@ | |||||
| require('./helper'); | |||||
| var fs = require('fs'); | |||||
| var path = require('path'); | |||||
| var _files = path.join(__dirname, '_files'); | |||||
| function getContents(testName, ext) { | |||||
| return fs.readFileSync(path.join(_files, testName + '.' + ext), 'utf8'); | |||||
| } | |||||
| function getView(testName) { | |||||
| var view = getContents(testName, 'js'); | |||||
| if (!view) throw new Error('Cannot find view for test "' + testName + '"'); | |||||
| return eval(view); | |||||
| } | |||||
| function getPartial(testName) { | |||||
| try { | |||||
| return getContents(testName, 'partial'); | |||||
| } catch (e) { | |||||
| // No big deal. Not all tests need to test partial support. | |||||
| } | |||||
| } | |||||
| function getTest(testName) { | |||||
| var test = {}; | |||||
| test.view = getView(testName); | |||||
| test.template = getContents(testName, 'mustache'); | |||||
| test.partial = getPartial(testName); | |||||
| test.expect = getContents(testName, 'txt'); | |||||
| return test; | |||||
| } | |||||
| // You can put the name of a specific test to run in the TEST environment | |||||
| // variable (e.g. TEST=backslashes vows test/render-test.js) | |||||
| var testToRun = process.env.TEST; | |||||
| var testNames; | |||||
| if (testToRun) { | |||||
| testNames = [testToRun]; | |||||
| } else { | |||||
| testNames = fs.readdirSync(_files).filter(function (file) { | |||||
| return (/\.js$/).test(file); | |||||
| }).map(function (file) { | |||||
| return path.basename(file).replace(/\.js$/, ''); | |||||
| }); | |||||
| } | |||||
| describe('Mustache.render', function () { | |||||
| beforeEach(function () { | |||||
| Mustache.clearCache(); | |||||
| }); | |||||
| testNames.forEach(function (testName) { | |||||
| var test = getTest(testName); | |||||
| it('knows how to render ' + testName, function () { | |||||
| var output; | |||||
| if (test.partial) { | |||||
| output = Mustache.render(test.template, test.view, { partial: test.partial }); | |||||
| } else { | |||||
| output = Mustache.render(test.template, test.view); | |||||
| } | |||||
| assert.equal(output, test.expect); | |||||
| }); | |||||
| }); | |||||
| }); | |||||
| @@ -1,66 +0,0 @@ | |||||
| var fs = require("fs"); | |||||
| var path = require("path"); | |||||
| var assert = require("assert"); | |||||
| var vows = require("vows"); | |||||
| var Mustache = require("./../mustache"); | |||||
| var _files = path.join(__dirname, "_files"); | |||||
| function getContents(testName, ext) { | |||||
| return fs.readFileSync(path.join(_files, testName + "." + ext), "utf8"); | |||||
| } | |||||
| // You can put the name of a specific test to run in the TEST environment | |||||
| // variable (e.g. TEST=backslashes vows test/render_test.js) | |||||
| var testToRun = process.env["TEST"]; | |||||
| var testNames; | |||||
| if (testToRun) { | |||||
| testNames = [testToRun]; | |||||
| } else { | |||||
| testNames = fs.readdirSync(_files).filter(function (file) { | |||||
| return (/\.js$/).test(file); | |||||
| }).map(function (file) { | |||||
| return path.basename(file).replace(/\.js$/, ""); | |||||
| }); | |||||
| } | |||||
| var spec = {}; | |||||
| testNames.forEach(function (testName) { | |||||
| var view = getContents(testName, "js"); | |||||
| if (view) { | |||||
| view = eval(view); | |||||
| } else { | |||||
| console.log("Cannot find view for test: " + testName); | |||||
| process.exit(); | |||||
| } | |||||
| var template = getContents(testName, "mustache"); | |||||
| var expect = getContents(testName, "txt"); | |||||
| var partial; | |||||
| try { | |||||
| partial = getContents(testName, "partial"); | |||||
| } catch (e) { | |||||
| // No big deal. | |||||
| } | |||||
| spec["knows how to render " + testName] = function () { | |||||
| Mustache.clearCache(); | |||||
| var output; | |||||
| if (partial) { | |||||
| output = Mustache.render(template, view, {partial: partial}); | |||||
| } else { | |||||
| output = Mustache.render(template, view); | |||||
| } | |||||
| assert.equal(output, expect); | |||||
| }; | |||||
| }); | |||||
| vows.describe("Mustache.render").addBatch({ | |||||
| "render": spec | |||||
| }).export(module); | |||||
| @@ -0,0 +1,78 @@ | |||||
| require('./helper'); | |||||
| var Scanner = Mustache.Scanner; | |||||
| describe('A new Mustache.Scanner', function () { | |||||
| describe('for an empty string', function () { | |||||
| it('is at the end', function () { | |||||
| var scanner = new Scanner(''); | |||||
| assert(scanner.eos()); | |||||
| }); | |||||
| }); | |||||
| describe('for a non-empty string', function () { | |||||
| var scanner; | |||||
| beforeEach(function () { | |||||
| scanner = new Scanner('a b c'); | |||||
| }); | |||||
| describe('scan', function () { | |||||
| describe('when the RegExp matches the entire string', function () { | |||||
| it('returns the entire string', function () { | |||||
| var match = scanner.scan(/a b c/); | |||||
| assert.equal(match, scanner.string); | |||||
| assert(scanner.eos()); | |||||
| }); | |||||
| }); | |||||
| describe('when the RegExp matches at index 0', function () { | |||||
| it('returns the portion of the string that matched', function () { | |||||
| var match = scanner.scan(/a/); | |||||
| assert.equal(match, 'a'); | |||||
| assert.equal(scanner.pos, 1); | |||||
| }); | |||||
| }); | |||||
| describe('when the RegExp matches at some index other than 0', function () { | |||||
| it('returns the empty string', function () { | |||||
| var match = scanner.scan(/b/); | |||||
| assert.equal(match, ''); | |||||
| assert.equal(scanner.pos, 0); | |||||
| }); | |||||
| }); | |||||
| describe('when the RegExp does not match', function () { | |||||
| it('returns the empty string', function () { | |||||
| var match = scanner.scan(/z/); | |||||
| assert.equal(match, ''); | |||||
| assert.equal(scanner.pos, 0); | |||||
| }); | |||||
| }); | |||||
| }); // scan | |||||
| describe('scanUntil', function () { | |||||
| describe('when the RegExp matches at index 0', function () { | |||||
| it('returns the empty string', function () { | |||||
| var match = scanner.scanUntil(/a/); | |||||
| assert.equal(match, ''); | |||||
| assert.equal(scanner.pos, 0); | |||||
| }); | |||||
| }); | |||||
| describe('when the RegExp matches at some index other than 0', function () { | |||||
| it('returns the string up to that index', function () { | |||||
| var match = scanner.scanUntil(/b/); | |||||
| assert.equal(match, 'a '); | |||||
| assert.equal(scanner.pos, 2); | |||||
| }); | |||||
| }); | |||||
| describe('when the RegExp does not match', function () { | |||||
| it('returns the entire string', function () { | |||||
| var match = scanner.scanUntil(/z/); | |||||
| assert.equal(match, scanner.string); | |||||
| assert(scanner.eos()); | |||||
| }); | |||||
| }); | |||||
| }); // scanUntil | |||||
| }); // for a non-empty string | |||||
| }); | |||||
| @@ -1,117 +0,0 @@ | |||||
| var assert = require("assert"); | |||||
| var vows = require("vows"); | |||||
| var Scanner = require("./../mustache").Scanner; | |||||
| vows.describe("Mustache.Scanner").addBatch({ | |||||
| "A Scanner": { | |||||
| "for an empty string": { | |||||
| topic: new Scanner(""), | |||||
| "is at the end of the string": function () { | |||||
| var scanner = new Scanner(""); | |||||
| assert(scanner.eos()); | |||||
| } | |||||
| }, | |||||
| "for a non-empty string": { | |||||
| topic: "a b c", | |||||
| "when calling scan": { | |||||
| "when the regexp matches the entire string": { | |||||
| topic: function (string) { | |||||
| var scanner = new Scanner(string); | |||||
| var match = scanner.scan(/a b c/); | |||||
| this.callback(scanner, match); | |||||
| }, | |||||
| "returns the entire string": function (scanner, match) { | |||||
| assert.equal(match, scanner.string); | |||||
| }, | |||||
| "is at the end of the string": function (scanner, match) { | |||||
| assert(scanner.eos()); | |||||
| } | |||||
| }, // when the regexp matches the entire string | |||||
| "when the regexp matches": { | |||||
| "at the 0th index": { | |||||
| topic: function (string) { | |||||
| var scanner = new Scanner(string); | |||||
| var match = scanner.scan(/a/); | |||||
| this.callback(scanner, match); | |||||
| }, | |||||
| "returns the portion of the string that was matched": function (scanner, match) { | |||||
| assert.equal(match, "a"); | |||||
| }, | |||||
| "advances the internal pointer the length of the match": function (scanner, match) { | |||||
| assert.equal(scanner.pos, 1); | |||||
| } | |||||
| }, // at the 0th index | |||||
| "at some index other than 0": { | |||||
| topic: function (string) { | |||||
| var scanner = new Scanner(string); | |||||
| var match = scanner.scan(/b/); | |||||
| this.callback(scanner, match); | |||||
| }, | |||||
| "returns the empty string": function (scanner, match) { | |||||
| assert.equal(match, ""); | |||||
| }, | |||||
| "does not advance the internal pointer": function (scanner, match) { | |||||
| assert.equal(scanner.pos, 0); | |||||
| } | |||||
| } // at some index other than 0 | |||||
| }, // when the regexp matches | |||||
| "when the regexp doesn't match": { | |||||
| topic: function (string) { | |||||
| var scanner = new Scanner(string); | |||||
| var match = scanner.scan(/z/); | |||||
| this.callback(scanner, match); | |||||
| }, | |||||
| "returns the empty string": function (scanner, match) { | |||||
| assert.equal(match, ""); | |||||
| }, | |||||
| "does not advance the internal pointer": function (scanner, match) { | |||||
| assert.equal(scanner.pos, 0); | |||||
| } | |||||
| } | |||||
| }, // when calling scan | |||||
| "when calling scanUntil": { | |||||
| "when the regexp matches": { | |||||
| "at the 0th index": { | |||||
| topic: function (string) { | |||||
| var scanner = new Scanner(string); | |||||
| var match = scanner.scanUntil(/a/); | |||||
| this.callback(scanner, match); | |||||
| }, | |||||
| "returns the empty string": function (scanner, match) { | |||||
| assert.equal(match, "") | |||||
| }, | |||||
| "does not advance the internal pointer": function (scanner, match) { | |||||
| assert.equal(scanner.pos, 0); | |||||
| } | |||||
| }, | |||||
| "at index 2": { | |||||
| topic: function (string) { | |||||
| var scanner = new Scanner(string); | |||||
| var match = scanner.scanUntil(/b/); | |||||
| this.callback(scanner, match); | |||||
| }, | |||||
| "returns the portion of the string it scanned": function (scanner, match) { | |||||
| assert.equal(match, "a "); | |||||
| }, | |||||
| "advances the internal pointer the length of the match": function (scanner, match) { | |||||
| assert.equal(scanner.pos, 2); | |||||
| } | |||||
| } | |||||
| }, // when the regexp matches | |||||
| "when the regexp doesn't match": { | |||||
| topic: function (string) { | |||||
| var scanner = new Scanner(string); | |||||
| var match = scanner.scanUntil(/z/); | |||||
| this.callback(scanner, match); | |||||
| }, | |||||
| "returns the entire string": function (scanner, match) { | |||||
| assert.equal(match, scanner.string); | |||||
| }, | |||||
| "is at the end of the string": function (scanner, match) { | |||||
| assert(scanner.eos()); | |||||
| } | |||||
| } // when the regexp doesn't match | |||||
| } // when calling scanUntil | |||||
| } // for a non-empty string | |||||
| } | |||||
| }).export(module); | |||||
| @@ -0,0 +1,43 @@ | |||||
| require('./helper'); | |||||
| var Writer = Mustache.Writer; | |||||
| describe('A new Mustache.Writer', function () { | |||||
| var writer; | |||||
| beforeEach(function () { | |||||
| writer = new Writer; | |||||
| }); | |||||
| it('loads partials correctly', function () { | |||||
| var partial = 'The content of the partial.'; | |||||
| var result = writer.render('{{>partial}}', {}, function (name) { | |||||
| assert.equal(name, 'partial'); | |||||
| return partial; | |||||
| }); | |||||
| assert.equal(result, partial); | |||||
| }); | |||||
| it('caches partials by content, not name', function () { | |||||
| var result = writer.render('{{>partial}}', {}, { | |||||
| partial: 'partial one' | |||||
| }); | |||||
| assert.equal(result, 'partial one'); | |||||
| result = writer.render('{{>partial}}', {}, { | |||||
| partial: 'partial two' | |||||
| }); | |||||
| assert.equal(result, 'partial two'); | |||||
| }); | |||||
| it('can compile an array of tokens', function () { | |||||
| var template = 'Hello {{name}}!'; | |||||
| var tokens = Mustache.parse(template); | |||||
| var render = writer.compileTokens(tokens, template); | |||||
| var result = render({ name: 'Michael' }); | |||||
| assert.equal(result, 'Hello Michael!'); | |||||
| }); | |||||
| }); | |||||
| @@ -1,44 +0,0 @@ | |||||
| var assert = require("assert"); | |||||
| var vows = require("vows"); | |||||
| var Mustache = require("./../mustache"); | |||||
| var Writer = Mustache.Writer; | |||||
| vows.describe("Mustache.Writer").addBatch({ | |||||
| "A Writer": { | |||||
| topic: function () { | |||||
| var writer = new Writer(); | |||||
| return writer; | |||||
| }, | |||||
| "loads partials correctly": function (writer) { | |||||
| var partial = "The content of the partial."; | |||||
| var result = writer.render("{{>partial}}", {}, function (name) { | |||||
| assert.equal(name, "partial"); | |||||
| return partial; | |||||
| }); | |||||
| assert.equal(result, partial); | |||||
| }, | |||||
| "caches partials by content, not by name": function (writer) { | |||||
| var result = writer.render("{{>partial}}", {}, { | |||||
| partial: "partial one" | |||||
| }); | |||||
| assert.equal(result, "partial one"); | |||||
| result = writer.render("{{>partial}}", {}, { | |||||
| partial: "partial two" | |||||
| }); | |||||
| assert.equal(result, "partial two"); | |||||
| }, | |||||
| "can compile an array of tokens": function (writer) { | |||||
| var template = "Hello {{name}}!"; | |||||
| var tokens = Mustache.parse(template); | |||||
| var render = writer.compileTokens(tokens, template); | |||||
| var result = render({ name: 'Michael' }); | |||||
| assert.equal(result, 'Hello Michael!'); | |||||
| } | |||||
| } | |||||
| }).export(module); | |||||