Files
handlebars.js/spec/helpers.js
T

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&#x27;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');
});
});
});