1048 lines
33 KiB
JavaScript
1048 lines
33 KiB
JavaScript
describe('helpers', function() {
|
|
it('helper with complex lookup$', function() {
|
|
expectTemplate('{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}')
|
|
.withInput({
|
|
prefix: '/root',
|
|
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }]
|
|
})
|
|
.withHelper('link', function(prefix) {
|
|
return (
|
|
'<a href="' + prefix + '/' + this.url + '">' + this.text + '</a>'
|
|
);
|
|
})
|
|
.toCompileTo('<a href="/root/goodbye">Goodbye</a>');
|
|
});
|
|
|
|
it('helper for raw block gets raw content', function() {
|
|
expectTemplate('{{{{raw}}}} {{test}} {{{{/raw}}}}')
|
|
.withInput({ test: 'hello' })
|
|
.withHelper('raw', function(options) {
|
|
return options.fn();
|
|
})
|
|
.withMessage('raw block helper gets raw content')
|
|
.toCompileTo(' {{test}} ');
|
|
});
|
|
|
|
it('helper for raw block gets parameters', function() {
|
|
expectTemplate('{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}')
|
|
.withInput({ test: 'hello' })
|
|
.withHelper('raw', function(a, b, c, options) {
|
|
return options.fn() + a + b + c;
|
|
})
|
|
.withMessage('raw block helper gets raw content')
|
|
.toCompileTo(' {{test}} 123');
|
|
});
|
|
|
|
describe('raw block parsing (with identity helper-function)', function() {
|
|
function runWithIdentityHelper(template, expected) {
|
|
expectTemplate(template)
|
|
.withHelper('identity', function(options) {
|
|
return options.fn();
|
|
})
|
|
.toCompileTo(expected);
|
|
}
|
|
|
|
it('helper for nested raw block gets raw content', function() {
|
|
runWithIdentityHelper(
|
|
'{{{{identity}}}} {{{{b}}}} {{{{/b}}}} {{{{/identity}}}}',
|
|
' {{{{b}}}} {{{{/b}}}} '
|
|
);
|
|
});
|
|
|
|
it('helper for nested raw block works with empty content', function() {
|
|
runWithIdentityHelper('{{{{identity}}}}{{{{/identity}}}}', '');
|
|
});
|
|
|
|
xit('helper for nested raw block works if nested raw blocks are broken', function() {
|
|
// This test was introduced in 4.4.4, but it was not the actual problem that lead to the patch release
|
|
// The test is deactivated, because in 3.x this template cases an exception and it also does not work in 4.4.3
|
|
// If anyone can make this template work without breaking everything else, then go for it,
|
|
// but for now, this is just a known bug, that will be documented.
|
|
runWithIdentityHelper(
|
|
'{{{{identity}}}} {{{{a}}}} {{{{ {{{{/ }}}} }}}} {{{{/identity}}}}',
|
|
' {{{{a}}}} {{{{ {{{{/ }}}} }}}} '
|
|
);
|
|
});
|
|
|
|
it('helper for nested raw block closes after first matching close', function() {
|
|
runWithIdentityHelper(
|
|
'{{{{identity}}}}abc{{{{/identity}}}} {{{{identity}}}}abc{{{{/identity}}}}',
|
|
'abc abc'
|
|
);
|
|
});
|
|
|
|
it('helper for nested raw block throw exception when with missing closing braces', function() {
|
|
var string = '{{{{a}}}} {{{{/a';
|
|
expectTemplate(string).toThrow();
|
|
});
|
|
});
|
|
|
|
it('helper block with identical context', function() {
|
|
expectTemplate('{{#goodbyes}}{{name}}{{/goodbyes}}')
|
|
.withInput({ name: 'Alan' })
|
|
.withHelper('goodbyes', function(options) {
|
|
var out = '';
|
|
var byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
|
for (var i = 0, j = byes.length; i < j; i++) {
|
|
out += byes[i] + ' ' + options.fn(this) + '! ';
|
|
}
|
|
return out;
|
|
})
|
|
.toCompileTo('Goodbye Alan! goodbye Alan! GOODBYE Alan! ');
|
|
});
|
|
|
|
it('helper block with complex lookup expression', function() {
|
|
expectTemplate('{{#goodbyes}}{{../name}}{{/goodbyes}}')
|
|
.withInput({ name: 'Alan' })
|
|
.withHelper('goodbyes', function(options) {
|
|
var out = '';
|
|
var byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
|
for (var i = 0, j = byes.length; i < j; i++) {
|
|
out += byes[i] + ' ' + options.fn({}) + '! ';
|
|
}
|
|
return out;
|
|
})
|
|
.toCompileTo('Goodbye Alan! goodbye Alan! GOODBYE Alan! ');
|
|
});
|
|
|
|
it('helper with complex lookup and nested template', function() {
|
|
expectTemplate(
|
|
'{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}'
|
|
)
|
|
.withInput({
|
|
prefix: '/root',
|
|
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }]
|
|
})
|
|
.withHelper('link', function(prefix, options) {
|
|
return (
|
|
'<a href="' +
|
|
prefix +
|
|
'/' +
|
|
this.url +
|
|
'">' +
|
|
options.fn(this) +
|
|
'</a>'
|
|
);
|
|
})
|
|
.toCompileTo('<a href="/root/goodbye">Goodbye</a>');
|
|
});
|
|
|
|
it('helper with complex lookup and nested template in VM+Compiler', function() {
|
|
expectTemplate(
|
|
'{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}'
|
|
)
|
|
.withInput({
|
|
prefix: '/root',
|
|
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }]
|
|
})
|
|
.withHelper('link', function(prefix, options) {
|
|
return (
|
|
'<a href="' +
|
|
prefix +
|
|
'/' +
|
|
this.url +
|
|
'">' +
|
|
options.fn(this) +
|
|
'</a>'
|
|
);
|
|
})
|
|
.toCompileTo('<a href="/root/goodbye">Goodbye</a>');
|
|
});
|
|
|
|
it('helper returning undefined value', function() {
|
|
expectTemplate(' {{nothere}}')
|
|
.withHelpers({
|
|
nothere: function() {}
|
|
})
|
|
.toCompileTo(' ');
|
|
|
|
expectTemplate(' {{#nothere}}{{/nothere}}')
|
|
.withHelpers({
|
|
nothere: function() {}
|
|
})
|
|
.toCompileTo(' ');
|
|
});
|
|
|
|
it('block helper', function() {
|
|
expectTemplate('{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!')
|
|
.withInput({ world: 'world' })
|
|
.withHelper('goodbyes', function(options) {
|
|
return options.fn({ text: 'GOODBYE' });
|
|
})
|
|
.withMessage('Block helper executed')
|
|
.toCompileTo('GOODBYE! cruel world!');
|
|
});
|
|
|
|
it('block helper staying in the same context', function() {
|
|
expectTemplate('{{#form}}<p>{{name}}</p>{{/form}}')
|
|
.withInput({ name: 'Yehuda' })
|
|
.withHelper('form', function(options) {
|
|
return '<form>' + options.fn(this) + '</form>';
|
|
})
|
|
.withMessage('Block helper executed with current context')
|
|
.toCompileTo('<form><p>Yehuda</p></form>');
|
|
});
|
|
|
|
it('block helper should have context in this', function() {
|
|
function link(options) {
|
|
return '<a href="/people/' + this.id + '">' + options.fn(this) + '</a>';
|
|
}
|
|
|
|
expectTemplate(
|
|
'<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>'
|
|
)
|
|
.withInput({
|
|
people: [
|
|
{ name: 'Alan', id: 1 },
|
|
{ name: 'Yehuda', id: 2 }
|
|
]
|
|
})
|
|
.withHelper('link', link)
|
|
.toCompileTo(
|
|
'<ul><li><a href="/people/1">Alan</a></li><li><a href="/people/2">Yehuda</a></li></ul>'
|
|
);
|
|
});
|
|
|
|
it('block helper for undefined value', function() {
|
|
expectTemplate("{{#empty}}shouldn't render{{/empty}}").toCompileTo('');
|
|
});
|
|
|
|
it('block helper passing a new context', function() {
|
|
expectTemplate('{{#form yehuda}}<p>{{name}}</p>{{/form}}')
|
|
.withInput({ yehuda: { name: 'Yehuda' } })
|
|
.withHelper('form', function(context, options) {
|
|
return '<form>' + options.fn(context) + '</form>';
|
|
})
|
|
.withMessage('Context variable resolved')
|
|
.toCompileTo('<form><p>Yehuda</p></form>');
|
|
});
|
|
|
|
it('block helper passing a complex path context', function() {
|
|
expectTemplate('{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}')
|
|
.withInput({ yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } })
|
|
.withHelper('form', function(context, options) {
|
|
return '<form>' + options.fn(context) + '</form>';
|
|
})
|
|
.withMessage('Complex path variable resolved')
|
|
.toCompileTo('<form><p>Harold</p></form>');
|
|
});
|
|
|
|
it('nested block helpers', function() {
|
|
expectTemplate(
|
|
'{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}'
|
|
)
|
|
.withInput({
|
|
yehuda: { name: 'Yehuda' }
|
|
})
|
|
.withHelper('link', function(options) {
|
|
return '<a href="' + this.name + '">' + options.fn(this) + '</a>';
|
|
})
|
|
.withHelper('form', function(context, options) {
|
|
return '<form>' + options.fn(context) + '</form>';
|
|
})
|
|
.withMessage('Both blocks executed')
|
|
.toCompileTo('<form><p>Yehuda</p><a href="Yehuda">Hello</a></form>');
|
|
});
|
|
|
|
it('block helper inverted sections', function() {
|
|
var string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}";
|
|
function list(context, options) {
|
|
if (context.length > 0) {
|
|
var out = '<ul>';
|
|
for (var i = 0, j = context.length; i < j; i++) {
|
|
out += '<li>';
|
|
out += options.fn(context[i]);
|
|
out += '</li>';
|
|
}
|
|
out += '</ul>';
|
|
return out;
|
|
} else {
|
|
return '<p>' + options.inverse(this) + '</p>';
|
|
}
|
|
}
|
|
|
|
// the meaning here may be kind of hard to catch, but list.not is always called,
|
|
// so we should see the output of both
|
|
expectTemplate(string)
|
|
.withInput({ people: [{ name: 'Alan' }, { name: 'Yehuda' }] })
|
|
.withHelpers({ list: list })
|
|
.withMessage('an inverse wrapper is passed in as a new context')
|
|
.toCompileTo('<ul><li>Alan</li><li>Yehuda</li></ul>');
|
|
|
|
expectTemplate(string)
|
|
.withInput({ people: [] })
|
|
.withHelpers({ list: list })
|
|
.withMessage('an inverse wrapper can be optionally called')
|
|
.toCompileTo("<p><em>Nobody's here</em></p>");
|
|
|
|
expectTemplate('{{#list people}}Hello{{^}}{{message}}{{/list}}')
|
|
.withInput({
|
|
people: [],
|
|
message: "Nobody's here"
|
|
})
|
|
.withHelpers({ list: list })
|
|
.withMessage('the context of an inverse is the parent of the block')
|
|
.toCompileTo('<p>Nobody's here</p>');
|
|
});
|
|
|
|
it('pathed lambas with parameters', function() {
|
|
var hash = {
|
|
helper: function() {
|
|
return 'winning';
|
|
}
|
|
};
|
|
hash.hash = hash;
|
|
var helpers = {
|
|
'./helper': function() {
|
|
return 'fail';
|
|
}
|
|
};
|
|
|
|
expectTemplate('{{./helper 1}}')
|
|
.withInput(hash)
|
|
.withHelpers(helpers)
|
|
.toCompileTo('winning');
|
|
|
|
expectTemplate('{{hash/helper 1}}')
|
|
.withInput(hash)
|
|
.withHelpers(helpers)
|
|
.toCompileTo('winning');
|
|
});
|
|
|
|
describe('helpers hash', function() {
|
|
it('providing a helpers hash', function() {
|
|
expectTemplate('Goodbye {{cruel}} {{world}}!')
|
|
.withInput({ cruel: 'cruel' })
|
|
.withHelpers({
|
|
world: function() {
|
|
return 'world';
|
|
}
|
|
})
|
|
.withMessage('helpers hash is available')
|
|
.toCompileTo('Goodbye cruel world!');
|
|
|
|
expectTemplate('Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!')
|
|
.withInput({ iter: [{ cruel: 'cruel' }] })
|
|
.withHelpers({
|
|
world: function() {
|
|
return 'world';
|
|
}
|
|
})
|
|
.withMessage('helpers hash is available inside other blocks')
|
|
.toCompileTo('Goodbye cruel world!');
|
|
});
|
|
|
|
it('in cases of conflict, helpers win', function() {
|
|
expectTemplate('{{{lookup}}}')
|
|
.withInput({ lookup: 'Explicit' })
|
|
.withHelpers({
|
|
lookup: function() {
|
|
return 'helpers';
|
|
}
|
|
})
|
|
.withMessage('helpers hash has precedence escaped expansion')
|
|
.toCompileTo('helpers');
|
|
|
|
expectTemplate('{{lookup}}')
|
|
.withInput({ lookup: 'Explicit' })
|
|
.withHelpers({
|
|
lookup: function() {
|
|
return 'helpers';
|
|
}
|
|
})
|
|
.withMessage('helpers hash has precedence simple expansion')
|
|
.toCompileTo('helpers');
|
|
});
|
|
|
|
it('the helpers hash is available is nested contexts', function() {
|
|
expectTemplate('{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}')
|
|
.withInput({ outer: { inner: { unused: [] } } })
|
|
.withHelpers({
|
|
helper: function() {
|
|
return 'helper';
|
|
}
|
|
})
|
|
.withMessage('helpers hash is available in nested contexts.')
|
|
.toCompileTo('helper');
|
|
});
|
|
|
|
it('the helper hash should augment the global hash', function() {
|
|
handlebarsEnv.registerHelper('test_helper', function() {
|
|
return 'found it!';
|
|
});
|
|
|
|
expectTemplate(
|
|
'{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}'
|
|
)
|
|
.withInput({ cruel: 'cruel' })
|
|
.withHelpers({
|
|
world: function() {
|
|
return 'world!';
|
|
}
|
|
})
|
|
.toCompileTo('found it! Goodbye cruel world!!');
|
|
});
|
|
});
|
|
|
|
describe('registration', function() {
|
|
it('unregisters', function() {
|
|
handlebarsEnv.helpers = {};
|
|
|
|
handlebarsEnv.registerHelper('foo', function() {
|
|
return 'fail';
|
|
});
|
|
handlebarsEnv.unregisterHelper('foo');
|
|
equals(handlebarsEnv.helpers.foo, undefined);
|
|
});
|
|
|
|
it('allows multiple globals', function() {
|
|
var helpers = handlebarsEnv.helpers;
|
|
handlebarsEnv.helpers = {};
|
|
|
|
handlebarsEnv.registerHelper({
|
|
if: helpers['if'],
|
|
world: function() {
|
|
return 'world!';
|
|
},
|
|
testHelper: function() {
|
|
return 'found it!';
|
|
}
|
|
});
|
|
|
|
expectTemplate(
|
|
'{{testHelper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}'
|
|
)
|
|
.withInput({ cruel: 'cruel' })
|
|
.toCompileTo('found it! Goodbye cruel world!!');
|
|
});
|
|
|
|
it('fails with multiple and args', function() {
|
|
shouldThrow(
|
|
function() {
|
|
handlebarsEnv.registerHelper(
|
|
{
|
|
world: function() {
|
|
return 'world!';
|
|
},
|
|
testHelper: function() {
|
|
return 'found it!';
|
|
}
|
|
},
|
|
{}
|
|
);
|
|
},
|
|
Error,
|
|
'Arg not supported with multiple helpers'
|
|
);
|
|
});
|
|
});
|
|
|
|
it('decimal number literals work', function() {
|
|
expectTemplate('Message: {{hello -1.2 1.2}}')
|
|
.withHelper('hello', function(times, times2) {
|
|
if (typeof times !== 'number') {
|
|
times = 'NaN';
|
|
}
|
|
if (typeof times2 !== 'number') {
|
|
times2 = 'NaN';
|
|
}
|
|
return 'Hello ' + times + ' ' + times2 + ' times';
|
|
})
|
|
.withMessage('template with a negative integer literal')
|
|
.toCompileTo('Message: Hello -1.2 1.2 times');
|
|
});
|
|
|
|
it('negative number literals work', function() {
|
|
expectTemplate('Message: {{hello -12}}')
|
|
.withHelper('hello', function(times) {
|
|
if (typeof times !== 'number') {
|
|
times = 'NaN';
|
|
}
|
|
return 'Hello ' + times + ' times';
|
|
})
|
|
.withMessage('template with a negative integer literal')
|
|
.toCompileTo('Message: Hello -12 times');
|
|
});
|
|
|
|
describe('String literal parameters', function() {
|
|
it('simple literals work', function() {
|
|
expectTemplate('Message: {{hello "world" 12 true false}}')
|
|
.withHelper('hello', function(param, times, bool1, bool2) {
|
|
if (typeof times !== 'number') {
|
|
times = 'NaN';
|
|
}
|
|
if (typeof bool1 !== 'boolean') {
|
|
bool1 = 'NaB';
|
|
}
|
|
if (typeof bool2 !== 'boolean') {
|
|
bool2 = 'NaB';
|
|
}
|
|
return (
|
|
'Hello ' + param + ' ' + times + ' times: ' + bool1 + ' ' + bool2
|
|
);
|
|
})
|
|
.withMessage('template with a simple String literal')
|
|
.toCompileTo('Message: Hello world 12 times: true false');
|
|
});
|
|
|
|
it('using a quote in the middle of a parameter raises an error', function() {
|
|
expectTemplate('Message: {{hello wo"rld"}}').toThrow(Error);
|
|
});
|
|
|
|
it('escaping a String is possible', function() {
|
|
expectTemplate('Message: {{{hello "\\"world\\""}}}')
|
|
.withHelper('hello', function(param) {
|
|
return 'Hello ' + param;
|
|
})
|
|
.withMessage('template with an escaped String literal')
|
|
.toCompileTo('Message: Hello "world"');
|
|
});
|
|
|
|
it("it works with ' marks", function() {
|
|
expectTemplate('Message: {{{hello "Alan\'s world"}}}')
|
|
.withHelper('hello', function(param) {
|
|
return 'Hello ' + param;
|
|
})
|
|
.withMessage("template with a ' mark")
|
|
.toCompileTo("Message: Hello Alan's world");
|
|
});
|
|
});
|
|
|
|
it('negative number literals work', function() {
|
|
expectTemplate('Message: {{hello -12}}')
|
|
.withHelper('hello', function(times) {
|
|
if (typeof times !== 'number') {
|
|
times = 'NaN';
|
|
}
|
|
return 'Hello ' + times + ' times';
|
|
})
|
|
.withMessage('template with a negative integer literal')
|
|
.toCompileTo('Message: Hello -12 times');
|
|
});
|
|
|
|
describe('multiple parameters', function() {
|
|
it('simple multi-params work', function() {
|
|
expectTemplate('Message: {{goodbye cruel world}}')
|
|
.withInput({ cruel: 'cruel', world: 'world' })
|
|
.withHelper('goodbye', function(cruel, world) {
|
|
return 'Goodbye ' + cruel + ' ' + world;
|
|
})
|
|
.withMessage('regular helpers with multiple params')
|
|
.toCompileTo('Message: Goodbye cruel world');
|
|
});
|
|
|
|
it('block multi-params work', function() {
|
|
expectTemplate(
|
|
'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}'
|
|
)
|
|
.withInput({ cruel: 'cruel', world: 'world' })
|
|
.withHelper('goodbye', function(cruel, world, options) {
|
|
return options.fn({ greeting: 'Goodbye', adj: cruel, noun: world });
|
|
})
|
|
.withMessage('block helpers with multiple params')
|
|
.toCompileTo('Message: Goodbye cruel world');
|
|
});
|
|
});
|
|
|
|
describe('hash', function() {
|
|
it('helpers can take an optional hash', function() {
|
|
expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" times=12}}')
|
|
.withHelper('goodbye', function(options) {
|
|
return (
|
|
'GOODBYE ' +
|
|
options.hash.cruel +
|
|
' ' +
|
|
options.hash.world +
|
|
' ' +
|
|
options.hash.times +
|
|
' TIMES'
|
|
);
|
|
})
|
|
.withMessage('Helper output hash')
|
|
.toCompileTo('GOODBYE CRUEL WORLD 12 TIMES');
|
|
});
|
|
|
|
it('helpers can take an optional hash with booleans', function() {
|
|
function goodbye(options) {
|
|
if (options.hash.print === true) {
|
|
return 'GOODBYE ' + options.hash.cruel + ' ' + options.hash.world;
|
|
} else if (options.hash.print === false) {
|
|
return 'NOT PRINTING';
|
|
} else {
|
|
return 'THIS SHOULD NOT HAPPEN';
|
|
}
|
|
}
|
|
|
|
expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" print=true}}')
|
|
.withHelper('goodbye', goodbye)
|
|
.withMessage('Helper output hash')
|
|
.toCompileTo('GOODBYE CRUEL WORLD');
|
|
|
|
expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" print=false}}')
|
|
.withHelper('goodbye', goodbye)
|
|
.withMessage('Boolean helper parameter honored')
|
|
.toCompileTo('NOT PRINTING');
|
|
});
|
|
|
|
it('block helpers can take an optional hash', function() {
|
|
expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
|
|
.withHelper('goodbye', function(options) {
|
|
return (
|
|
'GOODBYE ' +
|
|
options.hash.cruel +
|
|
' ' +
|
|
options.fn(this) +
|
|
' ' +
|
|
options.hash.times +
|
|
' TIMES'
|
|
);
|
|
})
|
|
.withMessage('Hash parameters output')
|
|
.toCompileTo('GOODBYE CRUEL world 12 TIMES');
|
|
});
|
|
|
|
it('block helpers can take an optional hash with single quoted stings', function() {
|
|
expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
|
|
.withHelper('goodbye', function(options) {
|
|
return (
|
|
'GOODBYE ' +
|
|
options.hash.cruel +
|
|
' ' +
|
|
options.fn(this) +
|
|
' ' +
|
|
options.hash.times +
|
|
' TIMES'
|
|
);
|
|
})
|
|
.withMessage('Hash parameters output')
|
|
.toCompileTo('GOODBYE CRUEL world 12 TIMES');
|
|
});
|
|
|
|
it('block helpers can take an optional hash with booleans', function() {
|
|
function goodbye(options) {
|
|
if (options.hash.print === true) {
|
|
return 'GOODBYE ' + options.hash.cruel + ' ' + options.fn(this);
|
|
} else if (options.hash.print === false) {
|
|
return 'NOT PRINTING';
|
|
} else {
|
|
return 'THIS SHOULD NOT HAPPEN';
|
|
}
|
|
}
|
|
|
|
expectTemplate('{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}')
|
|
.withHelper('goodbye', goodbye)
|
|
.withMessage('Boolean hash parameter honored')
|
|
.toCompileTo('GOODBYE CRUEL world');
|
|
|
|
expectTemplate('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}')
|
|
.withHelper('goodbye', goodbye)
|
|
.withMessage('Boolean hash parameter honored')
|
|
.toCompileTo('NOT PRINTING');
|
|
});
|
|
});
|
|
|
|
describe('helperMissing', function() {
|
|
it('if a context is not found, helperMissing is used', function() {
|
|
expectTemplate('{{hello}} {{link_to world}}').toThrow(
|
|
/Missing helper: "link_to"/
|
|
);
|
|
});
|
|
|
|
it('if a context is not found, custom helperMissing is used', function() {
|
|
expectTemplate('{{hello}} {{link_to world}}')
|
|
.withInput({ hello: 'Hello', world: 'world' })
|
|
.withHelper('helperMissing', function(mesg, options) {
|
|
if (options.name === 'link_to') {
|
|
return new Handlebars.SafeString('<a>' + mesg + '</a>');
|
|
}
|
|
})
|
|
.toCompileTo('Hello <a>world</a>');
|
|
});
|
|
|
|
it('if a value is not found, custom helperMissing is used', function() {
|
|
expectTemplate('{{hello}} {{link_to}}')
|
|
.withInput({ hello: 'Hello', world: 'world' })
|
|
.withHelper('helperMissing', function(options) {
|
|
if (options.name === 'link_to') {
|
|
return new Handlebars.SafeString('<a>winning</a>');
|
|
}
|
|
})
|
|
.toCompileTo('Hello <a>winning</a>');
|
|
});
|
|
});
|
|
|
|
describe('knownHelpers', function() {
|
|
it('Known helper should render helper', function() {
|
|
expectTemplate('{{hello}}')
|
|
.withCompileOptions({
|
|
knownHelpers: { hello: true }
|
|
})
|
|
.withHelper('hello', function() {
|
|
return 'foo';
|
|
})
|
|
.toCompileTo('foo');
|
|
});
|
|
|
|
it('Unknown helper in knownHelpers only mode should be passed as undefined', function() {
|
|
expectTemplate('{{typeof hello}}')
|
|
.withCompileOptions({
|
|
knownHelpers: { typeof: true },
|
|
knownHelpersOnly: true
|
|
})
|
|
.withHelper('typeof', function(arg) {
|
|
return typeof arg;
|
|
})
|
|
.withHelper('hello', function() {
|
|
return 'foo';
|
|
})
|
|
.toCompileTo('undefined');
|
|
});
|
|
|
|
it('Builtin helpers available in knownHelpers only mode', function() {
|
|
expectTemplate('{{#unless foo}}bar{{/unless}}')
|
|
.withCompileOptions({
|
|
knownHelpersOnly: true
|
|
})
|
|
.toCompileTo('bar');
|
|
});
|
|
|
|
it('Field lookup works in knownHelpers only mode', function() {
|
|
expectTemplate('{{foo}}')
|
|
.withCompileOptions({
|
|
knownHelpersOnly: true
|
|
})
|
|
.withInput({ foo: 'bar' })
|
|
.toCompileTo('bar');
|
|
});
|
|
|
|
it('Conditional blocks work in knownHelpers only mode', function() {
|
|
expectTemplate('{{#foo}}bar{{/foo}}')
|
|
.withCompileOptions({
|
|
knownHelpersOnly: true
|
|
})
|
|
.withInput({ foo: 'baz' })
|
|
.toCompileTo('bar');
|
|
});
|
|
|
|
it('Invert blocks work in knownHelpers only mode', function() {
|
|
expectTemplate('{{^foo}}bar{{/foo}}')
|
|
.withCompileOptions({
|
|
knownHelpersOnly: true
|
|
})
|
|
.withInput({ foo: false })
|
|
.toCompileTo('bar');
|
|
});
|
|
|
|
it('Functions are bound to the context in knownHelpers only mode', function() {
|
|
expectTemplate('{{foo}}')
|
|
.withCompileOptions({
|
|
knownHelpersOnly: true
|
|
})
|
|
.withInput({
|
|
foo: function() {
|
|
return this.bar;
|
|
},
|
|
bar: 'bar'
|
|
})
|
|
.toCompileTo('bar');
|
|
});
|
|
|
|
it('Unknown helper call in knownHelpers only mode should throw', function() {
|
|
expectTemplate('{{typeof hello}}')
|
|
.withCompileOptions({ knownHelpersOnly: true })
|
|
.toThrow(Error);
|
|
});
|
|
});
|
|
|
|
describe('blockHelperMissing', function() {
|
|
it('lambdas are resolved by blockHelperMissing, not handlebars proper', function() {
|
|
expectTemplate('{{#truthy}}yep{{/truthy}}')
|
|
.withInput({
|
|
truthy: function() {
|
|
return true;
|
|
}
|
|
})
|
|
.toCompileTo('yep');
|
|
});
|
|
|
|
it('lambdas resolved by blockHelperMissing are bound to the context', function() {
|
|
expectTemplate('{{#truthy}}yep{{/truthy}}')
|
|
.withInput({
|
|
truthy: function() {
|
|
return this.truthiness();
|
|
},
|
|
truthiness: function() {
|
|
return false;
|
|
}
|
|
})
|
|
.toCompileTo('');
|
|
});
|
|
});
|
|
|
|
describe('name field', function() {
|
|
var helpers = {
|
|
blockHelperMissing: function() {
|
|
return 'missing: ' + arguments[arguments.length - 1].name;
|
|
},
|
|
helperMissing: function() {
|
|
return 'helper missing: ' + arguments[arguments.length - 1].name;
|
|
},
|
|
helper: function() {
|
|
return 'ran: ' + arguments[arguments.length - 1].name;
|
|
}
|
|
};
|
|
|
|
it('should include in ambiguous mustache calls', function() {
|
|
expectTemplate('{{helper}}')
|
|
.withHelpers(helpers)
|
|
.toCompileTo('ran: helper');
|
|
});
|
|
|
|
it('should include in helper mustache calls', function() {
|
|
expectTemplate('{{helper 1}}')
|
|
.withHelpers(helpers)
|
|
.toCompileTo('ran: helper');
|
|
});
|
|
|
|
it('should include in ambiguous block calls', function() {
|
|
expectTemplate('{{#helper}}{{/helper}}')
|
|
.withHelpers(helpers)
|
|
.toCompileTo('ran: helper');
|
|
});
|
|
|
|
it('should include in simple block calls', function() {
|
|
expectTemplate('{{#./helper}}{{/./helper}}')
|
|
.withHelpers(helpers)
|
|
.toCompileTo('missing: ./helper');
|
|
});
|
|
|
|
it('should include in helper block calls', function() {
|
|
expectTemplate('{{#helper 1}}{{/helper}}')
|
|
.withHelpers(helpers)
|
|
.toCompileTo('ran: helper');
|
|
});
|
|
|
|
it('should include in known helper calls', function() {
|
|
expectTemplate('{{helper}}')
|
|
.withCompileOptions({
|
|
knownHelpers: { helper: true },
|
|
knownHelpersOnly: true
|
|
})
|
|
.withHelpers(helpers)
|
|
.toCompileTo('ran: helper');
|
|
});
|
|
|
|
it('should include full id', function() {
|
|
expectTemplate('{{#foo.helper}}{{/foo.helper}}')
|
|
.withInput({ foo: {} })
|
|
.withHelpers(helpers)
|
|
.toCompileTo('missing: foo.helper');
|
|
});
|
|
|
|
it('should include full id if a hash is passed', function() {
|
|
expectTemplate('{{#foo.helper bar=baz}}{{/foo.helper}}')
|
|
.withInput({ foo: {} })
|
|
.withHelpers(helpers)
|
|
.toCompileTo('helper missing: foo.helper');
|
|
});
|
|
});
|
|
|
|
describe('name conflicts', function() {
|
|
it('helpers take precedence over same-named context properties', function() {
|
|
expectTemplate('{{goodbye}} {{cruel world}}')
|
|
.withHelper('goodbye', function() {
|
|
return this.goodbye.toUpperCase();
|
|
})
|
|
.withHelper('cruel', function(world) {
|
|
return 'cruel ' + world.toUpperCase();
|
|
})
|
|
.withInput({
|
|
goodbye: 'goodbye',
|
|
world: 'world'
|
|
})
|
|
.withMessage('Helper executed')
|
|
.toCompileTo('GOODBYE cruel WORLD');
|
|
});
|
|
|
|
it('helpers take precedence over same-named context properties$', function() {
|
|
expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}}')
|
|
.withHelper('goodbye', function(options) {
|
|
return this.goodbye.toUpperCase() + options.fn(this);
|
|
})
|
|
.withHelper('cruel', function(world) {
|
|
return 'cruel ' + world.toUpperCase();
|
|
})
|
|
.withInput({
|
|
goodbye: 'goodbye',
|
|
world: 'world'
|
|
})
|
|
.withMessage('Helper executed')
|
|
.toCompileTo('GOODBYE cruel WORLD');
|
|
});
|
|
|
|
it('Scoped names take precedence over helpers', function() {
|
|
expectTemplate('{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}')
|
|
.withHelper('goodbye', function() {
|
|
return this.goodbye.toUpperCase();
|
|
})
|
|
.withHelper('cruel', function(world) {
|
|
return 'cruel ' + world.toUpperCase();
|
|
})
|
|
.withInput({
|
|
goodbye: 'goodbye',
|
|
world: 'world'
|
|
})
|
|
.withMessage('Helper not executed')
|
|
.toCompileTo('goodbye cruel WORLD cruel GOODBYE');
|
|
});
|
|
|
|
it('Scoped names take precedence over block helpers', function() {
|
|
expectTemplate(
|
|
'{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}'
|
|
)
|
|
.withHelper('goodbye', function(options) {
|
|
return this.goodbye.toUpperCase() + options.fn(this);
|
|
})
|
|
.withHelper('cruel', function(world) {
|
|
return 'cruel ' + world.toUpperCase();
|
|
})
|
|
.withInput({
|
|
goodbye: 'goodbye',
|
|
world: 'world'
|
|
})
|
|
.withMessage('Helper executed')
|
|
.toCompileTo('GOODBYE cruel WORLD goodbye');
|
|
});
|
|
});
|
|
|
|
describe('block params', function() {
|
|
it('should take presedence over context values', function() {
|
|
expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}')
|
|
.withInput({ value: 'foo' })
|
|
.withHelper('goodbyes', function(options) {
|
|
equals(options.fn.blockParams, 1);
|
|
return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
|
|
})
|
|
.toCompileTo('1foo');
|
|
});
|
|
|
|
it('should take presedence over helper values', function() {
|
|
expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}')
|
|
.withHelper('value', function() {
|
|
return 'foo';
|
|
})
|
|
.withHelper('goodbyes', function(options) {
|
|
equals(options.fn.blockParams, 1);
|
|
return options.fn({}, { blockParams: [1, 2] });
|
|
})
|
|
.toCompileTo('1foo');
|
|
});
|
|
|
|
it('should not take presedence over pathed values', function() {
|
|
expectTemplate(
|
|
'{{#goodbyes as |value|}}{{./value}}{{/goodbyes}}{{value}}'
|
|
)
|
|
.withInput({ value: 'bar' })
|
|
.withHelper('value', function() {
|
|
return 'foo';
|
|
})
|
|
.withHelper('goodbyes', function(options) {
|
|
equals(options.fn.blockParams, 1);
|
|
return options.fn(this, { blockParams: [1, 2] });
|
|
})
|
|
.toCompileTo('barfoo');
|
|
});
|
|
|
|
it('should take presednece over parent block params', function() {
|
|
var value = 1;
|
|
expectTemplate(
|
|
'{{#goodbyes as |value|}}{{#goodbyes}}{{value}}{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{/goodbyes}}{{/goodbyes}}{{value}}'
|
|
)
|
|
.withInput({ value: 'foo' })
|
|
.withHelper('goodbyes', function(options) {
|
|
return options.fn(
|
|
{ value: 'bar' },
|
|
{
|
|
blockParams:
|
|
options.fn.blockParams === 1 ? [value++, value++] : undefined
|
|
}
|
|
);
|
|
})
|
|
.toCompileTo('13foo');
|
|
});
|
|
|
|
it('should allow block params on chained helpers', function() {
|
|
expectTemplate(
|
|
'{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}'
|
|
)
|
|
.withInput({ value: 'foo' })
|
|
.withHelper('goodbyes', function(options) {
|
|
equals(options.fn.blockParams, 1);
|
|
return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
|
|
})
|
|
.toCompileTo('1foo');
|
|
});
|
|
});
|
|
|
|
describe('built-in helpers malformed arguments ', function() {
|
|
it('if helper - too few arguments', function() {
|
|
expectTemplate('{{#if}}{{/if}}').toThrow(
|
|
/#if requires exactly one argument/
|
|
);
|
|
});
|
|
|
|
it('if helper - too many arguments, string', function() {
|
|
expectTemplate('{{#if test "string"}}{{/if}}').toThrow(
|
|
/#if requires exactly one argument/
|
|
);
|
|
});
|
|
|
|
it('if helper - too many arguments, undefined', function() {
|
|
expectTemplate('{{#if test undefined}}{{/if}}').toThrow(
|
|
/#if requires exactly one argument/
|
|
);
|
|
});
|
|
|
|
it('if helper - too many arguments, null', function() {
|
|
expectTemplate('{{#if test null}}{{/if}}').toThrow(
|
|
/#if requires exactly one argument/
|
|
);
|
|
});
|
|
|
|
it('unless helper - too few arguments', function() {
|
|
expectTemplate('{{#unless}}{{/unless}}').toThrow(
|
|
/#unless requires exactly one argument/
|
|
);
|
|
});
|
|
|
|
it('unless helper - too many arguments', function() {
|
|
expectTemplate('{{#unless test null}}{{/unless}}').toThrow(
|
|
/#unless requires exactly one argument/
|
|
);
|
|
});
|
|
|
|
it('with helper - too few arguments', function() {
|
|
expectTemplate('{{#with}}{{/with}}').toThrow(
|
|
/#with requires exactly one argument/
|
|
);
|
|
});
|
|
|
|
it('with helper - too many arguments', function() {
|
|
expectTemplate('{{#with test "string"}}{{/with}}').toThrow(
|
|
/#with requires exactly one argument/
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('the lookupProperty-option', function() {
|
|
it('should be passed to custom helpers', function() {
|
|
expectTemplate('{{testHelper}}')
|
|
.withHelper('testHelper', function testHelper(options) {
|
|
return options.lookupProperty(this, 'testProperty');
|
|
})
|
|
.withInput({ testProperty: 'abc' })
|
|
.toCompileTo('abc');
|
|
});
|
|
});
|
|
});
|