diff --git a/spec/basic.js b/spec/basic.js index 1e4ce2b6b..4c7afb706 100644 --- a/spec/basic.js +++ b/spec/basic.js @@ -15,15 +15,19 @@ describe('basic context', function() { expectTemplate('\\{{foo}}') .withInput({ foo: 'food' }) .toCompileTo('{{foo}}'); + expectTemplate('content \\{{foo}}') .withInput({ foo: 'food' }) .toCompileTo('content {{foo}}'); + expectTemplate('\\\\{{foo}}') .withInput({ foo: 'food' }) .toCompileTo('\\food'); + expectTemplate('content \\\\{{foo}}') .withInput({ foo: 'food' }) .toCompileTo('content \\food'); + expectTemplate('\\\\ {{foo}}') .withInput({ foo: 'food' }) .toCompileTo('\\\\ food'); @@ -65,14 +69,19 @@ describe('basic context', function() { .toCompileTo('Goodbye\ncruel\nworld!'); expectTemplate(' {{~! comment ~}} blah').toCompileTo('blah'); + expectTemplate(' {{~!-- long-comment --~}} blah').toCompileTo( 'blah' ); + expectTemplate(' {{! comment ~}} blah').toCompileTo(' blah'); + expectTemplate(' {{!-- long-comment --~}} blah').toCompileTo( ' blah' ); + expectTemplate(' {{~! comment}} blah').toCompileTo(' blah'); + expectTemplate(' {{~!-- long-comment --}} blah').toCompileTo( ' blah' ); @@ -104,13 +113,16 @@ describe('basic context', function() { num2: 0 }) .toCompileTo('num1: 42, num2: 0'); + expectTemplate('num: {{.}}') .withInput(0) .toCompileTo('num: 0'); + expectTemplate('num: {{num1/num2}}') .withInput({ num1: { num2: 0 } }) .toCompileTo('num: 0'); }); + it('false', function() { /* eslint-disable no-new-wrappers */ expectTemplate('val1: {{val1}}, val2: {{val2}}') @@ -119,9 +131,11 @@ describe('basic context', function() { val2: new Boolean(false) }) .toCompileTo('val1: false, val2: false'); + expectTemplate('val: {{.}}') .withInput(false) .toCompileTo('val: false'); + expectTemplate('val: {{val1/val2}}') .withInput({ val1: { val2: false } }) .toCompileTo('val: false'); @@ -132,6 +146,7 @@ describe('basic context', function() { val2: new Boolean(false) }) .toCompileTo('val1: false, val2: false'); + expectTemplate('val: {{{val1/val2}}}') .withInput({ val1: { val2: false } }) .toCompileTo('val: false'); @@ -152,6 +167,7 @@ describe('basic context', function() { } }) .toCompileTo('true true object'); + expectTemplate('{{undefined}}') .withInput({ undefined: function() { @@ -159,6 +175,7 @@ describe('basic context', function() { } }) .toCompileTo('undefined!'); + expectTemplate('{{null}}') .withInput({ null: function() { @@ -170,6 +187,7 @@ describe('basic context', function() { it('newlines', function() { expectTemplate("Alan's\nTest").toCompileTo("Alan's\nTest"); + expectTemplate("Alan's\rTest").toCompileTo("Alan's\rTest"); }); @@ -179,16 +197,20 @@ describe('basic context', function() { "text is escaped so that it doesn't get caught on single quotes" ) .toCompileTo("Awesome's"); + expectTemplate('Awesome\\') .withMessage("text is escaped so that the closing quote can't be ignored") .toCompileTo('Awesome\\'); + expectTemplate('Awesome\\\\ foo') .withMessage("text is escaped so that it doesn't mess up backslashes") .toCompileTo('Awesome\\\\ foo'); + expectTemplate('Awesome {{foo}}') .withInput({ foo: '\\' }) .withMessage("text is escaped so that it doesn't mess up backslashes") .toCompileTo('Awesome \\'); + expectTemplate(" ' ' ") .withMessage('double quotes never produce invalid javascript') .toCompileTo(" ' ' "); @@ -217,13 +239,12 @@ describe('basic context', function() { }); it("functions returning safestrings shouldn't be escaped", function() { - var hash = { - awesome: function() { - return new Handlebars.SafeString("&'\\<>"); - } - }; expectTemplate('{{awesome}}') - .withInput(hash) + .withInput({ + awesome: function() { + return new Handlebars.SafeString("&'\\<>"); + } + }) .withMessage("functions returning safestrings aren't escaped") .toCompileTo("&'\\<>"); }); @@ -237,6 +258,7 @@ describe('basic context', function() { }) .withMessage('functions are called and render their output') .toCompileTo('Awesome'); + expectTemplate('{{awesome}}') .withInput({ awesome: function() { @@ -259,6 +281,7 @@ describe('basic context', function() { .withMessage('functions are called with context arguments') .toCompileTo('Frank'); }); + it('pathed functions with context argument', function() { expectTemplate('{{bar.awesome frank}}') .withInput({ @@ -272,6 +295,7 @@ describe('basic context', function() { .withMessage('functions are called with context arguments') .toCompileTo('Frank'); }); + it('depthed functions with context argument', function() { expectTemplate('{{#with frank}}{{../awesome .}}{{/with}}') .withInput({ @@ -319,6 +343,7 @@ describe('basic context', function() { .withMessage('block functions are called with options') .toCompileTo('inner'); }); + it('pathed block functions without context argument', function() { expectTemplate('{{#foo.awesome}}inner{{/foo.awesome}}') .withInput({ @@ -331,6 +356,7 @@ describe('basic context', function() { .withMessage('block functions are called with options') .toCompileTo('inner'); }); + it('depthed block functions without context argument', function() { expectTemplate( '{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}' @@ -350,10 +376,12 @@ describe('basic context', function() { .withInput({ 'foo-bar': 'baz' }) .withMessage('Paths can contain hyphens (-)') .toCompileTo('baz'); + expectTemplate('{{foo.foo-bar}}') .withInput({ foo: { 'foo-bar': 'baz' } }) .withMessage('Paths can contain hyphens (-)') .toCompileTo('baz'); + expectTemplate('{{foo/foo-bar}}') .withInput({ foo: { 'foo-bar': 'baz' } }) .withMessage('Paths can contain hyphens (-)') @@ -379,6 +407,7 @@ describe('basic context', function() { .withInput({ '@alan': { expression: 'beautiful' } }) .withMessage('Literal paths can be used') .toCompileTo('Goodbye beautiful world!'); + expectTemplate('Goodbye {{[foo bar]/expression}} world!') .withInput({ 'foo bar': { expression: 'beautiful' } }) .withMessage('Literal paths can be used') @@ -389,18 +418,23 @@ describe('basic context', function() { expectTemplate('Goodbye {{[foo bar]}} world!') .withInput({ 'foo bar': 'beautiful' }) .toCompileTo('Goodbye beautiful world!'); + expectTemplate('Goodbye {{"foo bar"}} world!') .withInput({ 'foo bar': 'beautiful' }) .toCompileTo('Goodbye beautiful world!'); + expectTemplate("Goodbye {{'foo bar'}} world!") .withInput({ 'foo bar': 'beautiful' }) .toCompileTo('Goodbye beautiful world!'); + expectTemplate('Goodbye {{"foo[bar"}} world!') .withInput({ 'foo[bar': 'beautiful' }) .toCompileTo('Goodbye beautiful world!'); + expectTemplate('Goodbye {{"foo\'bar"}} world!') .withInput({ "foo'bar": 'beautiful' }) .toCompileTo('Goodbye beautiful world!'); + expectTemplate("Goodbye {{'foo\"bar'}} world!") .withInput({ 'foo"bar': 'beautiful' }) .toCompileTo('Goodbye beautiful world!'); @@ -417,25 +451,22 @@ describe('basic context', function() { expectTemplate('{{person/name}}') .withInput({ person: { name: null } }) .toCompileTo(''); + expectTemplate('{{person/name}}') .withInput({ person: {} }) .toCompileTo(''); }); it('this keyword in paths', function() { - var string = '{{#goodbyes}}{{this}}{{/goodbyes}}'; - var hash = { goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}') + .withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] }) .withMessage('This keyword in paths evaluates to current context') .toCompileTo('goodbyeGoodbyeGOODBYE'); - string = '{{#hellos}}{{this/text}}{{/hellos}}'; - hash = { - hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }] - }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{#hellos}}{{this/text}}{{/hellos}}') + .withInput({ + hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }] + }) .withMessage('This keyword evaluates in more complex paths') .toCompileTo('helloHelloHELLO'); }); @@ -449,6 +480,7 @@ describe('basic context', function() { expectTemplate('{{[this]}}') .withInput({ this: 'bar' }) .toCompileTo('bar'); + expectTemplate('{{text/[this]}}') .withInput({ text: { this: 'bar' } }) .toCompileTo('bar'); @@ -460,28 +492,27 @@ describe('basic context', function() { return 'bar ' + value; } }; - var string = '{{#goodbyes}}{{foo this}}{{/goodbyes}}'; - var hash = { goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] }; - expectTemplate(string) - .withInput(hash) + + expectTemplate('{{#goodbyes}}{{foo this}}{{/goodbyes}}') + .withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] }) .withHelpers(helpers) .withMessage('This keyword in paths evaluates to current context') .toCompileTo('bar goodbyebar Goodbyebar GOODBYE'); - string = '{{#hellos}}{{foo this/text}}{{/hellos}}'; - hash = { - hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }] - }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{#hellos}}{{foo this/text}}{{/hellos}}') + .withInput({ + hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }] + }) .withHelpers(helpers) .withMessage('This keyword evaluates in more complex paths') .toCompileTo('bar hellobar Hellobar HELLO'); }); it('this keyword nested inside helpers param', function() { - var string = '{{#hellos}}{{foo text/this/foo}}{{/hellos}}'; - expectTemplate(string).toThrow(Error, 'Invalid path: text/this - 1:17'); + expectTemplate('{{#hellos}}{{foo text/this/foo}}{{/hellos}}').toThrow( + Error, + 'Invalid path: text/this - 1:17' + ); expectTemplate('{{foo [this]}}') .withInput({ @@ -491,6 +522,7 @@ describe('basic context', function() { this: 'bar' }) .toCompileTo('bar'); + expectTemplate('{{foo text/[this]}}') .withInput({ foo: function(value) { @@ -503,9 +535,11 @@ describe('basic context', function() { it('pass string literals', function() { expectTemplate('{{"foo"}}').toCompileTo(''); + expectTemplate('{{"foo"}}') .withInput({ foo: 'bar' }) .toCompileTo('bar'); + expectTemplate('{{#"foo"}}{{.}}{{/"foo"}}') .withInput({ foo: ['bar', 'baz'] @@ -515,13 +549,17 @@ describe('basic context', function() { it('pass number literals', function() { expectTemplate('{{12}}').toCompileTo(''); + expectTemplate('{{12}}') .withInput({ '12': 'bar' }) .toCompileTo('bar'); + expectTemplate('{{12.34}}').toCompileTo(''); + expectTemplate('{{12.34}}') .withInput({ '12.34': 'bar' }) .toCompileTo('bar'); + expectTemplate('{{12.34 1}}') .withInput({ '12.34': function(arg) { @@ -533,27 +571,26 @@ describe('basic context', function() { it('pass boolean literals', function() { expectTemplate('{{true}}').toCompileTo(''); + expectTemplate('{{true}}') .withInput({ '': 'foo' }) .toCompileTo(''); + expectTemplate('{{false}}') .withInput({ false: 'foo' }) .toCompileTo('foo'); }); it('should handle literals in subexpression', function() { - var helpers = { - foo: function(arg) { - return arg; - } - }; expectTemplate('{{foo (false)}}') .withInput({ false: function() { return 'bar'; } }) - .withHelpers(helpers) + .withHelper('foo', function(arg) { + return arg; + }) .toCompileTo('bar'); }); }); diff --git a/spec/blocks.js b/spec/blocks.js index ab16fd9d3..f15655428 100644 --- a/spec/blocks.js +++ b/spec/blocks.js @@ -1,12 +1,16 @@ describe('blocks', function() { it('array', function() { var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!'; - var hash = { - goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], - world: 'world' - }; + expectTemplate(string) - .withInput(hash) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage('Arrays iterate over the contents when not empty') .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); @@ -20,40 +24,49 @@ describe('blocks', function() { }); it('array without data', function() { - var string = - '{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}'; - var hash = { - goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], - world: 'world' - }; - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}' + ) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withCompileOptions({ compat: false }) .toCompileTo('goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE'); }); it('array with @index', function() { - var string = - '{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!'; - var hash = { - goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!' + ) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage('The @index variable is used') .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!'); }); it('empty block', function() { var string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!'; - var hash = { - goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], - world: 'world' - }; + expectTemplate(string) - .withInput(hash) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage('Arrays iterate over the contents when not empty') .toCompileTo('cruel world!'); @@ -67,14 +80,15 @@ describe('blocks', function() { }); it('block with complex lookup', function() { - var string = '{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}'; - var hash = { - name: 'Alan', - goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }] - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate('{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}') + .withInput({ + name: 'Alan', + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ] + }) .withMessage( 'Templates can access variables in contexts up the stack with relative path syntax' ) @@ -84,80 +98,72 @@ describe('blocks', function() { }); it('multiple blocks with complex lookup', function() { - var string = '{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}'; - var hash = { - name: 'Alan', - goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }] - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate('{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}') + .withInput({ + name: 'Alan', + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ] + }) .toCompileTo('AlanAlanAlanAlanAlanAlan'); }); it('block with complex lookup using nested context', function() { - var string = '{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}'; - - expectTemplate(string).toThrow(Error); + expectTemplate( + '{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}' + ).toThrow(Error); }); it('block with deep nested complex lookup', function() { - var string = - '{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}'; - var hash = { - omg: 'OMG!', - outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }] - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}' + ) + .withInput({ + omg: 'OMG!', + outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }] + }) .toCompileTo('Goodbye cruel sad OMG!'); }); it('works with cached blocks', function() { - var string = - '{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}'; - var compileOptions = { data: false }; - - var input = { - person: [ - { first: 'Alan', last: 'Johnson' }, - { first: 'Alan', last: 'Johnson' } - ] - }; - expectTemplate(string) - .withCompileOptions(compileOptions) - .withInput(input) + expectTemplate( + '{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}' + ) + .withCompileOptions({ data: false }) + .withInput({ + person: [ + { first: 'Alan', last: 'Johnson' }, + { first: 'Alan', last: 'Johnson' } + ] + }) .toCompileTo('Alan JohnsonAlan Johnson'); }); describe('inverted sections', function() { it('inverted sections with unset value', function() { - var string = - '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; - var hash = {}; - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}' + ) .withMessage("Inverted section rendered when value isn't set.") .toCompileTo('Right On!'); }); it('inverted section with false value', function() { - var string = - '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; - var hash = { goodbyes: false }; - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}' + ) + .withInput({ goodbyes: false }) .withMessage('Inverted section rendered when value is false.') .toCompileTo('Right On!'); }); it('inverted section with empty set', function() { - var string = - '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; - var hash = { goodbyes: [] }; - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}' + ) + .withInput({ goodbyes: [] }) .withMessage('Inverted section rendered when value is empty set.') .toCompileTo('Right On!'); }); @@ -167,21 +173,25 @@ describe('blocks', function() { .withInput({ none: 'No people' }) .toCompileTo('No people'); }); + it('chained inverted sections', function() { expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{/people}}') .withInput({ none: 'No people' }) .toCompileTo('No people'); + expectTemplate( '{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}' ) .withInput({ none: 'No people' }) .toCompileTo('No people'); + expectTemplate( '{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}' ) .withInput({ none: 'No people' }) .toCompileTo('No people'); }); + it('chained inverted sections with mismatch', function() { expectTemplate( '{{#people}}{{name}}{{else if none}}{{none}}{{/if}}' @@ -203,35 +213,42 @@ describe('blocks', function() { expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n') .withInput({ none: 'No people' }) .toCompileTo('No people\n'); + expectTemplate('{{#none}}\n{{.}}\n{{^}}\n{{none}}\n{{/none}}\n') .withInput({ none: 'No people' }) .toCompileTo('No people\n'); + expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n') .withInput({ none: 'No people' }) .toCompileTo('No people\n'); }); + it('block standalone else sections can be disabled', function() { expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n') .withInput({ none: 'No people' }) .withCompileOptions({ ignoreStandalone: true }) .toCompileTo('\nNo people\n\n'); + expectTemplate('{{#none}}\n{{.}}\n{{^}}\nFail\n{{/none}}\n') .withInput({ none: 'No people' }) .withCompileOptions({ ignoreStandalone: true }) .toCompileTo('\nNo people\n\n'); }); + it('block standalone chained else sections', function() { expectTemplate( '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n' ) .withInput({ none: 'No people' }) .toCompileTo('No people\n'); + expectTemplate( '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n' ) .withInput({ none: 'No people' }) .toCompileTo('No people\n'); }); + it('should handle nesting', function() { expectTemplate('{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.') .withInput({ @@ -243,39 +260,34 @@ describe('blocks', function() { describe('compat mode', function() { it('block with deep recursive lookup lookup', function() { - var string = - '{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}'; - var hash = { omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}' + ) + .withInput({ omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] }) .withCompileOptions({ compat: true }) .toCompileTo('Goodbye cruel OMG!'); }); it('block with deep recursive pathed lookup', function() { - var string = - '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'; - var hash = { - omg: { yes: 'OMG!' }, - outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}' + ) + .withInput({ + omg: { yes: 'OMG!' }, + outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] + }) .withCompileOptions({ compat: true }) .toCompileTo('Goodbye cruel OMG!'); }); + it('block with missed recursive lookup', function() { - var string = - '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'; - var hash = { - omg: { no: 'OMG!' }, - outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}' + ) + .withInput({ + omg: { no: 'OMG!' }, + outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] + }) .withCompileOptions({ compat: true }) .toCompileTo('Goodbye cruel '); }); @@ -283,145 +295,109 @@ describe('blocks', function() { describe('decorators', function() { it('should apply mustache decorators', function() { - var helpers = { - helper: function(options) { + expectTemplate('{{#helper}}{{*decorator}}{{/helper}}') + .withHelper('helper', function(options) { return options.fn.run; - } - }; - var decorators = { - decorator: function(fn) { + }) + .withDecorator('decorator', function(fn) { fn.run = 'success'; return fn; - } - }; - expectTemplate('{{#helper}}{{*decorator}}{{/helper}}') - .withHelpers(helpers) - .withDecorators(decorators) + }) .toCompileTo('success'); }); + it('should apply allow undefined return', function() { - var helpers = { - helper: function(options) { + expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}') + .withHelper('helper', function(options) { return options.fn() + options.fn.run; - } - }; - var decorators = { - decorator: function(fn) { + }) + .withDecorator('decorator', function(fn) { fn.run = 'cess'; - } - }; - expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}') - .withHelpers(helpers) - .withDecorators(decorators) + }) .toCompileTo('success'); }); it('should apply block decorators', function() { - var helpers = { - helper: function(options) { - return options.fn.run; - } - }; - var decorators = { - decorator: function(fn, props, container, options) { - fn.run = options.fn(); - return fn; - } - }; expectTemplate( '{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}' ) - .withHelpers(helpers) - .withDecorators(decorators) + .withHelper('helper', function(options) { + return options.fn.run; + }) + .withDecorator('decorator', function(fn, props, container, options) { + fn.run = options.fn(); + return fn; + }) .toCompileTo('success'); }); + it('should support nested decorators', function() { - var helpers = { - helper: function(options) { - return options.fn.run; - } - }; - var decorators = { - decorator: function(fn, props, container, options) { - fn.run = options.fn.nested + options.fn(); - return fn; - }, - nested: function(fn, props, container, options) { - props.nested = options.fn(); - } - }; expectTemplate( '{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}' ) - .withHelpers(helpers) - .withDecorators(decorators) + .withHelper('helper', function(options) { + return options.fn.run; + }) + .withDecorators({ + decorator: function(fn, props, container, options) { + fn.run = options.fn.nested + options.fn(); + return fn; + }, + nested: function(fn, props, container, options) { + props.nested = options.fn(); + } + }) .toCompileTo('success'); }); it('should apply multiple decorators', function() { - var helpers = { - helper: function(options) { - return options.fn.run; - } - }; - var decorators = { - decorator: function(fn, props, container, options) { - fn.run = (fn.run || '') + options.fn(); - return fn; - } - }; expectTemplate( '{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}' ) - .withHelpers(helpers) - .withDecorators(decorators) + .withHelper('helper', function(options) { + return options.fn.run; + }) + .withDecorator('decorator', function(fn, props, container, options) { + fn.run = (fn.run || '') + options.fn(); + return fn; + }) .toCompileTo('success'); }); it('should access parent variables', function() { - var helpers = { - helper: function(options) { + expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}') + .withHelper('helper', function(options) { return options.fn.run; - } - }; - var decorators = { - decorator: function(fn, props, container, options) { + }) + .withDecorator('decorator', function(fn, props, container, options) { fn.run = options.args; return fn; - } - }; - expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}') - .withHelpers(helpers) - .withDecorators(decorators) + }) .withInput({ foo: 'success' }) .toCompileTo('success'); }); + it('should work with root program', function() { var run; - var decorators = { - decorator: function(fn, props, container, options) { + expectTemplate('{{*decorator "success"}}') + .withDecorator('decorator', function(fn, props, container, options) { equals(options.args[0], 'success'); run = true; return fn; - } - }; - expectTemplate('{{*decorator "success"}}') - .withDecorators(decorators) + }) .withInput({ foo: 'success' }) .toCompileTo(''); equals(run, true); }); + it('should fail when accessing variables from root', function() { var run; - var decorators = { - decorator: function(fn, props, container, options) { + expectTemplate('{{*decorator foo}}') + .withDecorator('decorator', function(fn, props, container, options) { equals(options.args[0], undefined); run = true; return fn; - } - }; - expectTemplate('{{*decorator foo}}') - .withDecorators(decorators) + }) .withInput({ foo: 'fail' }) .toCompileTo(''); equals(run, true); @@ -455,6 +431,7 @@ describe('blocks', function() { equals(handlebarsEnv.decorators.foo, undefined); equals(handlebarsEnv.decorators.bar, undefined); }); + it('fails with multiple and args', function() { shouldThrow( function() { diff --git a/spec/builtins.js b/spec/builtins.js index b54c5ca3a..bd44d8b74 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -2,6 +2,7 @@ describe('builtin helpers', function() { describe('#if', function() { it('if', function() { var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; + expectTemplate(string) .withInput({ goodbye: true, @@ -9,6 +10,7 @@ describe('builtin helpers', function() { }) .withMessage('if with boolean argument shows the contents when true') .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) .withInput({ goodbye: 'dummy', @@ -16,6 +18,7 @@ describe('builtin helpers', function() { }) .withMessage('if with string argument shows the contents') .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) .withInput({ goodbye: false, @@ -25,10 +28,12 @@ describe('builtin helpers', function() { 'if with boolean argument does not show the contents when false' ) .toCompileTo('cruel world!'); + expectTemplate(string) .withInput({ world: 'world' }) .withMessage('if with undefined does not show the contents') .toCompileTo('cruel world!'); + expectTemplate(string) .withInput({ goodbye: ['foo'], @@ -36,6 +41,7 @@ describe('builtin helpers', function() { }) .withMessage('if with non-empty array shows the contents') .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) .withInput({ goodbye: [], @@ -43,6 +49,7 @@ describe('builtin helpers', function() { }) .withMessage('if with empty array does not show the contents') .toCompileTo('cruel world!'); + expectTemplate(string) .withInput({ goodbye: 0, @@ -50,6 +57,7 @@ describe('builtin helpers', function() { }) .withMessage('if with zero does not show the contents') .toCompileTo('cruel world!'); + expectTemplate( '{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!' ) @@ -63,6 +71,7 @@ describe('builtin helpers', function() { it('if with function argument', function() { var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; + expectTemplate(string) .withInput({ goodbye: function() { @@ -74,6 +83,7 @@ describe('builtin helpers', function() { 'if with function shows the contents when function returns true' ) .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) .withInput({ goodbye: function() { @@ -85,6 +95,7 @@ describe('builtin helpers', function() { 'if with function shows the contents when function returns string' ) .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) .withInput({ goodbye: function() { @@ -96,6 +107,7 @@ describe('builtin helpers', function() { 'if with function does not show the contents when returns false' ) .toCompileTo('cruel world!'); + expectTemplate(string) .withInput({ goodbye: function() { @@ -110,9 +122,9 @@ describe('builtin helpers', function() { }); it('should not change the depth list', function() { - var string = - '{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}'; - expectTemplate(string) + expectTemplate( + '{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}' + ) .withInput({ foo: { goodbye: true }, world: 'world' @@ -123,8 +135,7 @@ describe('builtin helpers', function() { describe('#with', function() { it('with', function() { - var string = '{{#with person}}{{first}} {{last}}{{/with}}'; - expectTemplate(string) + expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}') .withInput({ person: { first: 'Alan', @@ -133,9 +144,9 @@ describe('builtin helpers', function() { }) .toCompileTo('Alan Johnson'); }); + it('with with function argument', function() { - var string = '{{#with person}}{{first}} {{last}}{{/with}}'; - expectTemplate(string) + expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}') .withInput({ person: function() { return { @@ -146,14 +157,15 @@ describe('builtin helpers', function() { }) .toCompileTo('Alan Johnson'); }); + it('with with else', function() { - var string = - '{{#with person}}Person is present{{else}}Person is not present{{/with}}'; - expectTemplate(string).toCompileTo('Person is not present'); + expectTemplate( + '{{#with person}}Person is present{{else}}Person is not present{{/with}}' + ).toCompileTo('Person is not present'); }); + it('with provides block parameter', function() { - var string = '{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}'; - expectTemplate(string) + expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}') .withInput({ person: { first: 'Alan', @@ -162,6 +174,7 @@ describe('builtin helpers', function() { }) .toCompileTo('Alan Johnson'); }); + it('works when data is disabled', function() { expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}') .withInput({ person: { first: 'Alan', last: 'Johnson' } }) @@ -179,20 +192,21 @@ describe('builtin helpers', function() { it('each', function() { var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - var hash = { - goodbyes: [ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ], - world: 'world' - }; + expectTemplate(string) - .withInput(hash) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage( 'each with array argument iterates over the contents when not empty' ) .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); + expectTemplate(string) .withInput({ goodbyes: [], @@ -203,32 +217,28 @@ describe('builtin helpers', function() { }); it('each without data', function() { - var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - var hash = { - goodbyes: [ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ], - world: 'world' - }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!') + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withRuntimeOptions({ data: false }) .withCompileOptions({ data: false }) .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); - hash = { goodbyes: 'cruel', world: 'world' }; expectTemplate('{{#each .}}{{.}}{{/each}}') - .withInput(hash) + .withInput({ goodbyes: 'cruel', world: 'world' }) .withRuntimeOptions({ data: false }) .withCompileOptions({ data: false }) .toCompileTo('cruelworld'); }); it('each without context', function() { - var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - expectTemplate(string) + expectTemplate('{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!') .withInput(undefined) .toCompileTo('cruel !'); }); @@ -258,6 +268,7 @@ describe('builtin helpers', function() { true, 'each with object argument iterates over the contents when not empty' ); + expectTemplate(string) .withInput({ goodbyes: {}, @@ -267,37 +278,33 @@ describe('builtin helpers', function() { }); it('each with @index', function() { - var string = - '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'; - var hash = { - goodbyes: [ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ], - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!' + ) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage('The @index variable is used') .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!'); }); it('each with nested @index', function() { - var string = - '{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!'; - var hash = { - goodbyes: [ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ], - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!' + ) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage('The @index variable is used') .toCompileTo( '0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!' @@ -305,70 +312,62 @@ describe('builtin helpers', function() { }); it('each with block params', function() { - var string = - '{{#each goodbyes as |value index|}}{{index}}. {{value.text}}! {{#each ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/each}} After {{index}} {{/each}}{{index}}cruel {{world}}!'; - var hash = { - goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }], - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes as |value index|}}{{index}}. {{value.text}}! {{#each ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/each}} After {{index}} {{/each}}{{index}}cruel {{world}}!' + ) + .withInput({ + goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }], + world: 'world' + }) .toCompileTo( '0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!' ); }); it('each object with @index', function() { - var string = - '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'; - var hash = { - goodbyes: { - a: { text: 'goodbye' }, - b: { text: 'Goodbye' }, - c: { text: 'GOODBYE' } - }, - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!' + ) + .withInput({ + goodbyes: { + a: { text: 'goodbye' }, + b: { text: 'Goodbye' }, + c: { text: 'GOODBYE' } + }, + world: 'world' + }) .withMessage('The @index variable is used') .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!'); }); it('each with @first', function() { - var string = - '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; - var hash = { - goodbyes: [ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ], - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!' + ) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage('The @first variable is used') .toCompileTo('goodbye! cruel world!'); }); it('each with nested @first', function() { - var string = - '{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'; - var hash = { - goodbyes: [ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ], - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!' + ) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage('The @first variable is used') .toCompileTo( '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!' @@ -376,65 +375,57 @@ describe('builtin helpers', function() { }); it('each object with @first', function() { - var string = - '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; - var hash = { - goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } }, - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!' + ) + .withInput({ + goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } }, + world: 'world' + }) .withMessage('The @first variable is used') .toCompileTo('goodbye! cruel world!'); }); it('each with @last', function() { - var string = - '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; - var hash = { - goodbyes: [ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ], - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!' + ) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage('The @last variable is used') .toCompileTo('GOODBYE! cruel world!'); }); it('each object with @last', function() { - var string = - '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'; - var hash = { - goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } }, - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!' + ) + .withInput({ + goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } }, + world: 'world' + }) .withMessage('The @last variable is used') .toCompileTo('Goodbye! cruel world!'); }); it('each with nested @last', function() { - var string = - '{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'; - var hash = { - goodbyes: [ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ], - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!' + ) + .withInput({ + goodbyes: [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ], + world: 'world' + }) .withMessage('The @last variable is used') .toCompileTo( '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!' @@ -443,22 +434,23 @@ describe('builtin helpers', function() { it('each with function argument', function() { var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - var hash = { - goodbyes: function() { - return [ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ]; - }, - world: 'world' - }; + expectTemplate(string) - .withInput(hash) + .withInput({ + goodbyes: function() { + return [ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ]; + }, + world: 'world' + }) .withMessage( 'each with array function argument iterates over the contents when not empty' ) .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); + expectTemplate(string) .withInput({ goodbyes: [], @@ -471,29 +463,26 @@ describe('builtin helpers', function() { }); it('each object when last key is an empty string', function() { - var string = - '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'; - var hash = { - goodbyes: { - a: { text: 'goodbye' }, - b: { text: 'Goodbye' }, - '': { text: 'GOODBYE' } - }, - world: 'world' - }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!' + ) + .withInput({ + goodbyes: { + a: { text: 'goodbye' }, + b: { text: 'Goodbye' }, + '': { text: 'GOODBYE' } + }, + world: 'world' + }) .withMessage('Empty string key is not skipped') .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!'); }); it('data passed to helpers', function() { - var string = '{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}'; - var hash = { letters: ['a', 'b', 'c'] }; - - expectTemplate(string) - .withInput(hash) + expectTemplate( + '{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}' + ) + .withInput({ letters: ['a', 'b', 'c'] }) .withMessage('should output data') .withRuntimeOptions({ data: { @@ -504,8 +493,7 @@ describe('builtin helpers', function() { }); it('each on implicit context', function() { - var string = '{{#each}}{{text}}! {{/each}}cruel world!'; - expectTemplate(string).toThrow( + expectTemplate('{{#each}}{{text}}! {{/each}}cruel world!').toThrow( handlebarsEnv.Exception, 'Must pass iterator to #each' ); @@ -539,12 +527,14 @@ describe('builtin helpers', function() { ]); var goodbyesEmpty = new Iterable([]); var hash = { goodbyes: goodbyes, world: 'world' }; + expectTemplate(string) .withInput(hash) .withMessage( 'each with array argument iterates over the contents when not empty' ) .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); + expectTemplate(string) .withInput({ goodbyes: goodbyesEmpty, @@ -577,43 +567,37 @@ describe('builtin helpers', function() { }); it('should call logger at default level', function() { - var string = '{{log blah}}'; - var hash = { blah: 'whee' }; - var levelArg, logArg; handlebarsEnv.log = function(level, arg) { levelArg = level; logArg = arg; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah}}') + .withInput({ blah: 'whee' }) .withMessage('log should not display') .toCompileTo(''); equals(1, levelArg, 'should call log with 1'); equals('whee', logArg, "should call log with 'whee'"); }); - it('should call logger at data level', function() { - var string = '{{log blah}}'; - var hash = { blah: 'whee' }; + it('should call logger at data level', function() { var levelArg, logArg; handlebarsEnv.log = function(level, arg) { levelArg = level; logArg = arg; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah}}') + .withInput({ blah: 'whee' }) .withRuntimeOptions({ data: { level: '03' } }) .withCompileOptions({ data: true }) .toCompileTo(''); equals('03', levelArg); equals('whee', logArg); }); + it('should output to info', function() { - var string = '{{log blah}}'; - var hash = { blah: 'whee' }; var called; console.info = function(info) { @@ -629,14 +613,13 @@ describe('builtin helpers', function() { console.log = $log; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah}}') + .withInput({ blah: 'whee' }) .toCompileTo(''); equals(true, called); }); + it('should log at data level', function() { - var string = '{{log blah}}'; - var hash = { blah: 'whee' }; var called; console.error = function(log) { @@ -645,17 +628,16 @@ describe('builtin helpers', function() { console.error = $error; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah}}') + .withInput({ blah: 'whee' }) .withRuntimeOptions({ data: { level: '03' } }) .withCompileOptions({ data: true }) .toCompileTo(''); equals(true, called); }); + it('should handle missing logger', function() { - var string = '{{log blah}}'; - var hash = { blah: 'whee' }, - called = false; + var called = false; console.error = undefined; console.log = function(log) { @@ -664,8 +646,8 @@ describe('builtin helpers', function() { console.log = $log; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah}}') + .withInput({ blah: 'whee' }) .withRuntimeOptions({ data: { level: '03' } }) .withCompileOptions({ data: true }) .toCompileTo(''); @@ -698,6 +680,7 @@ describe('builtin helpers', function() { .toCompileTo(''); equals(true, called); }); + it('should handle hash log levels', function() { var string = '{{log blah level="error"}}'; var hash = { blah: 'whee' }; @@ -713,9 +696,8 @@ describe('builtin helpers', function() { .toCompileTo(''); equals(true, called); }); + it('should handle hash log levels', function() { - var string = '{{log blah level="debug"}}'; - var hash = { blah: 'whee' }; var called = false; console.info = console.log = console.error = console.debug = function() { @@ -723,14 +705,13 @@ describe('builtin helpers', function() { console.info = console.log = console.error = console.debug = $log; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah level="debug"}}') + .withInput({ blah: 'whee' }) .toCompileTo(''); equals(false, called); }); + it('should pass multiple log arguments', function() { - var string = '{{log blah "foo" 1}}'; - var hash = { blah: 'whee' }; var called; console.info = console.log = function(log1, log2, log3) { @@ -741,15 +722,13 @@ describe('builtin helpers', function() { console.log = $log; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah "foo" 1}}') + .withInput({ blah: 'whee' }) .toCompileTo(''); equals(true, called); }); it('should pass zero log arguments', function() { - var string = '{{log}}'; - var hash = { blah: 'whee' }; var called; console.info = console.log = function() { @@ -758,8 +737,8 @@ describe('builtin helpers', function() { console.log = $log; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log}}') + .withInput({ blah: 'whee' }) .toCompileTo(''); expect(called).to.be.true(); }); @@ -768,19 +747,14 @@ describe('builtin helpers', function() { describe('#lookup', function() { it('should lookup arbitrary content', function() { - var string = '{{#each goodbyes}}{{lookup ../data .}}{{/each}}', - hash = { goodbyes: [0, 1], data: ['foo', 'bar'] }; - - expectTemplate(string) - .withInput(hash) + expectTemplate('{{#each goodbyes}}{{lookup ../data .}}{{/each}}') + .withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] }) .toCompileTo('foobar'); }); - it('should not fail on undefined value', function() { - var string = '{{#each goodbyes}}{{lookup ../bar .}}{{/each}}', - hash = { goodbyes: [0, 1], data: ['foo', 'bar'] }; - expectTemplate(string) - .withInput(hash) + it('should not fail on undefined value', function() { + expectTemplate('{{#each goodbyes}}{{lookup ../bar .}}{{/each}}') + .withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] }) .toCompileTo(''); }); }); diff --git a/spec/data.js b/spec/data.js index 5589eb0b0..8a126d2b2 100644 --- a/spec/data.js +++ b/spec/data.js @@ -1,16 +1,10 @@ describe('data', function() { it('passing in data to a compiled function that expects data - works with helpers', function() { - var string = '{{hello}}'; - - var helpers = { - hello: function(options) { - return options.data.adjective + ' ' + this.noun; - } - }; - - expectTemplate(string) + expectTemplate('{{hello}}') .withCompileOptions({ data: true }) - .withHelpers(helpers) + .withHelper('hello', function(options) { + return options.data.adjective + ' ' + this.noun; + }) .withRuntimeOptions({ data: { adjective: 'happy' } }) .withInput({ noun: 'cat' }) .withMessage('Data output by helper') @@ -25,9 +19,6 @@ describe('data', function() { }); it('deep @foo triggers automatic top-level data', function() { - var string = - '{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}'; - var helpers = Handlebars.createFrame(handlebarsEnv.helpers); helpers.let = function(options) { @@ -41,7 +32,9 @@ describe('data', function() { return options.fn(this, { data: frame }); }; - expectTemplate(string) + expectTemplate( + '{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}' + ) .withInput({ foo: true }) .withHelpers(helpers) .withMessage('Automatic data was triggered') @@ -49,107 +42,82 @@ describe('data', function() { }); it('parameter data can be looked up via @foo', function() { - var string = '{{hello @world}}'; - var helpers = { - hello: function(noun) { - return 'Hello ' + noun; - } - }; - - expectTemplate(string) + expectTemplate('{{hello @world}}') .withRuntimeOptions({ data: { world: 'world' } }) - .withHelpers(helpers) + .withHelper('hello', function(noun) { + return 'Hello ' + noun; + }) .withMessage('@foo as a parameter retrieves template data') .toCompileTo('Hello world'); }); it('hash values can be looked up via @foo', function() { - var string = '{{hello noun=@world}}'; - var helpers = { - hello: function(options) { - return 'Hello ' + options.hash.noun; - } - }; - - expectTemplate(string) + expectTemplate('{{hello noun=@world}}') .withRuntimeOptions({ data: { world: 'world' } }) - .withHelpers(helpers) + .withHelper('hello', function(options) { + return 'Hello ' + options.hash.noun; + }) .withMessage('@foo as a parameter retrieves template data') .toCompileTo('Hello world'); }); it('nested parameter data can be looked up via @foo.bar', function() { - var string = '{{hello @world.bar}}'; - var helpers = { - hello: function(noun) { - return 'Hello ' + noun; - } - }; - - expectTemplate(string) + expectTemplate('{{hello @world.bar}}') .withRuntimeOptions({ data: { world: { bar: 'world' } } }) - .withHelpers(helpers) + .withHelper('hello', function(noun) { + return 'Hello ' + noun; + }) .withMessage('@foo as a parameter retrieves template data') .toCompileTo('Hello world'); }); it('nested parameter data does not fail with @world.bar', function() { - var string = '{{hello @world.bar}}'; - var helpers = { - hello: function(noun) { - return 'Hello ' + noun; - } - }; - - expectTemplate(string) + expectTemplate('{{hello @world.bar}}') .withRuntimeOptions({ data: { foo: { bar: 'world' } } }) - .withHelpers(helpers) + .withHelper('hello', function(noun) { + return 'Hello ' + noun; + }) .withMessage('@foo as a parameter retrieves template data') .toCompileTo('Hello undefined'); }); it('parameter data throws when using complex scope references', function() { - var string = '{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}'; - - expectTemplate(string).toThrow(Error); + expectTemplate( + '{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}' + ).toThrow(Error); }); it('data can be functions', function() { - var string = '{{@hello}}'; - var runtimeOptions = { - data: { - hello: function() { - return 'hello'; + expectTemplate('{{@hello}}') + .withRuntimeOptions({ + data: { + hello: function() { + return 'hello'; + } } - } - }; - - expectTemplate(string) - .withRuntimeOptions(runtimeOptions) + }) .toCompileTo('hello'); }); it('data can be functions with params', function() { - var string = '{{@hello "hello"}}'; - var runtimeOptions = { - data: { - hello: function(arg) { - return arg; + expectTemplate('{{@hello "hello"}}') + .withRuntimeOptions({ + data: { + hello: function(arg) { + return arg; + } } - } - }; - - expectTemplate(string) - .withRuntimeOptions(runtimeOptions) + }) .toCompileTo('hello'); }); it('data is inherited downstream', function() { - var string = - '{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}'; - var compileOptions = { data: true }; - var helpers = { - let: function(options) { + expectTemplate( + '{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}' + ) + .withInput({ bar: { baz: 'hello world' } }) + .withCompileOptions({ data: true }) + .withHelper('let', function(options) { var frame = Handlebars.createFrame(options.data); for (var prop in options.hash) { if (prop in options.hash) { @@ -157,193 +125,117 @@ describe('data', function() { } } return options.fn(this, { data: frame }); - } - }; - - expectTemplate(string) - .withInput({ bar: { baz: 'hello world' } }) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + }) .withRuntimeOptions({ data: {} }) .withMessage('data variables are inherited downstream') .toCompileTo('2hello world1'); }); it('passing in data to a compiled function that expects data - works with helpers in partials', function() { - var string = '{{>myPartial}}'; - var compileOptions = { data: true }; - var partials = { myPartial: CompilerContext.compile('{{hello}}', { data: true }) }; - var helpers = { - hello: function(options) { - return options.data.adjective + ' ' + this.noun; - } - }; - - var input = { noun: 'cat' }; - var runtimeOptions = { data: { adjective: 'happy' } }; - - expectTemplate(string) - .withCompileOptions(compileOptions) + expectTemplate('{{>myPartial}}') + .withCompileOptions({ data: true }) .withPartials(partials) - .withHelpers(helpers) - .withInput(input) - .withRuntimeOptions(runtimeOptions) + .withHelper('hello', function(options) { + return options.data.adjective + ' ' + this.noun; + }) + .withInput({ noun: 'cat' }) + .withRuntimeOptions({ data: { adjective: 'happy' } }) .withMessage('Data output by helper inside partial') .toCompileTo('happy cat'); }); it('passing in data to a compiled function that expects data - works with helpers and parameters', function() { - var string = '{{hello world}}'; - var compileOptions = { data: true }; - - var helpers = { - hello: function(noun, options) { + expectTemplate('{{hello world}}') + .withCompileOptions({ data: true }) + .withHelper('hello', function(noun, options) { return options.data.adjective + ' ' + noun + (this.exclaim ? '!' : ''); - } - }; - - var input = { exclaim: true, world: 'world' }; - var runtimeOptions = { data: { adjective: 'happy' } }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) - .withInput(input) - .withRuntimeOptions(runtimeOptions) + }) + .withInput({ exclaim: true, world: 'world' }) + .withRuntimeOptions({ data: { adjective: 'happy' } }) .withMessage('Data output by helper') .toCompileTo('happy world!'); }); it('passing in data to a compiled function that expects data - works with block helpers', function() { - var string = '{{#hello}}{{world}}{{/hello}}'; - var compileOptions = { - data: true - }; - - var helpers = { - hello: function(options) { + expectTemplate('{{#hello}}{{world}}{{/hello}}') + .withCompileOptions({ + data: true + }) + .withHelper('hello', function(options) { return options.fn(this); - }, - world: function(options) { + }) + .withHelper('world', function(options) { return options.data.adjective + ' world' + (this.exclaim ? '!' : ''); - } - }; - - var input = { exclaim: true }; - var runtimeOptions = { data: { adjective: 'happy' } }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) - .withInput(input) - .withRuntimeOptions(runtimeOptions) + }) + .withInput({ exclaim: true }) + .withRuntimeOptions({ data: { adjective: 'happy' } }) .withMessage('Data output by helper') .toCompileTo('happy world!'); }); it('passing in data to a compiled function that expects data - works with block helpers that use ..', function() { - var string = '{{#hello}}{{world ../zomg}}{{/hello}}'; - var compileOptions = { data: true }; - - var helpers = { - hello: function(options) { + expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}') + .withCompileOptions({ data: true }) + .withHelper('hello', function(options) { return options.fn({ exclaim: '?' }); - }, - world: function(thing, options) { + }) + .withHelper('world', function(thing, options) { return options.data.adjective + ' ' + thing + (this.exclaim || ''); - } - }; - - var input = { exclaim: true, zomg: 'world' }; - var runtimeOptions = { data: { adjective: 'happy' } }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) - .withInput(input) - .withRuntimeOptions(runtimeOptions) + }) + .withInput({ exclaim: true, zomg: 'world' }) + .withRuntimeOptions({ data: { adjective: 'happy' } }) .withMessage('Data output by helper') .toCompileTo('happy world?'); }); it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function() { - var string = '{{#hello}}{{world ../zomg}}{{/hello}}'; - var compileOptions = { data: true }; - - var helpers = { - hello: function(options) { + expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}') + .withCompileOptions({ data: true }) + .withHelper('hello', function(options) { return options.data.accessData + ' ' + options.fn({ exclaim: '?' }); - }, - world: function(thing, options) { + }) + .withHelper('world', function(thing, options) { return options.data.adjective + ' ' + thing + (this.exclaim || ''); - } - }; - - var input = { exclaim: true, zomg: 'world' }; - var runtimeOptions = { data: { adjective: 'happy', accessData: '#win' } }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) - .withInput(input) - .withRuntimeOptions(runtimeOptions) + }) + .withInput({ exclaim: true, zomg: 'world' }) + .withRuntimeOptions({ data: { adjective: 'happy', accessData: '#win' } }) .withMessage('Data output by helper') .toCompileTo('#win happy world?'); }); it('you can override inherited data when invoking a helper', function() { - var string = '{{#hello}}{{world zomg}}{{/hello}}'; - var compileOptions = { data: true }; - - var helpers = { - hello: function(options) { + expectTemplate('{{#hello}}{{world zomg}}{{/hello}}') + .withCompileOptions({ data: true }) + .withHelper('hello', function(options) { return options.fn( { exclaim: '?', zomg: 'world' }, { data: { adjective: 'sad' } } ); - }, - world: function(thing, options) { + }) + .withHelper('world', function(thing, options) { return options.data.adjective + ' ' + thing + (this.exclaim || ''); - } - }; - - var input = { exclaim: true, zomg: 'planet' }; - var runtimeOptions = { data: { adjective: 'happy' } }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) - .withInput(input) - .withRuntimeOptions(runtimeOptions) + }) + .withInput({ exclaim: true, zomg: 'planet' }) + .withRuntimeOptions({ data: { adjective: 'happy' } }) .withMessage('Overriden data output by helper') .toCompileTo('sad world?'); }); it('you can override inherited data when invoking a helper with depth', function() { - var string = '{{#hello}}{{world ../zomg}}{{/hello}}'; - var compileOptions = { data: true }; - - var helpers = { - hello: function(options) { + expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}') + .withCompileOptions({ data: true }) + .withHelper('hello', function(options) { return options.fn({ exclaim: '?' }, { data: { adjective: 'sad' } }); - }, - world: function(thing, options) { + }) + .withHelper('world', function(thing, options) { return options.data.adjective + ' ' + thing + (this.exclaim || ''); - } - }; - - var input = { exclaim: true, zomg: 'world' }; - var runtimeOptions = { data: { adjective: 'happy' } }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) - .withInput(input) - .withRuntimeOptions(runtimeOptions) + }) + .withInput({ exclaim: true, zomg: 'world' }) + .withRuntimeOptions({ data: { adjective: 'happy' } }) .withMessage('Overriden data output by helper') .toCompileTo('sad world?'); }); @@ -362,36 +254,30 @@ describe('data', function() { .withInput(input) .toCompileTo('hello'); }); + it('passed root values take priority', function() { - var string = '{{@root.foo}}'; - var runtimeOptions = { data: { root: { foo: 'hello' } } }; - expectTemplate(string) - .withRuntimeOptions(runtimeOptions) + expectTemplate('{{@root.foo}}') + .withRuntimeOptions({ data: { root: { foo: 'hello' } } }) .toCompileTo('hello'); }); }); describe('nesting', function() { it('the root context can be looked up via @root', function() { - var string = - '{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}'; - var input = { foo: 'hello' }; - var helpers = { - helper: function(options) { + expectTemplate( + '{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}' + ) + .withInput({ foo: 'hello' }) + .withHelper('helper', function(options) { var frame = Handlebars.createFrame(options.data); frame.depth = options.data.depth + 1; return options.fn(this, { data: frame }); - } - }; - var runtimeOptions = { - data: { - depth: 0 - } - }; - expectTemplate(string) - .withInput(input) - .withHelpers(helpers) - .withRuntimeOptions(runtimeOptions) + }) + .withRuntimeOptions({ + data: { + depth: 0 + } + }) .toCompileTo('2 1 0'); }); }); diff --git a/spec/helpers.js b/spec/helpers.js index 59960b384..5166d58d6 100644 --- a/spec/helpers.js +++ b/spec/helpers.js @@ -1,62 +1,44 @@ describe('helpers', function() { it('helper with complex lookup$', function() { - var string = '{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}'; - var hash = { - prefix: '/root', - goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] - }; - var helpers = { - link: function(prefix) { + expectTemplate('{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}') + .withInput({ + prefix: '/root', + goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] + }) + .withHelper('link', function(prefix) { return ( '' + this.text + '' ); - } - }; - expectTemplate(string) - .withInput(hash) - .withHelpers(helpers) + }) .toCompileTo('Goodbye'); }); it('helper for raw block gets raw content', function() { - var string = '{{{{raw}}}} {{test}} {{{{/raw}}}}'; - var hash = { test: 'hello' }; - var helpers = { - raw: function(options) { + expectTemplate('{{{{raw}}}} {{test}} {{{{/raw}}}}') + .withInput({ test: 'hello' }) + .withHelper('raw', function(options) { return options.fn(); - } - }; - expectTemplate(string) - .withInput(hash) - .withHelpers(helpers) + }) .withMessage('raw block helper gets raw content') .toCompileTo(' {{test}} '); }); it('helper for raw block gets parameters', function() { - var string = '{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}'; - var hash = { test: 'hello' }; - var helpers = { - raw: function(a, b, c, options) { + expectTemplate('{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}') + .withInput({ test: 'hello' }) + .withHelper('raw', function(a, b, c, options) { return options.fn() + a + b + c; - } - }; - expectTemplate(string) - .withInput(hash) - .withHelpers(helpers) + }) .withMessage('raw block helper gets raw content') .toCompileTo(' {{test}} 123'); }); describe('raw block parsing (with identity helper-function)', function() { function runWithIdentityHelper(template, expected) { - var helpers = { - identity: function(options) { - return options.fn(); - } - }; expectTemplate(template) - .withHelpers(helpers) + .withHelper('identity', function(options) { + return options.fn(); + }) .toCompileTo(expected); } @@ -96,51 +78,42 @@ describe('helpers', function() { }); it('helper block with identical context', function() { - var string = '{{#goodbyes}}{{name}}{{/goodbyes}}'; - var hash = { name: 'Alan' }; - var helpers = { - goodbyes: function(options) { + 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; - } - }; - expectTemplate(string) - .withInput(hash) - .withHelpers(helpers) + }) .toCompileTo('Goodbye Alan! goodbye Alan! GOODBYE Alan! '); }); + it('helper block with complex lookup expression', function() { - var string = '{{#goodbyes}}{{../name}}{{/goodbyes}}'; - var hash = { name: 'Alan' }; - var helpers = { - goodbyes: function(options) { + 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; - } - }; - expectTemplate(string) - .withInput(hash) - .withHelpers(helpers) + }) .toCompileTo('Goodbye Alan! goodbye Alan! GOODBYE Alan! '); }); it('helper with complex lookup and nested template', function() { - var string = - '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}'; - var hash = { - prefix: '/root', - goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] - }; - var helpers = { - link: function(prefix, options) { + expectTemplate( + '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}' + ) + .withInput({ + prefix: '/root', + goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] + }) + .withHelper('link', function(prefix, options) { return ( 'Goodbye'); }); it('helper with complex lookup and nested template in VM+Compiler', function() { - var string = - '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}'; - var hash = { - prefix: '/root', - goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] - }; - var helpers = { - link: function(prefix, options) { + expectTemplate( + '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}' + ) + .withInput({ + prefix: '/root', + goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] + }) + .withHelper('link', function(prefix, options) { return ( 'Goodbye'); }); + it('helper returning undefined value', function() { expectTemplate(' {{nothere}}') .withHelpers({ nothere: function() {} }) .toCompileTo(' '); + expectTemplate(' {{#nothere}}{{/nothere}}') .withHelpers({ nothere: function() {} @@ -197,55 +164,40 @@ describe('helpers', function() { }); it('block helper', function() { - var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!'; - - var input = { world: 'world' }; - var helpers = { - goodbyes: function(options) { + expectTemplate('{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!') + .withInput({ world: 'world' }) + .withHelper('goodbyes', function(options) { return options.fn({ text: 'GOODBYE' }); - } - }; - - expectTemplate(string) - .withInput(input) - .withHelpers(helpers) + }) .withMessage('Block helper executed') .toCompileTo('GOODBYE! cruel world!'); }); it('block helper staying in the same context', function() { - var string = '{{#form}}
{{name}}
{{/form}}'; - - var input = { name: 'Yehuda' }; - var helpers = { - form: function(options) { + expectTemplate('{{#form}}{{name}}
{{/form}}') + .withInput({ name: 'Yehuda' }) + .withHelper('form', function(options) { return ''; - } - }; - - expectTemplate(string) - .withInput(input) - .withHelpers(helpers) + }) .withMessage('Block helper executed with current context') .toCompileTo(''); }); it('block helper should have context in this', function() { - var source = - '{{name}}
{{/form}}'; - - var input = { yehuda: { name: 'Yehuda' } }; - var helpers = { - form: function(context, options) { + expectTemplate('{{#form yehuda}}{{name}}
{{/form}}') + .withInput({ yehuda: { name: 'Yehuda' } }) + .withHelper('form', function(context, options) { return ''; - } - }; - - expectTemplate(string) - .withInput(input) - .withHelpers(helpers) + }) .withMessage('Context variable resolved') .toCompileTo(''); }); it('block helper passing a complex path context', function() { - var string = '{{#form yehuda/cat}}{{name}}
{{/form}}'; - - var input = { yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } }; - var helpers = { - form: function(context, options) { + expectTemplate('{{#form yehuda/cat}}{{name}}
{{/form}}') + .withInput({ yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } }) + .withHelper('form', function(context, options) { return ''; - } - }; - - expectTemplate(string) - .withInput(input) - .withHelpers(helpers) + }) .withMessage('Complex path variable resolved') .toCompileTo(''); }); it('nested block helpers', function() { - var string = - '{{#form yehuda}}{{name}}
{{#link}}Hello{{/link}}{{/form}}'; - - var input = { - yehuda: { name: 'Yehuda' } - }; - var helpers = { - link: function(options) { + expectTemplate( + '{{#form yehuda}}{{name}}
{{#link}}Hello{{/link}}{{/form}}' + ) + .withInput({ + yehuda: { name: 'Yehuda' } + }) + .withHelper('link', function(options) { return '' + options.fn(this) + ''; - }, - form: function(context, options) { + }) + .withHelper('form', function(context, options) { return ''; - } - }; - - expectTemplate(string) - .withInput(input) - .withHelpers(helpers) + }) .withMessage('Both blocks executed') .toCompileTo(''); }); @@ -329,29 +261,25 @@ describe('helpers', function() { } } - var hash = { people: [{ name: 'Alan' }, { name: 'Yehuda' }] }; - var empty = { people: [] }; - var rootMessage = { - people: [], - message: "Nobody's here" - }; - - var messageString = '{{#list people}}Hello{{^}}{{message}}{{/list}}'; - // 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(hash) + .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(messageString) - .withInput(rootMessage) + + 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
'); @@ -369,10 +297,12 @@ describe('helpers', function() { return 'fail'; } }; + expectTemplate('{{./helper 1}}') .withInput(hash) .withHelpers(helpers) .toCompileTo('winning'); + expectTemplate('{{hash/helper 1}}') .withInput(hash) .withHelpers(helpers) @@ -412,6 +342,7 @@ describe('helpers', function() { }) .withMessage('helpers hash has precedence escaped expansion') .toCompileTo('helpers'); + expectTemplate('{{lookup}}') .withInput({ lookup: 'Explicit' }) .withHelpers({ @@ -484,6 +415,7 @@ describe('helpers', function() { .withInput({ cruel: 'cruel' }) .toCompileTo('found it! Goodbye cruel world!!'); }); + it('fails with multiple and args', function() { shouldThrow( function() { @@ -506,9 +438,8 @@ describe('helpers', function() { }); it('decimal number literals work', function() { - var string = 'Message: {{hello -1.2 1.2}}'; - var helpers = { - hello: function(times, times2) { + expectTemplate('Message: {{hello -1.2 1.2}}') + .withHelper('hello', function(times, times2) { if (typeof times !== 'number') { times = 'NaN'; } @@ -516,35 +447,27 @@ describe('helpers', function() { times2 = 'NaN'; } return 'Hello ' + times + ' ' + times2 + ' times'; - } - }; - expectTemplate(string) - .withHelpers(helpers) + }) .withMessage('template with a negative integer literal') .toCompileTo('Message: Hello -1.2 1.2 times'); }); it('negative number literals work', function() { - var string = 'Message: {{hello -12}}'; - var helpers = { - hello: function(times) { + expectTemplate('Message: {{hello -12}}') + .withHelper('hello', function(times) { if (typeof times !== 'number') { times = 'NaN'; } return 'Hello ' + times + ' times'; - } - }; - expectTemplate(string) - .withHelpers(helpers) + }) .withMessage('template with a negative integer literal') .toCompileTo('Message: Hello -12 times'); }); describe('String literal parameters', function() { it('simple literals work', function() { - var string = 'Message: {{hello "world" 12 true false}}'; - var helpers = { - hello: function(param, times, bool1, bool2) { + expectTemplate('Message: {{hello "world" 12 true false}}') + .withHelper('hello', function(param, times, bool1, bool2) { if (typeof times !== 'number') { times = 'NaN'; } @@ -557,90 +480,65 @@ describe('helpers', function() { return ( 'Hello ' + param + ' ' + times + ' times: ' + bool1 + ' ' + bool2 ); - } - }; - expectTemplate(string) - .withHelpers(helpers) + }) .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() { - var string = 'Message: {{hello wo"rld"}}'; - expectTemplate(string).toThrow(Error); + expectTemplate('Message: {{hello wo"rld"}}').toThrow(Error); }); it('escaping a String is possible', function() { - var string = 'Message: {{{hello "\\"world\\""}}}'; - var helpers = { - hello: function(param) { + expectTemplate('Message: {{{hello "\\"world\\""}}}') + .withHelper('hello', function(param) { return 'Hello ' + param; - } - }; - expectTemplate(string) - .withHelpers(helpers) + }) .withMessage('template with an escaped String literal') .toCompileTo('Message: Hello "world"'); }); it("it works with ' marks", function() { - var string = 'Message: {{{hello "Alan\'s world"}}}'; - var helpers = { - hello: function(param) { + expectTemplate('Message: {{{hello "Alan\'s world"}}}') + .withHelper('hello', function(param) { return 'Hello ' + param; - } - }; - expectTemplate(string) - .withHelpers(helpers) + }) .withMessage("template with a ' mark") .toCompileTo("Message: Hello Alan's world"); }); }); it('negative number literals work', function() { - var string = 'Message: {{hello -12}}'; - var helpers = { - hello: function(times) { + expectTemplate('Message: {{hello -12}}') + .withHelper('hello', function(times) { if (typeof times !== 'number') { times = 'NaN'; } return 'Hello ' + times + ' times'; - } - }; - expectTemplate(string) - .withHelpers(helpers) + }) .withMessage('template with a negative integer literal') .toCompileTo('Message: Hello -12 times'); }); describe('multiple parameters', function() { it('simple multi-params work', function() { - var string = 'Message: {{goodbye cruel world}}'; - var hash = { cruel: 'cruel', world: 'world' }; - var helpers = { - goodbye: function(cruel, world) { + expectTemplate('Message: {{goodbye cruel world}}') + .withInput({ cruel: 'cruel', world: 'world' }) + .withHelper('goodbye', function(cruel, world) { return 'Goodbye ' + cruel + ' ' + world; - } - }; - expectTemplate(string) - .withInput(hash) - .withHelpers(helpers) + }) .withMessage('regular helpers with multiple params') .toCompileTo('Message: Goodbye cruel world'); }); it('block multi-params work', function() { - var string = - 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}'; - var hash = { cruel: 'cruel', world: 'world' }; - var helpers = { - goodbye: function(cruel, world, options) { + 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 }); - } - }; - expectTemplate(string) - .withInput(hash) - .withHelpers(helpers) + }) .withMessage('block helpers with multiple params') .toCompileTo('Message: Goodbye cruel world'); }); @@ -648,10 +546,8 @@ describe('helpers', function() { describe('hash', function() { it('helpers can take an optional hash', function() { - var string = '{{goodbye cruel="CRUEL" world="WORLD" times=12}}'; - - var helpers = { - goodbye: function(options) { + expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" times=12}}') + .withHelper('goodbye', function(options) { return ( 'GOODBYE ' + options.hash.cruel + @@ -661,51 +557,36 @@ describe('helpers', function() { options.hash.times + ' TIMES' ); - } - }; - - var context = {}; - - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + }) .withMessage('Helper output hash') .toCompileTo('GOODBYE CRUEL WORLD 12 TIMES'); }); it('helpers can take an optional hash with booleans', function() { - var helpers = { - goodbye: function(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'; - } + 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'; } - }; - - var context = {}; + } expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" print=true}}') - .withHelpers(helpers) - .withInput(context) + .withHelper('goodbye', goodbye) .withMessage('Helper output hash') .toCompileTo('GOODBYE CRUEL WORLD'); expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" print=false}}') - .withHelpers(helpers) - .withInput(context) + .withHelper('goodbye', goodbye) .withMessage('Boolean helper parameter honored') .toCompileTo('NOT PRINTING'); }); it('block helpers can take an optional hash', function() { - var string = '{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}'; - - var helpers = { - goodbye: function(options) { + expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}') + .withHelper('goodbye', function(options) { return ( 'GOODBYE ' + options.hash.cruel + @@ -715,20 +596,14 @@ describe('helpers', function() { options.hash.times + ' TIMES' ); - } - }; - - expectTemplate(string) - .withHelpers(helpers) + }) .withMessage('Hash parameters output') .toCompileTo('GOODBYE CRUEL world 12 TIMES'); }); it('block helpers can take an optional hash with single quoted stings', function() { - var string = '{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}'; - - var helpers = { - goodbye: function(options) { + expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}') + .withHelper('goodbye', function(options) { return ( 'GOODBYE ' + options.hash.cruel + @@ -738,35 +613,29 @@ describe('helpers', function() { options.hash.times + ' TIMES' ); - } - }; - - expectTemplate(string) - .withHelpers(helpers) + }) .withMessage('Hash parameters output') .toCompileTo('GOODBYE CRUEL world 12 TIMES'); }); it('block helpers can take an optional hash with booleans', function() { - var helpers = { - goodbye: function(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'; - } + 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}}') - .withHelpers(helpers) + .withHelper('goodbye', goodbye) .withMessage('Boolean hash parameter honored') .toCompileTo('GOODBYE CRUEL world'); expectTemplate('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}') - .withHelpers(helpers) + .withHelper('goodbye', goodbye) .withMessage('Boolean hash parameter honored') .toCompileTo('NOT PRINTING'); }); @@ -780,144 +649,104 @@ describe('helpers', function() { }); it('if a context is not found, custom helperMissing is used', function() { - var string = '{{hello}} {{link_to world}}'; - var context = { hello: 'Hello', world: 'world' }; - - var helpers = { - helperMissing: function(mesg, options) { + 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 + ''); } - } - }; - - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + }) .toCompileTo('Hello world'); }); it('if a value is not found, custom helperMissing is used', function() { - var string = '{{hello}} {{link_to}}'; - var context = { hello: 'Hello', world: 'world' }; - - var helpers = { - helperMissing: function(options) { + expectTemplate('{{hello}} {{link_to}}') + .withInput({ hello: 'Hello', world: 'world' }) + .withHelper('helperMissing', function(options) { if (options.name === 'link_to') { return new Handlebars.SafeString('winning'); } - } - }; - - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + }) .toCompileTo('Hello winning'); }); }); describe('knownHelpers', function() { it('Known helper should render helper', function() { - var string = '{{hello}}'; - var compileOptions = { - knownHelpers: { hello: true } - }; - - var helpers = { - hello: function() { + expectTemplate('{{hello}}') + .withCompileOptions({ + knownHelpers: { hello: true } + }) + .withHelper('hello', function() { return 'foo'; - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + }) .toCompileTo('foo'); }); it('Unknown helper in knownHelpers only mode should be passed as undefined', function() { - var string = '{{typeof hello}}'; - var compileOptions = { - knownHelpers: { typeof: true }, - knownHelpersOnly: true - }; - - var helpers = { - typeof: function(arg) { + expectTemplate('{{typeof hello}}') + .withCompileOptions({ + knownHelpers: { typeof: true }, + knownHelpersOnly: true + }) + .withHelper('typeof', function(arg) { return typeof arg; - }, - hello: function() { + }) + .withHelper('hello', function() { return 'foo'; - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + }) .toCompileTo('undefined'); }); - it('Builtin helpers available in knownHelpers only mode', function() { - var string = '{{#unless foo}}bar{{/unless}}'; - var compileOptions = { - knownHelpersOnly: true - }; - expectTemplate(string) - .withCompileOptions(compileOptions) + 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() { - var string = '{{foo}}'; - var compileOptions = { - knownHelpersOnly: true - }; - - var input = { foo: 'bar' }; - expectTemplate(string) - .withCompileOptions(compileOptions) - .withInput(input) + expectTemplate('{{foo}}') + .withCompileOptions({ + knownHelpersOnly: true + }) + .withInput({ foo: 'bar' }) .toCompileTo('bar'); }); + it('Conditional blocks work in knownHelpers only mode', function() { - var string = '{{#foo}}bar{{/foo}}'; - var compileOptions = { - knownHelpersOnly: true - }; - - var input = { foo: 'baz' }; - expectTemplate(string) - .withCompileOptions(compileOptions) - .withInput(input) + expectTemplate('{{#foo}}bar{{/foo}}') + .withCompileOptions({ + knownHelpersOnly: true + }) + .withInput({ foo: 'baz' }) .toCompileTo('bar'); }); + it('Invert blocks work in knownHelpers only mode', function() { - var string = '{{^foo}}bar{{/foo}}'; - var compileOptions = { - knownHelpersOnly: true - }; - - var input = { foo: false }; - expectTemplate(string) - .withCompileOptions(compileOptions) - .withInput(input) + expectTemplate('{{^foo}}bar{{/foo}}') + .withCompileOptions({ + knownHelpersOnly: true + }) + .withInput({ foo: false }) .toCompileTo('bar'); }); + it('Functions are bound to the context in knownHelpers only mode', function() { - var string = '{{foo}}'; - var compileOptions = { - knownHelpersOnly: true - }; - var input = { - foo: function() { - return this.bar; - }, - bar: 'bar' - }; - expectTemplate(string) - .withCompileOptions(compileOptions) - .withInput(input) + 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 }) @@ -927,34 +756,30 @@ describe('helpers', function() { describe('blockHelperMissing', function() { it('lambdas are resolved by blockHelperMissing, not handlebars proper', function() { - var string = '{{#truthy}}yep{{/truthy}}'; - var data = { - truthy: function() { - return true; - } - }; - expectTemplate(string) - .withInput(data) + expectTemplate('{{#truthy}}yep{{/truthy}}') + .withInput({ + truthy: function() { + return true; + } + }) .toCompileTo('yep'); }); + it('lambdas resolved by blockHelperMissing are bound to the context', function() { - var string = '{{#truthy}}yep{{/truthy}}'; - var boundData = { - truthy: function() { - return this.truthiness(); - }, - truthiness: function() { - return false; - } - }; - expectTemplate(string) - .withInput(boundData) + expectTemplate('{{#truthy}}yep{{/truthy}}') + .withInput({ + truthy: function() { + return this.truthiness(); + }, + truthiness: function() { + return false; + } + }) .toCompileTo(''); }); }); describe('name field', function() { - var context = {}; var helpers = { blockHelperMissing: function() { return 'missing: ' + arguments[arguments.length - 1].name; @@ -969,43 +794,40 @@ describe('helpers', function() { it('should include in ambiguous mustache calls', function() { expectTemplate('{{helper}}') - .withInput(context) .withHelpers(helpers) .toCompileTo('ran: helper'); }); + it('should include in helper mustache calls', function() { expectTemplate('{{helper 1}}') - .withInput(context) .withHelpers(helpers) .toCompileTo('ran: helper'); }); + it('should include in ambiguous block calls', function() { expectTemplate('{{#helper}}{{/helper}}') - .withInput(context) .withHelpers(helpers) .toCompileTo('ran: helper'); }); + it('should include in simple block calls', function() { expectTemplate('{{#./helper}}{{/./helper}}') - .withInput(context) .withHelpers(helpers) .toCompileTo('missing: ./helper'); }); + it('should include in helper block calls', function() { expectTemplate('{{#helper 1}}{{/helper}}') - .withInput(context) .withHelpers(helpers) .toCompileTo('ran: helper'); }); + it('should include in known helper calls', function() { - var string = '{{helper}}'; - var compileOptions = { - knownHelpers: { helper: true }, - knownHelpersOnly: true - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) + expectTemplate('{{helper}}') + .withCompileOptions({ + knownHelpers: { helper: true }, + knownHelpersOnly: true + }) .withHelpers(helpers) .toCompileTo('ran: helper'); }); @@ -1027,101 +849,67 @@ describe('helpers', function() { describe('name conflicts', function() { it('helpers take precedence over same-named context properties', function() { - var string = '{{goodbye}} {{cruel world}}'; - - var helpers = { - goodbye: function() { + expectTemplate('{{goodbye}} {{cruel world}}') + .withHelper('goodbye', function() { return this.goodbye.toUpperCase(); - }, - - cruel: function(world) { + }) + .withHelper('cruel', function(world) { return 'cruel ' + world.toUpperCase(); - } - }; - - var context = { - goodbye: 'goodbye', - world: 'world' - }; - - expectTemplate(string) - .withHelpers(helpers) - .withInput(context) + }) + .withInput({ + goodbye: 'goodbye', + world: 'world' + }) .withMessage('Helper executed') .toCompileTo('GOODBYE cruel WORLD'); }); it('helpers take precedence over same-named context properties$', function() { - var string = '{{#goodbye}} {{cruel world}}{{/goodbye}}'; - - var helpers = { - goodbye: function(options) { + expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}}') + .withHelper('goodbye', function(options) { return this.goodbye.toUpperCase() + options.fn(this); - }, - - cruel: function(world) { + }) + .withHelper('cruel', function(world) { return 'cruel ' + world.toUpperCase(); - } - }; - - var context = { - goodbye: 'goodbye', - world: 'world' - }; - - expectTemplate(string) - .withHelpers(helpers) - .withInput(context) + }) + .withInput({ + goodbye: 'goodbye', + world: 'world' + }) .withMessage('Helper executed') .toCompileTo('GOODBYE cruel WORLD'); }); it('Scoped names take precedence over helpers', function() { - var string = '{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}'; - - var helpers = { - goodbye: function() { + expectTemplate('{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}') + .withHelper('goodbye', function() { return this.goodbye.toUpperCase(); - }, - - cruel: function(world) { + }) + .withHelper('cruel', function(world) { return 'cruel ' + world.toUpperCase(); - } - }; - - var context = { - goodbye: 'goodbye', - world: 'world' - }; - - expectTemplate(string) - .withHelpers(helpers) - .withInput(context) + }) + .withInput({ + goodbye: 'goodbye', + world: 'world' + }) .withMessage('Helper not executed') .toCompileTo('goodbye cruel WORLD cruel GOODBYE'); }); it('Scoped names take precedence over block helpers', function() { - var string = '{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}'; - - var helpers = { - goodbye: function(options) { + expectTemplate( + '{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}' + ) + .withHelper('goodbye', function(options) { return this.goodbye.toUpperCase() + options.fn(this); - }, - - cruel: function(world) { + }) + .withHelper('cruel', function(world) { return 'cruel ' + world.toUpperCase(); - } - }; - - var context = { - goodbye: 'goodbye', - world: 'world' - }; - - expectTemplate(string) - .withHelpers(helpers) - .withInput(context) + }) + .withInput({ + goodbye: 'goodbye', + world: 'world' + }) .withMessage('Helper executed') .toCompileTo('GOODBYE cruel WORLD goodbye'); }); @@ -1129,57 +917,49 @@ describe('helpers', function() { describe('block params', function() { it('should take presedence over context values', function() { - var hash = { value: 'foo' }; - var helpers = { - goodbyes: function(options) { + 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] }); - } - }; - expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}') - .withInput(hash) - .withHelpers(helpers) + }) .toCompileTo('1foo'); }); + it('should take presedence over helper values', function() { - var hash = {}; - var helpers = { - value: function() { + expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}') + .withHelper('value', function() { return 'foo'; - }, - goodbyes: function(options) { + }) + .withHelper('goodbyes', function(options) { equals(options.fn.blockParams, 1); return options.fn({}, { blockParams: [1, 2] }); - } - }; - expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}') - .withInput(hash) - .withHelpers(helpers) + }) .toCompileTo('1foo'); }); + it('should not take presedence over pathed values', function() { - var hash = { value: 'bar' }; - var helpers = { - value: function() { - return 'foo'; - }, - goodbyes: function(options) { - equals(options.fn.blockParams, 1); - return options.fn(this, { blockParams: [1, 2] }); - } - }; expectTemplate( '{{#goodbyes as |value|}}{{./value}}{{/goodbyes}}{{value}}' ) - .withInput(hash) - .withHelpers(helpers) + .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 hash = { value: 'foo' }, - value = 1; - var helpers = { - goodbyes: function(options) { + 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' }, { @@ -1187,72 +967,70 @@ describe('helpers', function() { options.fn.blockParams === 1 ? [value++, value++] : undefined } ); - } - }; - expectTemplate( - '{{#goodbyes as |value|}}{{#goodbyes}}{{value}}{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{/goodbyes}}{{/goodbyes}}{{value}}' - ) - .withInput(hash) - .withHelpers(helpers) + }) .toCompileTo('13foo'); }); it('should allow block params on chained helpers', function() { - var hash = { value: 'foo' }; - var helpers = { - goodbyes: function(options) { - equals(options.fn.blockParams, 1); - return options.fn({ value: 'bar' }, { blockParams: [1, 2] }); - } - }; expectTemplate( '{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}' ) - .withInput(hash) - .withHelpers(helpers) + .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() { - var string = '{{#if}}{{/if}}'; - expectTemplate(string).toThrow(/#if requires exactly one argument/); + expectTemplate('{{#if}}{{/if}}').toThrow( + /#if requires exactly one argument/ + ); }); it('if helper - too many arguments, string', function() { - var string = '{{#if test "string"}}{{/if}}'; - expectTemplate(string).toThrow(/#if requires exactly one argument/); + expectTemplate('{{#if test "string"}}{{/if}}').toThrow( + /#if requires exactly one argument/ + ); }); it('if helper - too many arguments, undefined', function() { - var string = '{{#if test undefined}}{{/if}}'; - expectTemplate(string).toThrow(/#if requires exactly one argument/); + expectTemplate('{{#if test undefined}}{{/if}}').toThrow( + /#if requires exactly one argument/ + ); }); it('if helper - too many arguments, null', function() { - var string = '{{#if test null}}{{/if}}'; - expectTemplate(string).toThrow(/#if requires exactly one argument/); + expectTemplate('{{#if test null}}{{/if}}').toThrow( + /#if requires exactly one argument/ + ); }); it('unless helper - too few arguments', function() { - var string = '{{#unless}}{{/unless}}'; - expectTemplate(string).toThrow(/#unless requires exactly one argument/); + expectTemplate('{{#unless}}{{/unless}}').toThrow( + /#unless requires exactly one argument/ + ); }); it('unless helper - too many arguments', function() { - var string = '{{#unless test null}}{{/unless}}'; - expectTemplate(string).toThrow(/#unless requires exactly one argument/); + expectTemplate('{{#unless test null}}{{/unless}}').toThrow( + /#unless requires exactly one argument/ + ); }); it('with helper - too few arguments', function() { - var string = '{{#with}}{{/with}}'; - expectTemplate(string).toThrow(/#with requires exactly one argument/); + expectTemplate('{{#with}}{{/with}}').toThrow( + /#with requires exactly one argument/ + ); }); it('with helper - too many arguments', function() { - var string = '{{#with test "string"}}{{/with}}'; - expectTemplate(string).toThrow(/#with requires exactly one argument/); + expectTemplate('{{#with test "string"}}{{/with}}').toThrow( + /#with requires exactly one argument/ + ); }); }); diff --git a/spec/partials.js b/spec/partials.js index a98bfd23f..a5bf1c452 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -8,10 +8,12 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; + expectTemplate(string) .withInput(hash) .withPartials({ dude: partial }) .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); + expectTemplate(string) .withInput(hash) .withPartials({ dude: partial }) @@ -34,11 +36,13 @@ describe('partials', function() { return 'dude'; } }; + expectTemplate(string) .withInput(hash) .withHelpers(helpers) .withPartials({ dude: partial }) .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); + expectTemplate(string) .withInput(hash) .withHelpers(helpers) @@ -47,39 +51,31 @@ describe('partials', function() { .withCompileOptions({ data: false }) .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); }); + it('failing dynamic partials', function() { - var string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}'; - var partial = '{{name}} ({{url}}) '; - var hash = { - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - var helpers = { - partial: function() { + expectTemplate('Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}') + .withInput({ + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withHelper('partial', function() { return 'missing'; - } - }; - expectTemplate(string) - .withInput(hash) - .withHelpers(helpers) - .withPartial('dude', partial) + }) + .withPartial('dude', '{{name}} ({{url}}) ') .toThrow(Handlebars.Exception, 'The partial missing could not be found'); }); it('partials with context', function() { - var string = 'Dudes: {{>dude dudes}}'; - var partial = '{{#this}}{{name}} ({{url}}) {{/this}}'; - var hash = { - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{>dude dudes}}') + .withInput({ + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withPartial('dude', '{{#this}}{{name}} ({{url}}) {{/this}}') .withMessage('Partials can be passed a context') .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); }); @@ -92,35 +88,30 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; + expectTemplate('Dudes: {{#dudes}}{{>dude}}{{/dudes}}') .withInput(hash) - .withPartials({ dude: partial }) + .withPartial('dude', partial) .withCompileOptions({ explicitPartialContext: true }) .toCompileTo('Dudes: () () '); + expectTemplate('Dudes: {{#dudes}}{{>dude name="foo"}}{{/dudes}}') .withInput(hash) - .withPartials({ dude: partial }) + .withPartial('dude', partial) .withCompileOptions({ explicitPartialContext: true }) .toCompileTo('Dudes: foo () foo () '); }); it('partials with string context', function() { - var string = 'Dudes: {{>dude "dudes"}}'; - var partial = '{{.}}'; - var hash = {}; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{>dude "dudes"}}') + .withPartial('dude', '{{.}}') .toCompileTo('Dudes: dudes'); }); it('partials with undefined context', function() { - var string = 'Dudes: {{>dude dudes}}'; - var partial = '{{foo}} Empty'; - var hash = {}; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{>dude dudes}}') + .withInput(undefined) + .withPartial('dude', '{{foo}} Empty') .toCompileTo('Dudes: Empty'); }); @@ -132,37 +123,30 @@ describe('partials', function() { }); it('partials with parameters', function() { - var string = 'Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}'; - var partial = '{{others.foo}}{{name}} ({{url}}) '; - var hash = { - foo: 'bar', - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}') + .withInput({ + foo: 'bar', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withPartial('dude', '{{others.foo}}{{name}} ({{url}}) ') .withMessage('Basic partials output based on current context.') .toCompileTo('Dudes: barYehuda (http://yehuda) barAlan (http://alan) '); }); it('partial in a partial', function() { - var string = 'Dudes: {{#dudes}}{{>dude}}{{/dudes}}'; - var dude = '{{name}} {{> url}} '; - var url = '{{url}}'; - var hash = { - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) + expectTemplate('Dudes: {{#dudes}}{{>dude}}{{/dudes}}') + .withInput({ + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) .withPartials({ - dude: dude, - url: url + dude: '{{name}} {{> url}} ', + url: '{{url}}' }) .withMessage('Partials are rendered inside of other partials') .toCompileTo( @@ -196,52 +180,41 @@ describe('partials', function() { }); it('rendering function partial in vm mode', function() { - var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}'; function partial(context) { return context.name + ' (' + context.url + ') '; } - var hash = { - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}') + .withInput({ + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withPartial('dude', partial) .withMessage('Function partials output based in VM.') .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); }); it('GH-14: a partial preceding a selector', function() { - var string = 'Dudes: {{>dude}} {{anotherDude}}'; - var dude = '{{name}}'; - var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: dude }) + expectTemplate('Dudes: {{>dude}} {{anotherDude}}') + .withInput({ name: 'Jeepers', anotherDude: 'Creepers' }) + .withPartial('dude', '{{name}}') .withMessage('Regular selectors can follow a partial') .toCompileTo('Dudes: Jeepers Creepers'); }); it('Partials with slash paths', function() { - var string = 'Dudes: {{> shared/dude}}'; - var dude = '{{name}}'; - var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - expectTemplate(string) - .withInput(hash) - .withPartials({ 'shared/dude': dude }) + expectTemplate('Dudes: {{> shared/dude}}') + .withInput({ name: 'Jeepers', anotherDude: 'Creepers' }) + .withPartial('shared/dude', '{{name}}') .withMessage('Partials can use literal paths') .toCompileTo('Dudes: Jeepers'); }); it('Partials with slash and point paths', function() { - var string = 'Dudes: {{> shared/dude.thing}}'; - var dude = '{{name}}'; - var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - expectTemplate(string) - .withInput(hash) - .withPartials({ 'shared/dude.thing': dude }) + expectTemplate('Dudes: {{> shared/dude.thing}}') + .withInput({ name: 'Jeepers', anotherDude: 'Creepers' }) + .withPartial('shared/dude.thing', '{{name}}') .withMessage('Partials can use literal with points in paths') .toCompileTo('Dudes: Jeepers'); }); @@ -249,12 +222,9 @@ describe('partials', function() { it('Global Partials', function() { handlebarsEnv.registerPartial('globalTest', '{{anotherDude}}'); - var string = 'Dudes: {{> shared/dude}} {{> globalTest}}'; - var dude = '{{name}}'; - var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - expectTemplate(string) - .withInput(hash) - .withPartials({ 'shared/dude': dude }) + expectTemplate('Dudes: {{> shared/dude}} {{> globalTest}}') + .withInput({ name: 'Jeepers', anotherDude: 'Creepers' }) + .withPartial('shared/dude', '{{name}}') .withMessage('Partials can use globals or passed') .toCompileTo('Dudes: Jeepers Creepers'); @@ -268,71 +238,54 @@ describe('partials', function() { globalTest: '{{anotherDude}}' }); - var string = 'Dudes: {{> shared/dude}} {{> globalTest}}'; - var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - expectTemplate(string) - .withInput(hash) + expectTemplate('Dudes: {{> shared/dude}} {{> globalTest}}') + .withInput({ name: 'Jeepers', anotherDude: 'Creepers' }) .withPartial('notused', 'notused') // trick the test bench into running with partials enabled .withMessage('Partials can use globals or passed') .toCompileTo('Dudes: Jeepers Creepers'); }); it('Partials with integer path', function() { - var string = 'Dudes: {{> 404}}'; - var dude = '{{name}}'; - var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - expectTemplate(string) - .withInput(hash) - .withPartials({ 404: dude }) + expectTemplate('Dudes: {{> 404}}') + .withInput({ name: 'Jeepers', anotherDude: 'Creepers' }) + .withPartial(404, '{{name}}') .withMessage('Partials can use literal paths') .toCompileTo('Dudes: Jeepers'); }); it('Partials with complex path', function() { - var string = 'Dudes: {{> 404/asdf?.bar}}'; - var dude = '{{name}}'; - var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - expectTemplate(string) - .withInput(hash) - .withPartials({ '404/asdf?.bar': dude }) + expectTemplate('Dudes: {{> 404/asdf?.bar}}') + .withInput({ name: 'Jeepers', anotherDude: 'Creepers' }) + .withPartial('404/asdf?.bar', '{{name}}') .withMessage('Partials can use literal paths') .toCompileTo('Dudes: Jeepers'); }); it('Partials with escaped', function() { - var string = 'Dudes: {{> [+404/asdf?.bar]}}'; - var dude = '{{name}}'; - var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - expectTemplate(string) - .withInput(hash) - .withPartials({ '+404/asdf?.bar': dude }) + expectTemplate('Dudes: {{> [+404/asdf?.bar]}}') + .withInput({ name: 'Jeepers', anotherDude: 'Creepers' }) + .withPartial('+404/asdf?.bar', '{{name}}') .withMessage('Partials can use literal paths') .toCompileTo('Dudes: Jeepers'); }); it('Partials with string', function() { - var string = "Dudes: {{> '+404/asdf?.bar'}}"; - var dude = '{{name}}'; - var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - expectTemplate(string) - .withInput(hash) - .withPartials({ '+404/asdf?.bar': dude }) + expectTemplate("Dudes: {{> '+404/asdf?.bar'}}") + .withInput({ name: 'Jeepers', anotherDude: 'Creepers' }) + .withPartial('+404/asdf?.bar', '{{name}}') .withMessage('Partials can use literal paths') .toCompileTo('Dudes: Jeepers'); }); it('should handle empty partial', function() { - var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}'; - var partial = ''; - var hash = { - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}') + .withInput({ + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withPartial('dude', '') .toCompileTo('Dudes: '); }); @@ -352,11 +305,13 @@ describe('partials', function() { it('should render partial block as default', function() { expectTemplate('{{#> dude}}success{{/dude}}').toCompileTo('success'); }); + it('should execute default block with proper context', function() { expectTemplate('{{#> dude context}}{{value}}{{/dude}}') .withInput({ context: { value: 'success' } }) .toCompileTo('success'); }); + it('should propagate block parameters to default block', function() { expectTemplate( '{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}' @@ -376,11 +331,13 @@ describe('partials', function() { .withPartials({ dude: '{{> @partial-block }}' }) .toCompileTo('success'); }); + it('should be able to render the partial-block twice', function() { expectTemplate('{{#> dude}}success{{/dude}}') .withPartials({ dude: '{{> @partial-block }} {{> @partial-block }}' }) .toCompileTo('success success'); }); + it('should render block from partial with context', function() { expectTemplate('{{#> dude}}{{value}}{{/dude}}') .withInput({ context: { value: 'success' } }) @@ -415,6 +372,7 @@ describe('partials', function() { '