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 ( '' + this.text + '' ); }) .toCompileTo('Goodbye'); }); 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 ( '' + options.fn(this) + '' ); }) .toCompileTo('Goodbye'); }); 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 ( '' + options.fn(this) + '' ); }) .toCompileTo('Goodbye'); }); 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}}
{{name}}
{{/form}}') .withInput({ name: 'Yehuda' }) .withHelper('form', function(options) { return ''; }) .withMessage('Block helper executed with current context') .toCompileTo(''); }); it('block helper should have context in this', function() { function link(options) { return '' + options.fn(this) + ''; } expectTemplate( '{{name}}
{{/form}}') .withInput({ yehuda: { name: 'Yehuda' } }) .withHelper('form', function(context, options) { return ''; }) .withMessage('Context variable resolved') .toCompileTo(''); }); it('block helper passing a complex path context', function() { expectTemplate('{{#form yehuda/cat}}{{name}}
{{/form}}') .withInput({ yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } }) .withHelper('form', function(context, options) { return ''; }) .withMessage('Complex path variable resolved') .toCompileTo(''); }); it('nested block helpers', function() { expectTemplate( '{{#form yehuda}}{{name}}
{{#link}}Hello{{/link}}{{/form}}' ) .withInput({ yehuda: { name: 'Yehuda' } }) .withHelper('link', function(options) { return '' + options.fn(this) + ''; }) .withHelper('form', function(context, options) { return ''; }) .withMessage('Both blocks executed') .toCompileTo(''); }); it('block helper inverted sections', function() { var string = "{{#list people}}{{name}}{{^}}Nobody's here{{/list}}"; function list(context, options) { if (context.length > 0) { var out = '' + options.inverse(this) + '
'; } } // 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('Nobody's here
"); 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('Nobody's here
'); }); 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('' + mesg + ''); } }) .toCompileTo('Hello world'); }); 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('winning'); } }) .toCompileTo('Hello winning'); }); }); 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'); }); }); });