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 '
' + options.fn(this) + '
'; }) .withMessage('Block helper executed with current context') .toCompileTo('

Yehuda

'); }); it('block helper should have context in this', function () { function link(options) { return '' + options.fn(this) + ''; } expectTemplate( '' ) .withInput({ people: [ { name: 'Alan', id: 1 }, { name: 'Yehuda', id: 2 }, ], }) .withHelper('link', link) .toCompileTo( '' ); }); 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}}

{{name}}

{{/form}}') .withInput({ yehuda: { name: 'Yehuda' } }) .withHelper('form', function (context, options) { return '
' + options.fn(context) + '
'; }) .withMessage('Context variable resolved') .toCompileTo('

Yehuda

'); }); 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 '
' + options.fn(context) + '
'; }) .withMessage('Complex path variable resolved') .toCompileTo('

Harold

'); }); 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 '
' + options.fn(context) + '
'; }) .withMessage('Both blocks executed') .toCompileTo('

Yehuda

Hello
'); }); 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 = ''; return out; } else { return '

' + 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(''); expectTemplate(string) .withInput({ people: [] }) .withHelpers({ list: list }) .withMessage('an inverse wrapper can be optionally called') .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'); }); }); });