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