Merge pull request #681 from wycats/sauce-tests
Adds in-browser testing and Saucelabs CI
This commit is contained in:
@@ -8,3 +8,4 @@ node_modules
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
npm-debug.log
|
||||
sauce_connect.log*
|
||||
|
||||
@@ -18,5 +18,6 @@ dist/components/*
|
||||
spec/*
|
||||
src/*
|
||||
tasks/*
|
||||
tmp/*
|
||||
publish/*
|
||||
vendor/*
|
||||
|
||||
+13
-10
@@ -1,13 +1,12 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
|
||||
before_install:
|
||||
- npm install -g grunt-cli
|
||||
|
||||
script:
|
||||
- grunt --stack default metrics publish:latest
|
||||
- grunt --stack travis
|
||||
|
||||
email:
|
||||
on_failure: change
|
||||
@@ -15,13 +14,17 @@ email:
|
||||
env:
|
||||
global:
|
||||
- S3_BUCKET_NAME=builds.handlebarsjs.com
|
||||
- secure: ! 'PJaukuvkBBsSDOqbIcNSSMgb96VVEaIt/eq9GPjXPeFbSd3hXgwhwVE62Lrq
|
||||
- secure: PJaukuvkBBsSDOqbIcNSSMgb96VVEaIt/eq9GPjXPeFbSd3hXgwhwVE62LrqtJO8BaUfX+PzpiQjEl4D5/KBmvlFZ057Hqmy0zmPOT5mDZfJe8Ja5zyvTMb+KkCWN/tjAp8kawHojE04pn6jIpPdwXFnAYwPhaHbATFrmdt9fdg=
|
||||
- secure: mBcGL2tnmiRujJdV/4fxrVd8E8wn6AW9IQKVcMv8tvOc7i5dOzZ39rpBKLuTMRXDtMV1LyLiuKYb1pHj1IyeadEahcLYFfGygF4LG7Yzp4NWHtRzQ7Q8LXaJV7dXDboYCFkn2a8/Rtx1YSVh/sCONf5UoRC+MUIqrj4UiHN9r3s=
|
||||
|
||||
tJO8BaUfX+PzpiQjEl4D5/KBmvlFZ057Hqmy0zmPOT5mDZfJe8Ja5zyvTMb+
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "0.10"
|
||||
env:
|
||||
- PUBLISH=true
|
||||
- secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw=
|
||||
- secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY=
|
||||
|
||||
KkCWN/tjAp8kawHojE04pn6jIpPdwXFnAYwPhaHbATFrmdt9fdg='
|
||||
- secure: ! 'mBcGL2tnmiRujJdV/4fxrVd8E8wn6AW9IQKVcMv8tvOc7i5dOzZ39rpBKLuT
|
||||
|
||||
MRXDtMV1LyLiuKYb1pHj1IyeadEahcLYFfGygF4LG7Yzp4NWHtRzQ7Q8LXaJ
|
||||
|
||||
V7dXDboYCFkn2a8/Rtx1YSVh/sCONf5UoRC+MUIqrj4UiHN9r3s='
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
+48
-1
@@ -14,7 +14,7 @@ module.exports = function(grunt) {
|
||||
]
|
||||
},
|
||||
|
||||
clean: ["dist", "lib/handlebars/compiler/parser.js"],
|
||||
clean: ['tmp', 'dist', 'lib/handlebars/compiler/parser.js'],
|
||||
|
||||
copy: {
|
||||
dist: {
|
||||
@@ -110,6 +110,46 @@ module.exports = function(grunt) {
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
concat: {
|
||||
tests: {
|
||||
src: ['spec/!(require).js'],
|
||||
dest: 'tmp/tests.js'
|
||||
}
|
||||
},
|
||||
|
||||
connect: {
|
||||
server: {
|
||||
options: {
|
||||
base: '.',
|
||||
hostname: '*',
|
||||
port: 9999
|
||||
}
|
||||
}
|
||||
},
|
||||
'saucelabs-mocha': {
|
||||
all: {
|
||||
options: {
|
||||
build: process.env.TRAVIS_JOB_ID,
|
||||
urls: ['http://localhost:9999/spec/?headless=true'],
|
||||
detailedError: true,
|
||||
concurrency: 2,
|
||||
browsers: [
|
||||
{browserName: 'chrome'},
|
||||
{browserName: 'firefox'},
|
||||
{browserName: 'firefox', version: '3.6'},
|
||||
{browserName: 'safari', version: 7, platform: 'OS X 10.9'},
|
||||
{browserName: 'safari', version: 6, platform: 'OS X 10.8'},
|
||||
{browserName: 'opera'},
|
||||
{browserName: 'internet explorer', version: 11, platform: 'Windows 8.1'},
|
||||
{browserName: 'internet explorer', version: 10, platform: 'Windows 8'},
|
||||
{browserName: 'internet explorer', version: 9, platform: 'Windows 7'},
|
||||
{browserName: 'internet explorer', version: 8, platform: 'XP'},
|
||||
{browserName: 'internet explorer', version: 7, platform: 'XP'}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -124,20 +164,27 @@ module.exports = function(grunt) {
|
||||
this.registerTask('amd', ['packager:amd', 'requirejs']);
|
||||
this.registerTask('node', ['packager:cjs']);
|
||||
this.registerTask('globals', ['packager:global']);
|
||||
this.registerTask('tests', ['concat:tests']);
|
||||
|
||||
this.registerTask('release', 'Build final packages', ['amd', 'jshint', 'uglify', 'copy:dist', 'copy:components', 'copy:cdnjs']);
|
||||
|
||||
// Load tasks from npm
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-requirejs');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-saucelabs');
|
||||
grunt.loadNpmTasks('es6-module-packager');
|
||||
|
||||
grunt.task.loadTasks('tasks');
|
||||
|
||||
grunt.registerTask('bench', ['metrics']);
|
||||
grunt.registerTask('sauce', process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : []);
|
||||
|
||||
grunt.registerTask('travis', process.env.PUBLISH ? ['default', 'sauce', 'metrics', 'publish:latest'] : ['default']);
|
||||
|
||||
grunt.registerTask('default', ['build', 'test', 'release']);
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
[](https://travis-ci.org/wycats/handlebars.js)
|
||||
|
||||
[](https://travis-ci.org/wycats/handlebars.js)
|
||||
[](https://saucelabs.com/u/handlebars)
|
||||
|
||||
Handlebars.js
|
||||
=============
|
||||
|
||||
+3
-1
@@ -35,17 +35,19 @@
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "~0.1.10",
|
||||
"grunt-contrib-clean": "~0.4.1",
|
||||
"grunt-contrib-concat": "~0.3.0",
|
||||
"grunt-contrib-connect": "~0.5.0",
|
||||
"grunt-contrib-copy": "~0.4.1",
|
||||
"grunt-contrib-jshint": "~0.7.2",
|
||||
"grunt-contrib-requirejs": "~0.4.1",
|
||||
"grunt-contrib-uglify": "~0.2.2",
|
||||
"grunt-saucelabs": "~4.1.2",
|
||||
"es6-module-packager": "0.x",
|
||||
"jison": "~0.3.0",
|
||||
"keen.io": "0.0.3",
|
||||
"mocha": "*",
|
||||
"mustache": "~0.7.2",
|
||||
"semver": "~2.1.0",
|
||||
"should": "~1.2.2",
|
||||
"underscore": "~1.5.1"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
|
||||
+5
-4
@@ -1,3 +1,4 @@
|
||||
/*global CompilerContext, Handlebars, beforeEach, shouldCompileTo */
|
||||
global.handlebarsEnv = null;
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -160,9 +161,9 @@ describe("basic context", function() {
|
||||
|
||||
it("this keyword nested inside path", function() {
|
||||
var string = "{{#hellos}}{{text/this/foo}}{{/hellos}}";
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(string);
|
||||
}).should.throw(Error);
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it("this keyword in helpers", function() {
|
||||
@@ -181,8 +182,8 @@ describe("basic context", function() {
|
||||
|
||||
it("this keyword nested inside helpers param", function() {
|
||||
var string = "{{#hellos}}{{foo text/this/foo}}{{/hellos}}";
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(string);
|
||||
}).should.throw(Error);
|
||||
}, Error);
|
||||
});
|
||||
});
|
||||
|
||||
+2
-2
@@ -42,9 +42,9 @@ describe('blocks', function() {
|
||||
it("block with complex lookup using nested context", function() {
|
||||
var string = "{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}";
|
||||
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(string);
|
||||
}).should.throw(Error);
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it("block with deep nested complex lookup", function() {
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ describe('builtin helpers', function() {
|
||||
var expected1 = "<b>#1</b>. goodbye! 2. GOODBYE! cruel world!";
|
||||
var expected2 = "2. GOODBYE! <b>#1</b>. goodbye! cruel world!";
|
||||
|
||||
(actual === expected1 || actual === expected2).should.equal(true, "each with object argument iterates over the contents when not empty");
|
||||
equals(actual === expected1 || actual === expected2, true, "each with object argument iterates over the contents when not empty");
|
||||
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
|
||||
"each with object argument ignores the contents when empty");
|
||||
});
|
||||
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
/*global CompilerContext */
|
||||
/*global CompilerContext, Handlebars, handlebarsEnv, shouldThrow */
|
||||
describe('data', function() {
|
||||
it("passing in data to a compiled function that expects data - works with helpers", function() {
|
||||
var template = CompilerContext.compile("{{hello}}", {data: true});
|
||||
@@ -88,25 +88,25 @@ describe('data', function() {
|
||||
it("parameter data throws when using this scope references", function() {
|
||||
var string = "{{#goodbyes}}{{text}} cruel {{@./name}}! {{/goodbyes}}";
|
||||
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(string);
|
||||
}).should.throw(Error);
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it("parameter data throws when using parent scope references", function() {
|
||||
var string = "{{#goodbyes}}{{text}} cruel {{@../name}}! {{/goodbyes}}";
|
||||
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(string);
|
||||
}).should.throw(Error);
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it("parameter data throws when using complex scope references", function() {
|
||||
var string = "{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}";
|
||||
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(string);
|
||||
}).should.throw(Error);
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it("data is inherited downstream", function() {
|
||||
|
||||
Vendored
+24
-4
@@ -1,12 +1,12 @@
|
||||
global.should = require('should');
|
||||
|
||||
global.shouldCompileTo = function(string, hashOrArray, expected, message) {
|
||||
shouldCompileToWithPartials(string, hashOrArray, false, expected, message);
|
||||
};
|
||||
|
||||
global.shouldCompileToWithPartials = function(string, hashOrArray, partials, expected, message) {
|
||||
var result = compileWithPartials(string, hashOrArray, partials);
|
||||
result.should.equal(expected, "'" + expected + "' should === '" + result + "': " + message);
|
||||
if (result !== expected) {
|
||||
throw new Error("'" + expected + "' should === '" + result + "': " + message);
|
||||
}
|
||||
};
|
||||
|
||||
global.compileWithPartials = function(string, hashOrArray, partials) {
|
||||
@@ -24,5 +24,25 @@ global.compileWithPartials = function(string, hashOrArray, partials) {
|
||||
|
||||
|
||||
global.equals = global.equal = function(a, b, msg) {
|
||||
a.should.equal(b, msg || '');
|
||||
if (a !== b) {
|
||||
throw new Error("'" + b + "' should === '" + a + "'" + (msg ? ": " + msg : ''));
|
||||
}
|
||||
};
|
||||
|
||||
global.shouldThrow = function(callback, type, msg) {
|
||||
var failed;
|
||||
try {
|
||||
callback();
|
||||
failed = true;
|
||||
} catch (err) {
|
||||
if (type && !(err instanceof type)) {
|
||||
throw new Error('Type failure');
|
||||
}
|
||||
if (msg && !(msg.test ? msg.test(err.message) : msg === err.message)) {
|
||||
throw new Error('Message failure');
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
throw new Error('It failed to throw');
|
||||
}
|
||||
};
|
||||
|
||||
+10
-10
@@ -1,4 +1,4 @@
|
||||
/*global CompilerContext, shouldCompileTo, shouldCompileToWithPartials */
|
||||
/*global CompilerContext, Handlebars, shouldCompileTo, shouldCompileToWithPartials, shouldThrow, handlebarsEnv */
|
||||
describe('helpers', function() {
|
||||
it("helper with complex lookup$", function() {
|
||||
var string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}";
|
||||
@@ -212,10 +212,10 @@ describe('helpers', function() {
|
||||
});
|
||||
|
||||
it("using a quote in the middle of a parameter raises an error", function() {
|
||||
(function() {
|
||||
var string = 'Message: {{hello wo"rld"}}';
|
||||
var string = 'Message: {{hello wo"rld"}}';
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(string);
|
||||
}).should.throw(Error);
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it("escaping a String is possible", function(){
|
||||
@@ -351,10 +351,10 @@ describe('helpers', function() {
|
||||
|
||||
describe("helperMissing", function() {
|
||||
it("if a context is not found, helperMissing is used", function() {
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile("{{hello}} {{link_to world}}");
|
||||
template({});
|
||||
}).should.throw(/Missing helper: 'link_to'/);
|
||||
}, undefined, /Missing helper: 'link_to'/);
|
||||
});
|
||||
|
||||
it("if a context is not found, custom helperMissing is used", function() {
|
||||
@@ -417,9 +417,9 @@ describe('helpers', function() {
|
||||
equal(result, "bar", "'bar' should === '" + result);
|
||||
});
|
||||
it("Unknown helper call in knownHelpers only mode should throw", function() {
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile("{{typeof hello}}", {knownHelpersOnly: true});
|
||||
}).should.throw(Error);
|
||||
}, Error);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -491,7 +491,7 @@ describe('helpers', function() {
|
||||
|
||||
cruel: function(world) {
|
||||
return "cruel " + world.toUpperCase();
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var context = {
|
||||
@@ -513,7 +513,7 @@ describe('helpers', function() {
|
||||
|
||||
cruel: function(world) {
|
||||
return "cruel " + world.toUpperCase();
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var context = {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Mocha</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link rel="stylesheet" href="/node_modules/mocha/mocha.css" />
|
||||
<style>
|
||||
.headless .suite > h1,
|
||||
.headless .test.pass {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Show only errors in "headless", non-interactive mode.
|
||||
if (/headless=true/.test(location.href)) {
|
||||
document.documentElement.className = 'headless';
|
||||
}
|
||||
</script>
|
||||
<script src="/node_modules/mocha/mocha.js"></script>
|
||||
<script>
|
||||
mocha.setup('bdd');
|
||||
</script>
|
||||
<script src="/dist/handlebars.js"></script>
|
||||
|
||||
<script src="/spec/env/common.js"></script>
|
||||
<script>
|
||||
var CompilerContext = {
|
||||
compile: function(template, options) {
|
||||
var templateSpec = handlebarsEnv.precompile(template, options);
|
||||
return handlebarsEnv.template(safeEval(templateSpec));
|
||||
},
|
||||
compileWithPartial: function(template, options) {
|
||||
return handlebarsEnv.compile(template, options);
|
||||
}
|
||||
};
|
||||
|
||||
function safeEval(templateSpec) {
|
||||
var ret;
|
||||
eval('ret = ' + templateSpec);
|
||||
return ret;
|
||||
}
|
||||
</script>
|
||||
<script src="/tmp/tests.js"></script>
|
||||
<script>
|
||||
onload = function(){
|
||||
// The test harness leaks under FF. We should have decent global leak coverage from other tests
|
||||
if (!navigator.userAgent.match(/Firefox\/([\d.]+)/)) {
|
||||
mocha.checkLeaks();
|
||||
}
|
||||
mocha.run();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
</body>
|
||||
</html>
|
||||
+59
-58
@@ -1,3 +1,4 @@
|
||||
/*global Handlebars */
|
||||
describe('parser', function() {
|
||||
if (!Handlebars.print) {
|
||||
return;
|
||||
@@ -9,165 +10,165 @@ describe('parser', function() {
|
||||
}
|
||||
|
||||
it('parses simple mustaches', function() {
|
||||
ast_for('{{foo}}').should.equal("{{ ID:foo [] }}\n");
|
||||
ast_for('{{foo?}}').should.equal("{{ ID:foo? [] }}\n");
|
||||
ast_for('{{foo_}}').should.equal("{{ ID:foo_ [] }}\n");
|
||||
ast_for('{{foo-}}').should.equal("{{ ID:foo- [] }}\n");
|
||||
ast_for('{{foo:}}').should.equal("{{ ID:foo: [] }}\n");
|
||||
equals(ast_for('{{foo}}'), "{{ ID:foo [] }}\n");
|
||||
equals(ast_for('{{foo?}}'), "{{ ID:foo? [] }}\n");
|
||||
equals(ast_for('{{foo_}}'), "{{ ID:foo_ [] }}\n");
|
||||
equals(ast_for('{{foo-}}'), "{{ ID:foo- [] }}\n");
|
||||
equals(ast_for('{{foo:}}'), "{{ ID:foo: [] }}\n");
|
||||
});
|
||||
|
||||
it('parses simple mustaches with data', function() {
|
||||
ast_for("{{@foo}}").should.equal("{{ @ID:foo [] }}\n");
|
||||
equals(ast_for("{{@foo}}"), "{{ @ID:foo [] }}\n");
|
||||
});
|
||||
|
||||
it('parses mustaches with paths', function() {
|
||||
ast_for("{{foo/bar}}").should.equal("{{ PATH:foo/bar [] }}\n");
|
||||
equals(ast_for("{{foo/bar}}"), "{{ PATH:foo/bar [] }}\n");
|
||||
});
|
||||
|
||||
it('parses mustaches with this/foo', function() {
|
||||
ast_for("{{this/foo}}").should.equal("{{ ID:foo [] }}\n");
|
||||
equals(ast_for("{{this/foo}}"), "{{ ID:foo [] }}\n");
|
||||
});
|
||||
|
||||
it('parses mustaches with - in a path', function() {
|
||||
ast_for("{{foo-bar}}").should.equal("{{ ID:foo-bar [] }}\n");
|
||||
equals(ast_for("{{foo-bar}}"), "{{ ID:foo-bar [] }}\n");
|
||||
});
|
||||
|
||||
it('parses mustaches with parameters', function() {
|
||||
ast_for("{{foo bar}}").should.equal("{{ ID:foo [ID:bar] }}\n");
|
||||
equals(ast_for("{{foo bar}}"), "{{ ID:foo [ID:bar] }}\n");
|
||||
});
|
||||
|
||||
it('parses mustaches with string parameters', function() {
|
||||
ast_for("{{foo bar \"baz\" }}").should.equal('{{ ID:foo [ID:bar, "baz"] }}\n');
|
||||
equals(ast_for("{{foo bar \"baz\" }}"), '{{ ID:foo [ID:bar, "baz"] }}\n');
|
||||
});
|
||||
|
||||
it('parses mustaches with INTEGER parameters', function() {
|
||||
ast_for("{{foo 1}}").should.equal("{{ ID:foo [INTEGER{1}] }}\n");
|
||||
equals(ast_for("{{foo 1}}"), "{{ ID:foo [INTEGER{1}] }}\n");
|
||||
});
|
||||
|
||||
it('parses mustaches with BOOLEAN parameters', function() {
|
||||
ast_for("{{foo true}}").should.equal("{{ ID:foo [BOOLEAN{true}] }}\n");
|
||||
ast_for("{{foo false}}").should.equal("{{ ID:foo [BOOLEAN{false}] }}\n");
|
||||
equals(ast_for("{{foo true}}"), "{{ ID:foo [BOOLEAN{true}] }}\n");
|
||||
equals(ast_for("{{foo false}}"), "{{ ID:foo [BOOLEAN{false}] }}\n");
|
||||
});
|
||||
|
||||
it('parses mutaches with DATA parameters', function() {
|
||||
ast_for("{{foo @bar}}").should.equal("{{ ID:foo [@ID:bar] }}\n");
|
||||
equals(ast_for("{{foo @bar}}"), "{{ ID:foo [@ID:bar] }}\n");
|
||||
});
|
||||
|
||||
it('parses mustaches with hash arguments', function() {
|
||||
ast_for("{{foo bar=baz}}").should.equal("{{ ID:foo [] HASH{bar=ID:baz} }}\n");
|
||||
ast_for("{{foo bar=1}}").should.equal("{{ ID:foo [] HASH{bar=INTEGER{1}} }}\n");
|
||||
ast_for("{{foo bar=true}}").should.equal("{{ ID:foo [] HASH{bar=BOOLEAN{true}} }}\n");
|
||||
ast_for("{{foo bar=false}}").should.equal("{{ ID:foo [] HASH{bar=BOOLEAN{false}} }}\n");
|
||||
ast_for("{{foo bar=@baz}}").should.equal("{{ ID:foo [] HASH{bar=@ID:baz} }}\n");
|
||||
equals(ast_for("{{foo bar=baz}}"), "{{ ID:foo [] HASH{bar=ID:baz} }}\n");
|
||||
equals(ast_for("{{foo bar=1}}"), "{{ ID:foo [] HASH{bar=INTEGER{1}} }}\n");
|
||||
equals(ast_for("{{foo bar=true}}"), "{{ ID:foo [] HASH{bar=BOOLEAN{true}} }}\n");
|
||||
equals(ast_for("{{foo bar=false}}"), "{{ ID:foo [] HASH{bar=BOOLEAN{false}} }}\n");
|
||||
equals(ast_for("{{foo bar=@baz}}"), "{{ ID:foo [] HASH{bar=@ID:baz} }}\n");
|
||||
|
||||
ast_for("{{foo bar=baz bat=bam}}").should.equal("{{ ID:foo [] HASH{bar=ID:baz, bat=ID:bam} }}\n");
|
||||
ast_for("{{foo bar=baz bat=\"bam\"}}").should.equal('{{ ID:foo [] HASH{bar=ID:baz, bat="bam"} }}\n');
|
||||
equals(ast_for("{{foo bar=baz bat=bam}}"), "{{ ID:foo [] HASH{bar=ID:baz, bat=ID:bam} }}\n");
|
||||
equals(ast_for("{{foo bar=baz bat=\"bam\"}}"), '{{ ID:foo [] HASH{bar=ID:baz, bat="bam"} }}\n');
|
||||
|
||||
ast_for("{{foo bat='bam'}}").should.equal('{{ ID:foo [] HASH{bat="bam"} }}\n');
|
||||
equals(ast_for("{{foo bat='bam'}}"), '{{ ID:foo [] HASH{bat="bam"} }}\n');
|
||||
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\"}}").should.equal('{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam"} }}\n');
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should.equal('{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=INTEGER{1}} }}\n');
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should.equal('{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=BOOLEAN{true}} }}\n');
|
||||
ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should.equal('{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=BOOLEAN{false}} }}\n');
|
||||
equals(ast_for("{{foo omg bar=baz bat=\"bam\"}}"), '{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam"} }}\n');
|
||||
equals(ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}"), '{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=INTEGER{1}} }}\n');
|
||||
equals(ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}"), '{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=BOOLEAN{true}} }}\n');
|
||||
equals(ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}"), '{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=BOOLEAN{false}} }}\n');
|
||||
});
|
||||
|
||||
it('parses contents followed by a mustache', function() {
|
||||
ast_for("foo bar {{baz}}").should.equal("CONTENT[ \'foo bar \' ]\n{{ ID:baz [] }}\n");
|
||||
equals(ast_for("foo bar {{baz}}"), "CONTENT[ \'foo bar \' ]\n{{ ID:baz [] }}\n");
|
||||
});
|
||||
|
||||
it('parses a partial', function() {
|
||||
ast_for("{{> foo }}").should.equal("{{> PARTIAL:foo }}\n");
|
||||
equals(ast_for("{{> foo }}"), "{{> PARTIAL:foo }}\n");
|
||||
});
|
||||
|
||||
it('parses a partial with context', function() {
|
||||
ast_for("{{> foo bar}}").should.equal("{{> PARTIAL:foo ID:bar }}\n");
|
||||
equals(ast_for("{{> foo bar}}"), "{{> PARTIAL:foo ID:bar }}\n");
|
||||
});
|
||||
|
||||
it('parses a partial with a complex name', function() {
|
||||
ast_for("{{> shared/partial?.bar}}").should.equal("{{> PARTIAL:shared/partial?.bar }}\n");
|
||||
equals(ast_for("{{> shared/partial?.bar}}"), "{{> PARTIAL:shared/partial?.bar }}\n");
|
||||
});
|
||||
|
||||
it('parses a comment', function() {
|
||||
ast_for("{{! this is a comment }}").should.equal("{{! ' this is a comment ' }}\n");
|
||||
equals(ast_for("{{! this is a comment }}"), "{{! ' this is a comment ' }}\n");
|
||||
});
|
||||
|
||||
it('parses a multi-line comment', function() {
|
||||
ast_for("{{!\nthis is a multi-line comment\n}}").should.equal("{{! \'\nthis is a multi-line comment\n\' }}\n");
|
||||
equals(ast_for("{{!\nthis is a multi-line comment\n}}"), "{{! \'\nthis is a multi-line comment\n\' }}\n");
|
||||
});
|
||||
|
||||
it('parses an inverse section', function() {
|
||||
ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
|
||||
equals(ast_for("{{#foo}} bar {{^}} baz {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
|
||||
});
|
||||
|
||||
it('parses an inverse (else-style) section', function() {
|
||||
ast_for("{{#foo}} bar {{else}} baz {{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
|
||||
equals(ast_for("{{#foo}} bar {{else}} baz {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
|
||||
});
|
||||
|
||||
it('parses empty blocks', function() {
|
||||
ast_for("{{#foo}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n");
|
||||
equals(ast_for("{{#foo}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n");
|
||||
});
|
||||
|
||||
it('parses empty blocks with empty inverse section', function() {
|
||||
ast_for("{{#foo}}{{^}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n");
|
||||
equals(ast_for("{{#foo}}{{^}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n");
|
||||
});
|
||||
|
||||
it('parses empty blocks with empty inverse (else-style) section', function() {
|
||||
ast_for("{{#foo}}{{else}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n");
|
||||
equals(ast_for("{{#foo}}{{else}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n");
|
||||
});
|
||||
|
||||
it('parses non-empty blocks with empty inverse section', function() {
|
||||
ast_for("{{#foo}} bar {{^}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
|
||||
equals(ast_for("{{#foo}} bar {{^}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
|
||||
});
|
||||
|
||||
it('parses non-empty blocks with empty inverse (else-style) section', function() {
|
||||
ast_for("{{#foo}} bar {{else}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
|
||||
equals(ast_for("{{#foo}} bar {{else}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
|
||||
});
|
||||
|
||||
it('parses empty blocks with non-empty inverse section', function() {
|
||||
ast_for("{{#foo}}{{^}} bar {{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
|
||||
equals(ast_for("{{#foo}}{{^}} bar {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
|
||||
});
|
||||
|
||||
it('parses empty blocks with non-empty inverse (else-style) section', function() {
|
||||
ast_for("{{#foo}}{{else}} bar {{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
|
||||
equals(ast_for("{{#foo}}{{else}} bar {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
|
||||
});
|
||||
|
||||
it('parses a standalone inverse section', function() {
|
||||
ast_for("{{^foo}}bar{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n {{^}}\n CONTENT[ 'bar' ]\n");
|
||||
equals(ast_for("{{^foo}}bar{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n {{^}}\n CONTENT[ 'bar' ]\n");
|
||||
});
|
||||
|
||||
it("raises if there's a Parse error", function() {
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
ast_for("foo{{^}}bar");
|
||||
}).should.throw(/Parse error on line 1/);
|
||||
(function() {
|
||||
}, Error, /Parse error on line 1/);
|
||||
shouldThrow(function() {
|
||||
ast_for("{{foo}");
|
||||
}).should.throw(/Parse error on line 1/);
|
||||
(function() {
|
||||
}, Error, /Parse error on line 1/);
|
||||
shouldThrow(function() {
|
||||
ast_for("{{foo &}}");
|
||||
}).should.throw(/Parse error on line 1/);
|
||||
(function() {
|
||||
}, Error, /Parse error on line 1/);
|
||||
shouldThrow(function() {
|
||||
ast_for("{{#goodbyes}}{{/hellos}}");
|
||||
}).should.throw(/goodbyes doesn't match hellos/);
|
||||
}, Error, /goodbyes doesn't match hellos/);
|
||||
});
|
||||
|
||||
it('knows how to report the correct line number in errors', function() {
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
ast_for("hello\nmy\n{{foo}");
|
||||
}).should.throw(/Parse error on line 3/);
|
||||
(function() {
|
||||
}, Error, /Parse error on line 3/);
|
||||
shouldThrow(function() {
|
||||
ast_for("hello\n\nmy\n\n{{foo}");
|
||||
}).should.throw(/Parse error on line 5/);
|
||||
}, Error, /Parse error on line 5/);
|
||||
});
|
||||
|
||||
it('knows how to report the correct line number in errors when the first character is a newline', function() {
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
ast_for("\n\nhello\n\nmy\n\n{{foo}");
|
||||
}).should.throw(/Parse error on line 7/);
|
||||
}, Error, /Parse error on line 7/);
|
||||
});
|
||||
|
||||
describe('externally compiled AST', function() {
|
||||
it('can pass through an already-compiled AST', function() {
|
||||
ast_for(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")])).should.equal("CONTENT[ \'Hello\' ]\n");
|
||||
equals(ast_for(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")])), "CONTENT[ \'Hello\' ]\n");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+4
-4
@@ -32,17 +32,17 @@ describe('partials', function() {
|
||||
});
|
||||
|
||||
it("rendering undefined partial throws an exception", function() {
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile("{{> whatever}}");
|
||||
template();
|
||||
}).should.throw(Handlebars.Exception, 'The partial whatever could not be found');
|
||||
}, Handlebars.Exception, 'The partial whatever could not be found');
|
||||
});
|
||||
|
||||
it("rendering template partial in vm mode throws an exception", function() {
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile("{{> whatever}}");
|
||||
template();
|
||||
}).should.throw(Handlebars.Exception, 'The partial whatever could not be found');
|
||||
}, Handlebars.Exception, 'The partial whatever could not be found');
|
||||
});
|
||||
|
||||
it("rendering function partial in vm mode", function() {
|
||||
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
/*global CompilerContext, shouldCompileTo */
|
||||
/*global CompilerContext, Handlebars, shouldCompileTo, shouldThrow */
|
||||
describe('Regressions', function() {
|
||||
it("GH-94: Cannot read property of undefined", function() {
|
||||
var data = {"books":[{"title":"The origin of species","author":{"name":"Charles Darwin"}},{"title":"Lazarillo de Tormes"}]};
|
||||
@@ -85,12 +85,12 @@ describe('Regressions', function() {
|
||||
});
|
||||
|
||||
it('GH-437: Matching escaping', function() {
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile('{{{a}}');
|
||||
}).should.throw(Error);
|
||||
(function() {
|
||||
}, Error);
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile('{{a}}}');
|
||||
}).should.throw(Error);
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it("Mustache man page", function() {
|
||||
@@ -106,9 +106,9 @@ describe('Regressions', function() {
|
||||
});
|
||||
|
||||
it("Passing falsy values to Handlebars.compile throws an error", function() {
|
||||
(function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(null);
|
||||
}).should.throw("You must pass a string or Handlebars AST to Handlebars.precompile. You passed null");
|
||||
}, Error, 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null');
|
||||
});
|
||||
|
||||
if (Handlebars.AST) {
|
||||
|
||||
+4
-4
@@ -2,22 +2,22 @@ if (typeof(require) !== 'undefined' && require.extensions[".handlebars"]) {
|
||||
describe('Require', function() {
|
||||
it('Load .handlebars files with require()', function() {
|
||||
var template = require("./artifacts/example_1");
|
||||
template.should.eql(require("./artifacts/example_1.handlebars"));
|
||||
equal(template, require("./artifacts/example_1.handlebars"));
|
||||
|
||||
var expected = 'foo\n';
|
||||
var result = template({foo: "foo"});
|
||||
|
||||
result.should.equal(expected);
|
||||
equal(result, expected);
|
||||
});
|
||||
|
||||
it('Load .hbs files with require()', function() {
|
||||
var template = require("./artifacts/example_2");
|
||||
template.should.eql(require("./artifacts/example_2.hbs"));
|
||||
equal(template, require("./artifacts/example_2.hbs"));
|
||||
|
||||
var expected = 'Hello, World!\n';
|
||||
var result = template({name: "World"});
|
||||
|
||||
result.should.equal(expected);
|
||||
equal(result, expected);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+121
-122
@@ -1,13 +1,12 @@
|
||||
var should = require('should');
|
||||
|
||||
should.Assertion.prototype.match_tokens = function(tokens) {
|
||||
this.obj.forEach(function(value, index) {
|
||||
value.name.should.equal(tokens[index]);
|
||||
});
|
||||
};
|
||||
should.Assertion.prototype.be_token = function(name, text) {
|
||||
this.obj.should.eql({name: name, text: text});
|
||||
};
|
||||
function shouldMatchTokens(result, tokens) {
|
||||
for (var index = 0; index < result.length; index++) {
|
||||
equals(result[index].name, tokens[index]);
|
||||
}
|
||||
}
|
||||
function shouldBeToken(result, name, text) {
|
||||
equals(result.name, name);
|
||||
equals(result.text, text);
|
||||
}
|
||||
|
||||
describe('Tokenizer', function() {
|
||||
if (!Handlebars.Parser) {
|
||||
@@ -35,324 +34,324 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('tokenizes a simple mustache as "OPEN ID CLOSE"', function() {
|
||||
var result = tokenize("{{foo}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("ID", "foo");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "ID", "foo");
|
||||
});
|
||||
|
||||
it('supports unescaping with &', function() {
|
||||
var result = tokenize("{{&bar}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
||||
|
||||
result[0].should.be_token("OPEN", "{{&");
|
||||
result[1].should.be_token("ID", "bar");
|
||||
shouldBeToken(result[0], "OPEN", "{{&");
|
||||
shouldBeToken(result[1], "ID", "bar");
|
||||
});
|
||||
|
||||
it('supports unescaping with {{{', function() {
|
||||
var result = tokenize("{{{bar}}}");
|
||||
result.should.match_tokens(['OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED']);
|
||||
shouldMatchTokens(result, ['OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED']);
|
||||
|
||||
result[1].should.be_token("ID", "bar");
|
||||
shouldBeToken(result[1], "ID", "bar");
|
||||
});
|
||||
|
||||
it('supports escaping delimiters', function() {
|
||||
var result = tokenize("{{foo}} \\{{bar}} {{baz}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
|
||||
result[3].should.be_token("CONTENT", " ");
|
||||
result[4].should.be_token("CONTENT", "{{bar}} ");
|
||||
shouldBeToken(result[3], "CONTENT", " ");
|
||||
shouldBeToken(result[4], "CONTENT", "{{bar}} ");
|
||||
});
|
||||
|
||||
it('supports escaping multiple delimiters', function() {
|
||||
var result = tokenize("{{foo}} \\{{bar}} \\{{baz}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']);
|
||||
|
||||
result[3].should.be_token("CONTENT", " ");
|
||||
result[4].should.be_token("CONTENT", "{{bar}} ");
|
||||
result[5].should.be_token("CONTENT", "{{baz}}");
|
||||
shouldBeToken(result[3], "CONTENT", " ");
|
||||
shouldBeToken(result[4], "CONTENT", "{{bar}} ");
|
||||
shouldBeToken(result[5], "CONTENT", "{{baz}}");
|
||||
});
|
||||
|
||||
it('supports escaping a triple stash', function() {
|
||||
var result = tokenize("{{foo}} \\{{{bar}}} {{baz}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
|
||||
result[4].should.be_token("CONTENT", "{{{bar}}} ");
|
||||
shouldBeToken(result[4], "CONTENT", "{{{bar}}} ");
|
||||
});
|
||||
|
||||
it('supports escaping escape character', function() {
|
||||
var result = tokenize("{{foo}} \\\\{{bar}} {{baz}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
|
||||
result[3].should.be_token("CONTENT", " \\");
|
||||
result[5].should.be_token("ID", "bar");
|
||||
shouldBeToken(result[3], "CONTENT", " \\");
|
||||
shouldBeToken(result[5], "ID", "bar");
|
||||
});
|
||||
|
||||
it('supports escaping multiple escape characters', function() {
|
||||
var result = tokenize("{{foo}} \\\\{{bar}} \\\\{{baz}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
|
||||
result[3].should.be_token("CONTENT", " \\");
|
||||
result[5].should.be_token("ID", "bar");
|
||||
result[7].should.be_token("CONTENT", " \\");
|
||||
result[9].should.be_token("ID", "baz");
|
||||
shouldBeToken(result[3], "CONTENT", " \\");
|
||||
shouldBeToken(result[5], "ID", "bar");
|
||||
shouldBeToken(result[7], "CONTENT", " \\");
|
||||
shouldBeToken(result[9], "ID", "baz");
|
||||
});
|
||||
|
||||
it('supports mixed escaped delimiters and escaped escape characters', function() {
|
||||
var result = tokenize("{{foo}} \\\\{{bar}} \\{{baz}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']);
|
||||
|
||||
result[3].should.be_token("CONTENT", " \\");
|
||||
result[4].should.be_token("OPEN", "{{");
|
||||
result[5].should.be_token("ID", "bar");
|
||||
result[7].should.be_token("CONTENT", " ");
|
||||
result[8].should.be_token("CONTENT", "{{baz}}");
|
||||
shouldBeToken(result[3], "CONTENT", " \\");
|
||||
shouldBeToken(result[4], "OPEN", "{{");
|
||||
shouldBeToken(result[5], "ID", "bar");
|
||||
shouldBeToken(result[7], "CONTENT", " ");
|
||||
shouldBeToken(result[8], "CONTENT", "{{baz}}");
|
||||
});
|
||||
|
||||
it('supports escaped escape character on a triple stash', function() {
|
||||
var result = tokenize("{{foo}} \\\\{{{bar}}} {{baz}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
|
||||
result[3].should.be_token("CONTENT", " \\");
|
||||
result[5].should.be_token("ID", "bar");
|
||||
shouldBeToken(result[3], "CONTENT", " \\");
|
||||
shouldBeToken(result[5], "ID", "bar");
|
||||
});
|
||||
|
||||
it('tokenizes a simple path', function() {
|
||||
var result = tokenize("{{foo/bar}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('allows dot notation', function() {
|
||||
var result = tokenize("{{foo.bar}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
|
||||
tokenize("{{foo.bar.baz}}").should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(tokenize("{{foo.bar.baz}}"), ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('allows path literals with []', function() {
|
||||
var result = tokenize("{{foo.[bar]}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('allows multiple path literals on a line with []', function() {
|
||||
var result = tokenize("{{foo.[bar]}}{{foo.[baz]}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE', 'OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE', 'OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes {{.}} as OPEN ID CLOSE', function() {
|
||||
var result = tokenize("{{.}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes a path as "OPEN (ID SEP)* ID CLOSE"', function() {
|
||||
var result = tokenize("{{../foo/bar}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("ID", "..");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "ID", "..");
|
||||
});
|
||||
|
||||
it('tokenizes a path with .. as a parent path', function() {
|
||||
var result = tokenize("{{../foo.bar}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("ID", "..");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "ID", "..");
|
||||
});
|
||||
|
||||
it('tokenizes a path with this/foo as OPEN ID SEP ID CLOSE', function() {
|
||||
var result = tokenize("{{this/foo}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("ID", "this");
|
||||
result[3].should.be_token("ID", "foo");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "ID", "this");
|
||||
shouldBeToken(result[3], "ID", "foo");
|
||||
});
|
||||
|
||||
it('tokenizes a simple mustache with spaces as "OPEN ID CLOSE"', function() {
|
||||
var result = tokenize("{{ foo }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("ID", "foo");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "ID", "foo");
|
||||
});
|
||||
|
||||
it('tokenizes a simple mustache with line breaks as "OPEN ID ID CLOSE"', function() {
|
||||
var result = tokenize("{{ foo \n bar }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("ID", "foo");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "ID", "foo");
|
||||
});
|
||||
|
||||
it('tokenizes raw content as "CONTENT"', function() {
|
||||
var result = tokenize("foo {{ bar }} baz");
|
||||
result.should.match_tokens(['CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT']);
|
||||
result[0].should.be_token("CONTENT", "foo ");
|
||||
result[4].should.be_token("CONTENT", " baz");
|
||||
shouldMatchTokens(result, ['CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT']);
|
||||
shouldBeToken(result[0], "CONTENT", "foo ");
|
||||
shouldBeToken(result[4], "CONTENT", " baz");
|
||||
});
|
||||
|
||||
it('tokenizes a partial as "OPEN_PARTIAL ID CLOSE"', function() {
|
||||
var result = tokenize("{{> foo}}");
|
||||
result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes a partial with context as "OPEN_PARTIAL ID ID CLOSE"', function() {
|
||||
var result = tokenize("{{> foo bar }}");
|
||||
result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes a partial without spaces as "OPEN_PARTIAL ID CLOSE"', function() {
|
||||
var result = tokenize("{{>foo}}");
|
||||
result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() {
|
||||
var result = tokenize("{{>foo }}");
|
||||
result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() {
|
||||
var result = tokenize("{{>foo/bar.baz }}");
|
||||
result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes a comment as "COMMENT"', function() {
|
||||
var result = tokenize("foo {{! this is a comment }} bar {{ baz }}");
|
||||
result.should.match_tokens(['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("COMMENT", " this is a comment ");
|
||||
shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "COMMENT", " this is a comment ");
|
||||
});
|
||||
|
||||
it('tokenizes a block comment as "COMMENT"', function() {
|
||||
var result = tokenize("foo {{!-- this is a {{comment}} --}} bar {{ baz }}");
|
||||
result.should.match_tokens(['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("COMMENT", " this is a {{comment}} ");
|
||||
shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "COMMENT", " this is a {{comment}} ");
|
||||
});
|
||||
|
||||
it('tokenizes a block comment with whitespace as "COMMENT"', function() {
|
||||
var result = tokenize("foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}");
|
||||
result.should.match_tokens(['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("COMMENT", " this is a\n{{comment}}\n");
|
||||
shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "COMMENT", " this is a\n{{comment}}\n");
|
||||
});
|
||||
|
||||
it('tokenizes open and closing blocks as OPEN_BLOCK, ID, CLOSE ..., OPEN_ENDBLOCK ID CLOSE', function() {
|
||||
var result = tokenize("{{#foo}}content{{/foo}}");
|
||||
result.should.match_tokens(['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes inverse sections as "OPEN_INVERSE CLOSE"', function() {
|
||||
tokenize("{{^}}").should.match_tokens(['OPEN_INVERSE', 'CLOSE']);
|
||||
tokenize("{{else}}").should.match_tokens(['OPEN_INVERSE', 'CLOSE']);
|
||||
tokenize("{{ else }}").should.match_tokens(['OPEN_INVERSE', 'CLOSE']);
|
||||
shouldMatchTokens(tokenize("{{^}}"), ['OPEN_INVERSE', 'CLOSE']);
|
||||
shouldMatchTokens(tokenize("{{else}}"), ['OPEN_INVERSE', 'CLOSE']);
|
||||
shouldMatchTokens(tokenize("{{ else }}"), ['OPEN_INVERSE', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes inverse sections with ID as "OPEN_INVERSE ID CLOSE"', function() {
|
||||
var result = tokenize("{{^foo}}");
|
||||
result.should.match_tokens(['OPEN_INVERSE', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("ID", "foo");
|
||||
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "ID", "foo");
|
||||
});
|
||||
|
||||
it('tokenizes inverse sections with ID and spaces as "OPEN_INVERSE ID CLOSE"', function() {
|
||||
var result = tokenize("{{^ foo }}");
|
||||
result.should.match_tokens(['OPEN_INVERSE', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("ID", "foo");
|
||||
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "ID", "foo");
|
||||
});
|
||||
|
||||
it('tokenizes mustaches with params as "OPEN ID ID ID CLOSE"', function() {
|
||||
var result = tokenize("{{ foo bar baz }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'CLOSE']);
|
||||
result[1].should.be_token("ID", "foo");
|
||||
result[2].should.be_token("ID", "bar");
|
||||
result[3].should.be_token("ID", "baz");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[1], "ID", "foo");
|
||||
shouldBeToken(result[2], "ID", "bar");
|
||||
shouldBeToken(result[3], "ID", "baz");
|
||||
});
|
||||
|
||||
it('tokenizes mustaches with String params as "OPEN ID ID STRING CLOSE"', function() {
|
||||
var result = tokenize("{{ foo bar \"baz\" }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
||||
result[3].should.be_token("STRING", "baz");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
||||
shouldBeToken(result[3], "STRING", "baz");
|
||||
});
|
||||
|
||||
it('tokenizes mustaches with String params using single quotes as "OPEN ID ID STRING CLOSE"', function() {
|
||||
var result = tokenize("{{ foo bar \'baz\' }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
||||
result[3].should.be_token("STRING", "baz");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
||||
shouldBeToken(result[3], "STRING", "baz");
|
||||
});
|
||||
|
||||
it('tokenizes String params with spaces inside as "STRING"', function() {
|
||||
var result = tokenize("{{ foo bar \"baz bat\" }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
||||
result[3].should.be_token("STRING", "baz bat");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
||||
shouldBeToken(result[3], "STRING", "baz bat");
|
||||
});
|
||||
|
||||
it('tokenizes String params with escapes quotes as STRING', function() {
|
||||
var result = tokenize('{{ foo "bar\\"baz" }}');
|
||||
result.should.match_tokens(['OPEN', 'ID', 'STRING', 'CLOSE']);
|
||||
result[2].should.be_token("STRING", 'bar"baz');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
|
||||
shouldBeToken(result[2], "STRING", 'bar"baz');
|
||||
});
|
||||
|
||||
it('tokenizes String params using single quotes with escapes quotes as STRING', function() {
|
||||
var result = tokenize("{{ foo 'bar\\'baz' }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'STRING', 'CLOSE']);
|
||||
result[2].should.be_token("STRING", "bar'baz");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
|
||||
shouldBeToken(result[2], "STRING", "bar'baz");
|
||||
});
|
||||
|
||||
it('tokenizes numbers', function() {
|
||||
var result = tokenize('{{ foo 1 }}');
|
||||
result.should.match_tokens(['OPEN', 'ID', 'INTEGER', 'CLOSE']);
|
||||
result[2].should.be_token("INTEGER", "1");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'INTEGER', 'CLOSE']);
|
||||
shouldBeToken(result[2], "INTEGER", "1");
|
||||
|
||||
result = tokenize('{{ foo -1 }}');
|
||||
result.should.match_tokens(['OPEN', 'ID', 'INTEGER', 'CLOSE']);
|
||||
result[2].should.be_token("INTEGER", "-1");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'INTEGER', 'CLOSE']);
|
||||
shouldBeToken(result[2], "INTEGER", "-1");
|
||||
});
|
||||
|
||||
it('tokenizes booleans', function() {
|
||||
var result = tokenize('{{ foo true }}');
|
||||
result.should.match_tokens(['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
|
||||
result[2].should.be_token("BOOLEAN", "true");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
|
||||
shouldBeToken(result[2], "BOOLEAN", "true");
|
||||
|
||||
result = tokenize('{{ foo false }}');
|
||||
result.should.match_tokens(['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
|
||||
result[2].should.be_token("BOOLEAN", "false");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
|
||||
shouldBeToken(result[2], "BOOLEAN", "false");
|
||||
});
|
||||
|
||||
it('tokenizes hash arguments', function() {
|
||||
var result = tokenize("{{ foo bar=baz }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
|
||||
result = tokenize("{{ foo bar baz=bat }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
|
||||
result = tokenize("{{ foo bar baz=1 }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'INTEGER', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'INTEGER', 'CLOSE']);
|
||||
|
||||
result = tokenize("{{ foo bar baz=true }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']);
|
||||
|
||||
result = tokenize("{{ foo bar baz=false }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']);
|
||||
|
||||
result = tokenize("{{ foo bar\n baz=bat }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
|
||||
result = tokenize("{{ foo bar baz=\"bat\" }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']);
|
||||
|
||||
result = tokenize("{{ foo bar baz=\"bat\" bam=wot }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
|
||||
result = tokenize("{{foo omg bar=baz bat=\"bam\"}}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']);
|
||||
result[2].should.be_token("ID", "omg");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']);
|
||||
shouldBeToken(result[2], "ID", "omg");
|
||||
});
|
||||
|
||||
it('tokenizes special @ identifiers', function() {
|
||||
var result = tokenize("{{ @foo }}");
|
||||
result.should.match_tokens(['OPEN', 'DATA', 'ID', 'CLOSE']);
|
||||
result[2].should.be_token("ID", "foo");
|
||||
shouldMatchTokens(result, ['OPEN', 'DATA', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[2], "ID", "foo");
|
||||
|
||||
result = tokenize("{{ foo @bar }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'DATA', 'ID', 'CLOSE']);
|
||||
result[3].should.be_token("ID", "bar");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'DATA', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[3], "ID", "bar");
|
||||
|
||||
result = tokenize("{{ foo bar=@baz }}");
|
||||
result.should.match_tokens(['OPEN', 'ID', 'ID', 'EQUALS', 'DATA', 'ID', 'CLOSE']);
|
||||
result[5].should.be_token("ID", "baz");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'DATA', 'ID', 'CLOSE']);
|
||||
shouldBeToken(result[5], "ID", "baz");
|
||||
});
|
||||
|
||||
it('does not time out in a mustache with a single } followed by EOF', function() {
|
||||
tokenize("{{foo}").should.match_tokens(['OPEN', 'ID']);
|
||||
shouldMatchTokens(tokenize("{{foo}"), ['OPEN', 'ID']);
|
||||
});
|
||||
|
||||
it('does not time out in a mustache when invalid ID characters are used', function() {
|
||||
tokenize("{{foo & }}").should.match_tokens(['OPEN', 'ID']);
|
||||
shouldMatchTokens(tokenize("{{foo & }}"), ['OPEN', 'ID']);
|
||||
});
|
||||
});
|
||||
|
||||
+23
-21
@@ -1,11 +1,13 @@
|
||||
/*global shouldCompileTo */
|
||||
/*global Handlebars, shouldCompileTo */
|
||||
|
||||
describe('utils', function() {
|
||||
describe('#SafeString', function() {
|
||||
it("constructing a safestring from a string and checking its type", function() {
|
||||
var safe = new Handlebars.SafeString("testing 1, 2, 3");
|
||||
safe.should.be.instanceof(Handlebars.SafeString);
|
||||
(safe == "testing 1, 2, 3").should.equal(true, "SafeString is equivalent to its underlying string");
|
||||
if (!(safe instanceof Handlebars.SafeString)) {
|
||||
throw new Error('Must be instance of SafeString');
|
||||
}
|
||||
equals(safe == 'testing 1, 2, 3', true, 'SafeString is equivalent to its underlying string');
|
||||
});
|
||||
|
||||
it("it should not escape SafeString properties", function() {
|
||||
@@ -17,41 +19,41 @@ describe('utils', function() {
|
||||
|
||||
describe('#escapeExpression', function() {
|
||||
it('shouhld escape html', function() {
|
||||
Handlebars.Utils.escapeExpression('foo<&"\'>').should.equal('foo<&"'>');
|
||||
equals(Handlebars.Utils.escapeExpression('foo<&"\'>'), 'foo<&"'>');
|
||||
});
|
||||
it('should not escape SafeString', function() {
|
||||
var string = new Handlebars.SafeString('foo<&"\'>');
|
||||
Handlebars.Utils.escapeExpression(string).should.equal('foo<&"\'>');
|
||||
equals(Handlebars.Utils.escapeExpression(string), 'foo<&"\'>');
|
||||
|
||||
});
|
||||
it('should handle falsy', function() {
|
||||
Handlebars.Utils.escapeExpression('').should.equal('');
|
||||
Handlebars.Utils.escapeExpression(undefined).should.equal('');
|
||||
Handlebars.Utils.escapeExpression(null).should.equal('');
|
||||
Handlebars.Utils.escapeExpression(false).should.equal('');
|
||||
equals(Handlebars.Utils.escapeExpression(''), '');
|
||||
equals(Handlebars.Utils.escapeExpression(undefined), '');
|
||||
equals(Handlebars.Utils.escapeExpression(null), '');
|
||||
equals(Handlebars.Utils.escapeExpression(false), '');
|
||||
|
||||
Handlebars.Utils.escapeExpression(0).should.equal('0');
|
||||
equals(Handlebars.Utils.escapeExpression(0), '0');
|
||||
});
|
||||
it('should handle empty objects', function() {
|
||||
Handlebars.Utils.escapeExpression({}).should.equal({}.toString());
|
||||
Handlebars.Utils.escapeExpression([]).should.equal([].toString());
|
||||
equals(Handlebars.Utils.escapeExpression({}), {}.toString());
|
||||
equals(Handlebars.Utils.escapeExpression([]), [].toString());
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isEmpty', function() {
|
||||
it('should not be empty', function() {
|
||||
Handlebars.Utils.isEmpty(undefined).should.equal(true);
|
||||
Handlebars.Utils.isEmpty(null).should.equal(true);
|
||||
Handlebars.Utils.isEmpty(false).should.equal(true);
|
||||
Handlebars.Utils.isEmpty('').should.equal(true);
|
||||
Handlebars.Utils.isEmpty([]).should.equal(true);
|
||||
equals(Handlebars.Utils.isEmpty(undefined), true);
|
||||
equals(Handlebars.Utils.isEmpty(null), true);
|
||||
equals(Handlebars.Utils.isEmpty(false), true);
|
||||
equals(Handlebars.Utils.isEmpty(''), true);
|
||||
equals(Handlebars.Utils.isEmpty([]), true);
|
||||
});
|
||||
|
||||
it('should be empty', function() {
|
||||
Handlebars.Utils.isEmpty(0).should.equal(false);
|
||||
Handlebars.Utils.isEmpty([1]).should.equal(false);
|
||||
Handlebars.Utils.isEmpty('foo').should.equal(false);
|
||||
Handlebars.Utils.isEmpty({bar: 1}).should.equal(false);
|
||||
equals(Handlebars.Utils.isEmpty(0), false);
|
||||
equals(Handlebars.Utils.isEmpty([1]), false);
|
||||
equals(Handlebars.Utils.isEmpty('foo'), false);
|
||||
equals(Handlebars.Utils.isEmpty({bar: 1}), false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user