306 lines
13 KiB
JavaScript
306 lines
13 KiB
JavaScript
global.handlebarsEnv = null;
|
|
|
|
beforeEach(function() {
|
|
global.handlebarsEnv = Handlebars.create();
|
|
});
|
|
|
|
describe('basic context', function() {
|
|
it('most basic', function() {
|
|
shouldCompileTo('{{foo}}', { foo: 'foo' }, 'foo');
|
|
});
|
|
|
|
it('escaping', function() {
|
|
shouldCompileTo('\\{{foo}}', { foo: 'food' }, '{{foo}}');
|
|
shouldCompileTo('content \\{{foo}}', { foo: 'food' }, 'content {{foo}}');
|
|
shouldCompileTo('\\\\{{foo}}', { foo: 'food' }, '\\food');
|
|
shouldCompileTo('content \\\\{{foo}}', { foo: 'food' }, 'content \\food');
|
|
shouldCompileTo('\\\\ {{foo}}', { foo: 'food' }, '\\\\ food');
|
|
});
|
|
|
|
it('compiling with a basic context', function() {
|
|
shouldCompileTo('Goodbye\n{{cruel}}\n{{world}}!', {cruel: 'cruel', world: 'world'}, 'Goodbye\ncruel\nworld!',
|
|
'It works if all the required keys are provided');
|
|
});
|
|
|
|
it('compiling with a string context', function() {
|
|
shouldCompileTo('{{.}}{{length}}', 'bye', 'bye3');
|
|
});
|
|
|
|
it('compiling with an undefined context', function() {
|
|
shouldCompileTo('Goodbye\n{{cruel}}\n{{world.bar}}!', undefined, 'Goodbye\n\n!');
|
|
|
|
shouldCompileTo('{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}', undefined, 'Goodbye');
|
|
});
|
|
|
|
it('comments', function() {
|
|
shouldCompileTo('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!',
|
|
{cruel: 'cruel', world: 'world'}, 'Goodbye\ncruel\nworld!',
|
|
'comments are ignored');
|
|
|
|
shouldCompileTo(' {{~! comment ~}} blah', {}, 'blah');
|
|
shouldCompileTo(' {{~!-- long-comment --~}} blah', {}, 'blah');
|
|
shouldCompileTo(' {{! comment ~}} blah', {}, ' blah');
|
|
shouldCompileTo(' {{!-- long-comment --~}} blah', {}, ' blah');
|
|
shouldCompileTo(' {{~! comment}} blah', {}, ' blah');
|
|
shouldCompileTo(' {{~!-- long-comment --}} blah', {}, ' blah');
|
|
});
|
|
|
|
it('boolean', function() {
|
|
var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!';
|
|
shouldCompileTo(string, {goodbye: true, world: 'world'}, 'GOODBYE cruel world!',
|
|
'booleans show the contents when true');
|
|
|
|
shouldCompileTo(string, {goodbye: false, world: 'world'}, 'cruel world!',
|
|
'booleans do not show the contents when false');
|
|
});
|
|
|
|
it('zeros', function() {
|
|
shouldCompileTo('num1: {{num1}}, num2: {{num2}}', {num1: 42, num2: 0},
|
|
'num1: 42, num2: 0');
|
|
shouldCompileTo('num: {{.}}', 0, 'num: 0');
|
|
shouldCompileTo('num: {{num1/num2}}', {num1: {num2: 0}}, 'num: 0');
|
|
});
|
|
it('false', function() {
|
|
/* eslint-disable no-new-wrappers */
|
|
shouldCompileTo('val1: {{val1}}, val2: {{val2}}', {val1: false, val2: new Boolean(false)}, 'val1: false, val2: false');
|
|
shouldCompileTo('val: {{.}}', false, 'val: false');
|
|
shouldCompileTo('val: {{val1/val2}}', {val1: {val2: false}}, 'val: false');
|
|
|
|
shouldCompileTo('val1: {{{val1}}}, val2: {{{val2}}}', {val1: false, val2: new Boolean(false)}, 'val1: false, val2: false');
|
|
shouldCompileTo('val: {{{val1/val2}}}', {val1: {val2: false}}, 'val: false');
|
|
/* eslint-enable */
|
|
});
|
|
|
|
it('should handle undefined and null', function() {
|
|
shouldCompileTo('{{awesome undefined null}}',
|
|
{
|
|
awesome: function(_undefined, _null, options) {
|
|
return (_undefined === undefined) + ' ' + (_null === null) + ' ' + (typeof options);
|
|
}
|
|
},
|
|
'true true object');
|
|
shouldCompileTo('{{undefined}}',
|
|
{
|
|
'undefined': function() {
|
|
return 'undefined!';
|
|
}
|
|
},
|
|
'undefined!');
|
|
shouldCompileTo('{{null}}',
|
|
{
|
|
'null': function() {
|
|
return 'null!';
|
|
}
|
|
},
|
|
'null!');
|
|
});
|
|
|
|
it('newlines', function() {
|
|
shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest");
|
|
shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest");
|
|
});
|
|
|
|
it('escaping text', function() {
|
|
shouldCompileTo("Awesome's", {}, "Awesome's", "text is escaped so that it doesn't get caught on single quotes");
|
|
shouldCompileTo('Awesome\\', {}, 'Awesome\\', "text is escaped so that the closing quote can't be ignored");
|
|
shouldCompileTo('Awesome\\\\ foo', {}, 'Awesome\\\\ foo', "text is escaped so that it doesn't mess up backslashes");
|
|
shouldCompileTo('Awesome {{foo}}', {foo: '\\'}, 'Awesome \\', "text is escaped so that it doesn't mess up backslashes");
|
|
shouldCompileTo(" ' ' ", {}, " ' ' ", 'double quotes never produce invalid javascript');
|
|
});
|
|
|
|
it('escaping expressions', function() {
|
|
shouldCompileTo('{{{awesome}}}', {awesome: '&\'\\<>'}, '&\'\\<>',
|
|
"expressions with 3 handlebars aren't escaped");
|
|
|
|
shouldCompileTo('{{&awesome}}', {awesome: '&\'\\<>'}, '&\'\\<>',
|
|
"expressions with {{& handlebars aren't escaped");
|
|
|
|
shouldCompileTo('{{awesome}}', {awesome: "&\"'`\\<>"}, '&"'`\\<>',
|
|
'by default expressions should be escaped');
|
|
|
|
shouldCompileTo('{{awesome}}', {awesome: 'Escaped, <b> looks like: <b>'}, 'Escaped, <b> looks like: &lt;b&gt;',
|
|
'escaping should properly handle amperstands');
|
|
});
|
|
|
|
it("functions returning safestrings shouldn't be escaped", function() {
|
|
var hash = {awesome: function() { return new Handlebars.SafeString('&\'\\<>'); }};
|
|
shouldCompileTo('{{awesome}}', hash, '&\'\\<>',
|
|
"functions returning safestrings aren't escaped");
|
|
});
|
|
|
|
it('functions', function() {
|
|
shouldCompileTo('{{awesome}}', {awesome: function() { return 'Awesome'; }}, 'Awesome',
|
|
'functions are called and render their output');
|
|
shouldCompileTo('{{awesome}}', {awesome: function() { return this.more; }, more: 'More awesome'}, 'More awesome',
|
|
'functions are bound to the context');
|
|
});
|
|
|
|
it('functions with context argument', function() {
|
|
shouldCompileTo('{{awesome frank}}',
|
|
{awesome: function(context) { return context; },
|
|
frank: 'Frank'},
|
|
'Frank', 'functions are called with context arguments');
|
|
});
|
|
it('pathed functions with context argument', function() {
|
|
shouldCompileTo('{{bar.awesome frank}}',
|
|
{bar: {awesome: function(context) { return context; }},
|
|
frank: 'Frank'},
|
|
'Frank', 'functions are called with context arguments');
|
|
});
|
|
it('depthed functions with context argument', function() {
|
|
shouldCompileTo('{{#with frank}}{{../awesome .}}{{/with}}',
|
|
{awesome: function(context) { return context; },
|
|
frank: 'Frank'},
|
|
'Frank', 'functions are called with context arguments');
|
|
});
|
|
|
|
it('block functions with context argument', function() {
|
|
shouldCompileTo('{{#awesome 1}}inner {{.}}{{/awesome}}',
|
|
{awesome: function(context, options) { return options.fn(context); }},
|
|
'inner 1', 'block functions are called with context and options');
|
|
});
|
|
|
|
it('depthed block functions with context argument', function() {
|
|
shouldCompileTo('{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}',
|
|
{value: true, awesome: function(context, options) { return options.fn(context); }},
|
|
'inner 1', 'block functions are called with context and options');
|
|
});
|
|
|
|
it('block functions without context argument', function() {
|
|
shouldCompileTo('{{#awesome}}inner{{/awesome}}',
|
|
{awesome: function(options) { return options.fn(this); }},
|
|
'inner', 'block functions are called with options');
|
|
});
|
|
it('pathed block functions without context argument', function() {
|
|
shouldCompileTo('{{#foo.awesome}}inner{{/foo.awesome}}',
|
|
{foo: {awesome: function() { return this; }}},
|
|
'inner', 'block functions are called with options');
|
|
});
|
|
it('depthed block functions without context argument', function() {
|
|
shouldCompileTo('{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}',
|
|
{value: true, awesome: function() { return this; }},
|
|
'inner', 'block functions are called with options');
|
|
});
|
|
|
|
|
|
it('paths with hyphens', function() {
|
|
shouldCompileTo('{{foo-bar}}', {'foo-bar': 'baz'}, 'baz', 'Paths can contain hyphens (-)');
|
|
shouldCompileTo('{{foo.foo-bar}}', {foo: {'foo-bar': 'baz'}}, 'baz', 'Paths can contain hyphens (-)');
|
|
shouldCompileTo('{{foo/foo-bar}}', {foo: {'foo-bar': 'baz'}}, 'baz', 'Paths can contain hyphens (-)');
|
|
});
|
|
|
|
it('nested paths', function() {
|
|
shouldCompileTo('Goodbye {{alan/expression}} world!', {alan: {expression: 'beautiful'}},
|
|
'Goodbye beautiful world!', 'Nested paths access nested objects');
|
|
});
|
|
|
|
it('nested paths with empty string value', function() {
|
|
shouldCompileTo('Goodbye {{alan/expression}} world!', {alan: {expression: ''}},
|
|
'Goodbye world!', 'Nested paths access nested objects with empty string');
|
|
});
|
|
|
|
it('literal paths', function() {
|
|
shouldCompileTo('Goodbye {{[@alan]/expression}} world!', {'@alan': {expression: 'beautiful'}},
|
|
'Goodbye beautiful world!', 'Literal paths can be used');
|
|
shouldCompileTo('Goodbye {{[foo bar]/expression}} world!', {'foo bar': {expression: 'beautiful'}},
|
|
'Goodbye beautiful world!', 'Literal paths can be used');
|
|
});
|
|
|
|
it('literal references', function() {
|
|
shouldCompileTo('Goodbye {{[foo bar]}} world!', {'foo bar': 'beautiful'}, 'Goodbye beautiful world!');
|
|
shouldCompileTo('Goodbye {{"foo bar"}} world!', {'foo bar': 'beautiful'}, 'Goodbye beautiful world!');
|
|
shouldCompileTo("Goodbye {{'foo bar'}} world!", {'foo bar': 'beautiful'}, 'Goodbye beautiful world!');
|
|
shouldCompileTo('Goodbye {{"foo[bar"}} world!', {'foo[bar': 'beautiful'}, 'Goodbye beautiful world!');
|
|
shouldCompileTo('Goodbye {{"foo\'bar"}} world!', {"foo'bar": 'beautiful'}, 'Goodbye beautiful world!');
|
|
shouldCompileTo("Goodbye {{'foo\"bar'}} world!", {'foo"bar': 'beautiful'}, 'Goodbye beautiful world!');
|
|
});
|
|
|
|
it("that current context path ({{.}}) doesn't hit helpers", function() {
|
|
shouldCompileTo('test: {{.}}', [null, {helper: 'awesome'}], 'test: ');
|
|
});
|
|
|
|
it('complex but empty paths', function() {
|
|
shouldCompileTo('{{person/name}}', {person: {name: null}}, '');
|
|
shouldCompileTo('{{person/name}}', {person: {}}, '');
|
|
});
|
|
|
|
it('this keyword in paths', function() {
|
|
var string = '{{#goodbyes}}{{this}}{{/goodbyes}}';
|
|
var hash = {goodbyes: ['goodbye', 'Goodbye', 'GOODBYE']};
|
|
shouldCompileTo(string, hash, 'goodbyeGoodbyeGOODBYE',
|
|
'This keyword in paths evaluates to current context');
|
|
|
|
string = '{{#hellos}}{{this/text}}{{/hellos}}';
|
|
hash = {hellos: [{text: 'hello'}, {text: 'Hello'}, {text: 'HELLO'}]};
|
|
shouldCompileTo(string, hash, 'helloHelloHELLO', 'This keyword evaluates in more complex paths');
|
|
});
|
|
|
|
it('this keyword nested inside path', function() {
|
|
shouldThrow(function() {
|
|
CompilerContext.compile('{{#hellos}}{{text/this/foo}}{{/hellos}}');
|
|
}, Error, 'Invalid path: text/this - 1:13');
|
|
|
|
shouldCompileTo('{{[this]}}', {'this': 'bar'}, 'bar');
|
|
shouldCompileTo('{{text/[this]}}', {text: {'this': 'bar'}}, 'bar');
|
|
});
|
|
|
|
it('this keyword in helpers', function() {
|
|
var helpers = {foo: function(value) {
|
|
return 'bar ' + value;
|
|
}};
|
|
var string = '{{#goodbyes}}{{foo this}}{{/goodbyes}}';
|
|
var hash = {goodbyes: ['goodbye', 'Goodbye', 'GOODBYE']};
|
|
shouldCompileTo(string, [hash, helpers], 'bar goodbyebar Goodbyebar GOODBYE',
|
|
'This keyword in paths evaluates to current context');
|
|
|
|
string = '{{#hellos}}{{foo this/text}}{{/hellos}}';
|
|
hash = {hellos: [{text: 'hello'}, {text: 'Hello'}, {text: 'HELLO'}]};
|
|
shouldCompileTo(string, [hash, helpers], 'bar hellobar Hellobar HELLO', 'This keyword evaluates in more complex paths');
|
|
});
|
|
|
|
it('this keyword nested inside helpers param', function() {
|
|
var string = '{{#hellos}}{{foo text/this/foo}}{{/hellos}}';
|
|
shouldThrow(function() {
|
|
CompilerContext.compile(string);
|
|
}, Error, 'Invalid path: text/this - 1:17');
|
|
|
|
shouldCompileTo(
|
|
'{{foo [this]}}',
|
|
{foo: function(value) { return value; }, 'this': 'bar'},
|
|
'bar');
|
|
shouldCompileTo(
|
|
'{{foo text/[this]}}',
|
|
{foo: function(value) { return value; }, text: {'this': 'bar'}},
|
|
'bar');
|
|
});
|
|
|
|
it('pass string literals', function() {
|
|
shouldCompileTo('{{"foo"}}', {}, '');
|
|
shouldCompileTo('{{"foo"}}', { foo: 'bar' }, 'bar');
|
|
shouldCompileTo('{{#"foo"}}{{.}}{{/"foo"}}', { foo: ['bar', 'baz'] }, 'barbaz');
|
|
});
|
|
|
|
it('pass number literals', function() {
|
|
shouldCompileTo('{{12}}', {}, '');
|
|
shouldCompileTo('{{12}}', { '12': 'bar' }, 'bar');
|
|
shouldCompileTo('{{12.34}}', {}, '');
|
|
shouldCompileTo('{{12.34}}', { '12.34': 'bar' }, 'bar');
|
|
shouldCompileTo('{{12.34 1}}', { '12.34': function(arg) { return 'bar' + arg; } }, 'bar1');
|
|
});
|
|
|
|
it('pass boolean literals', function() {
|
|
shouldCompileTo('{{true}}', {}, '');
|
|
shouldCompileTo('{{true}}', { '': 'foo' }, '');
|
|
shouldCompileTo('{{false}}', { 'false': 'foo' }, 'foo');
|
|
});
|
|
|
|
it('should handle literals in subexpression', function() {
|
|
var helpers = {
|
|
foo: function(arg) {
|
|
return arg;
|
|
}
|
|
};
|
|
shouldCompileTo('{{foo (false)}}', [{ 'false': function() { return 'bar'; } }, helpers], 'bar');
|
|
});
|
|
});
|