From cccb90e884011bdf4f47ac3f198dab8298610360 Mon Sep 17 00:00:00 2001 From: John Boehr Date: Sun, 19 Apr 2020 14:27:44 -0700 Subject: [PATCH 01/11] In tests, use expectTemplate() over equals() and shouldThrow() --- spec/basic.js | 13 +- spec/blocks.js | 42 ++-- spec/builtins.js | 172 ++++++--------- spec/data.js | 356 ++++++++++++++++-------------- spec/env/common.js | 16 ++ spec/helpers.js | 484 ++++++++++++++++++----------------------- spec/partials.js | 48 +--- spec/regressions.js | 32 +-- spec/security.js | 30 +-- spec/strict.js | 241 ++++++++------------ spec/string-params.js | 158 +++++++------- spec/subexpressions.js | 41 ++-- spec/track-ids.js | 280 ++++++++++++------------ 13 files changed, 869 insertions(+), 1044 deletions(-) diff --git a/spec/basic.js b/spec/basic.js index 65d228729..4ed1914de 100644 --- a/spec/basic.js +++ b/spec/basic.js @@ -483,10 +483,7 @@ describe('basic context', function() { }); it('this keyword nested inside path', function() { - shouldThrow( - function() { - CompilerContext.compile('{{#hellos}}{{text/this/foo}}{{/hellos}}'); - }, + expectTemplate('{{#hellos}}{{text/this/foo}}{{/hellos}}').toThrow( Error, 'Invalid path: text/this - 1:13' ); @@ -524,13 +521,7 @@ describe('basic context', function() { it('this keyword nested inside helpers param', function() { var string = '{{#hellos}}{{foo text/this/foo}}{{/hellos}}'; - shouldThrow( - function() { - CompilerContext.compile(string); - }, - Error, - 'Invalid path: text/this - 1:17' - ); + expectTemplate(string).toThrow(Error, 'Invalid path: text/this - 1:17'); shouldCompileTo( '{{foo [this]}}', diff --git a/spec/blocks.js b/spec/blocks.js index ad534aaba..2e5516198 100644 --- a/spec/blocks.js +++ b/spec/blocks.js @@ -42,14 +42,10 @@ describe('blocks', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal( - result, - '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', - 'The @index variable is used' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('The @index variable is used') + .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!'); }); it('empty block', function() { @@ -101,9 +97,7 @@ describe('blocks', function() { it('block with complex lookup using nested context', function() { var string = '{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}'; - shouldThrow(function() { - CompilerContext.compile(string); - }, Error); + expectTemplate(string).toThrow(Error); }); it('block with deep nested complex lookup', function() { @@ -118,18 +112,20 @@ describe('blocks', function() { }); it('works with cached blocks', function() { - var template = CompilerContext.compile( - '{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}', - { data: false } - ); + var string = + '{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}'; + var compileOptions = { data: false }; - var result = template({ + var input = { person: [ { first: 'Alan', last: 'Johnson' }, { first: 'Alan', last: 'Johnson' } ] - }); - equals(result, 'Alan JohnsonAlan Johnson'); + }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withInput(input) + .toCompileTo('Alan JohnsonAlan Johnson'); }); describe('inverted sections', function() { @@ -194,13 +190,9 @@ describe('blocks', function() { ); }); it('chained inverted sections with mismatch', function() { - shouldThrow(function() { - shouldCompileTo( - '{{#people}}{{name}}{{else if none}}{{none}}{{/if}}', - { none: 'No people' }, - 'No people' - ); - }, Error); + expectTemplate( + '{{#people}}{{name}}{{else if none}}{{none}}{{/if}}' + ).toThrow(Error); }); it('block inverted sections with empty arrays', function() { diff --git a/spec/builtins.js b/spec/builtins.js index c74092e1b..8502bfe44 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -146,13 +146,10 @@ describe('builtin helpers', function() { ); }); it('works when data is disabled', function() { - var template = CompilerContext.compile( - '{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}', - { data: false } - ); - - var result = template({ person: { first: 'Alan', last: 'Johnson' } }); - equals(result, 'Alan Johnson'); + expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}') + .withInput({ person: { first: 'Alan', last: 'Johnson' } }) + .withCompileOptions({ data: false }) + .toCompileTo('Alan Johnson'); }); }); @@ -256,14 +253,10 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal( - result, - '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', - 'The @index variable is used' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('The @index variable is used') + .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!'); }); it('each with nested @index', function() { @@ -278,14 +271,12 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal( - result, - '0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!', - 'The @index variable is used' - ); + expectTemplate(string) + .withInput(hash) + .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!' + ); }); it('each with block params', function() { @@ -296,13 +287,11 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal( - result, - '0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!' - ); + expectTemplate(string) + .withInput(hash) + .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() { @@ -317,14 +306,10 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal( - result, - '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', - 'The @index variable is used' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('The @index variable is used') + .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!'); }); it('each with @first', function() { @@ -339,10 +324,10 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal(result, 'goodbye! cruel world!', 'The @first variable is used'); + expectTemplate(string) + .withInput(hash) + .withMessage('The @first variable is used') + .toCompileTo('goodbye! cruel world!'); }); it('each with nested @first', function() { @@ -357,14 +342,12 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal( - result, - '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!', - 'The @first variable is used' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('The @first variable is used') + .toCompileTo( + '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!' + ); }); it('each object with @first', function() { @@ -375,10 +358,10 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal(result, 'goodbye! cruel world!', 'The @first variable is used'); + expectTemplate(string) + .withInput(hash) + .withMessage('The @first variable is used') + .toCompileTo('goodbye! cruel world!'); }); it('each with @last', function() { @@ -393,10 +376,10 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal(result, 'GOODBYE! cruel world!', 'The @last variable is used'); + expectTemplate(string) + .withInput(hash) + .withMessage('The @last variable is used') + .toCompileTo('GOODBYE! cruel world!'); }); it('each object with @last', function() { @@ -407,10 +390,10 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal(result, 'Goodbye! cruel world!', 'The @last variable is used'); + expectTemplate(string) + .withInput(hash) + .withMessage('The @last variable is used') + .toCompileTo('Goodbye! cruel world!'); }); it('each with nested @last', function() { @@ -425,14 +408,12 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal( - result, - '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!', - 'The @last variable is used' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('The @last variable is used') + .toCompileTo( + '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!' + ); }); it('each with function argument', function() { @@ -473,37 +454,30 @@ describe('builtin helpers', function() { world: 'world' }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal( - result, - '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', - 'Empty string key is not skipped' - ); + expectTemplate(string) + .withInput(hash) + .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'] }; - var template = CompilerContext.compile(string); - var result = template(hash, { - data: { - exclaim: '!' - } - }); - equal(result, 'a!b!c!', 'should output data'); + expectTemplate(string) + .withInput(hash) + .withMessage('should output data') + .withRuntimeOptions({ + data: { + exclaim: '!' + } + }) + .toCompileTo('a!b!c!'); }); it('each on implicit context', function() { - shouldThrow( - function() { - var template = CompilerContext.compile( - '{{#each}}{{text}}! {{/each}}cruel world!' - ); - template({}); - }, + var string = '{{#each}}{{text}}! {{/each}}cruel world!'; + expectTemplate(string).toThrow( handlebarsEnv.Exception, 'Must pass iterator to #each' ); @@ -735,19 +709,17 @@ describe('builtin helpers', function() { var string = '{{#each goodbyes}}{{lookup ../data .}}{{/each}}', hash = { goodbyes: [0, 1], data: ['foo', 'bar'] }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal(result, 'foobar'); + expectTemplate(string) + .withInput(hash) + .toCompileTo('foobar'); }); it('should not fail on undefined value', function() { var string = '{{#each goodbyes}}{{lookup ../bar .}}{{/each}}', hash = { goodbyes: [0, 1], data: ['foo', 'bar'] }; - var template = CompilerContext.compile(string); - var result = template(hash); - - equal(result, ''); + expectTemplate(string) + .withInput(hash) + .toCompileTo(''); }); }); }); diff --git a/spec/data.js b/spec/data.js index 0defdc14a..5589eb0b0 100644 --- a/spec/data.js +++ b/spec/data.js @@ -1,6 +1,6 @@ describe('data', function() { it('passing in data to a compiled function that expects data - works with helpers', function() { - var template = CompilerContext.compile('{{hello}}', { data: true }); + var string = '{{hello}}'; var helpers = { hello: function(options) { @@ -8,23 +8,25 @@ describe('data', function() { } }; - var result = template( - { noun: 'cat' }, - { helpers: helpers, data: { adjective: 'happy' } } - ); - equals('happy cat', result, 'Data output by helper'); + expectTemplate(string) + .withCompileOptions({ data: true }) + .withHelpers(helpers) + .withRuntimeOptions({ data: { adjective: 'happy' } }) + .withInput({ noun: 'cat' }) + .withMessage('Data output by helper') + .toCompileTo('happy cat'); }); it('data can be looked up via @foo', function() { - var template = CompilerContext.compile('{{@hello}}'); - var result = template({}, { data: { hello: 'hello' } }); - equals('hello', result, '@foo retrieves template data'); + expectTemplate('{{@hello}}') + .withRuntimeOptions({ data: { hello: 'hello' } }) + .withMessage('@foo retrieves template data') + .toCompileTo('hello'); }); it('deep @foo triggers automatic top-level data', function() { - var template = CompilerContext.compile( - '{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}' - ); + var string = + '{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}'; var helpers = Handlebars.createFrame(handlebarsEnv.helpers); @@ -39,122 +41,113 @@ describe('data', function() { return options.fn(this, { data: frame }); }; - var result = template({ foo: true }, { helpers: helpers }); - equals('Hello world', result, 'Automatic data was triggered'); + expectTemplate(string) + .withInput({ foo: true }) + .withHelpers(helpers) + .withMessage('Automatic data was triggered') + .toCompileTo('Hello world'); }); it('parameter data can be looked up via @foo', function() { - var template = CompilerContext.compile('{{hello @world}}'); + var string = '{{hello @world}}'; var helpers = { hello: function(noun) { return 'Hello ' + noun; } }; - var result = template({}, { helpers: helpers, data: { world: 'world' } }); - equals( - 'Hello world', - result, - '@foo as a parameter retrieves template data' - ); + expectTemplate(string) + .withRuntimeOptions({ data: { world: 'world' } }) + .withHelpers(helpers) + .withMessage('@foo as a parameter retrieves template data') + .toCompileTo('Hello world'); }); it('hash values can be looked up via @foo', function() { - var template = CompilerContext.compile('{{hello noun=@world}}'); + var string = '{{hello noun=@world}}'; var helpers = { hello: function(options) { return 'Hello ' + options.hash.noun; } }; - var result = template({}, { helpers: helpers, data: { world: 'world' } }); - equals( - 'Hello world', - result, - '@foo as a parameter retrieves template data' - ); + expectTemplate(string) + .withRuntimeOptions({ data: { world: 'world' } }) + .withHelpers(helpers) + .withMessage('@foo as a parameter retrieves template data') + .toCompileTo('Hello world'); }); it('nested parameter data can be looked up via @foo.bar', function() { - var template = CompilerContext.compile('{{hello @world.bar}}'); + var string = '{{hello @world.bar}}'; var helpers = { hello: function(noun) { return 'Hello ' + noun; } }; - var result = template( - {}, - { helpers: helpers, data: { world: { bar: 'world' } } } - ); - equals( - 'Hello world', - result, - '@foo as a parameter retrieves template data' - ); + expectTemplate(string) + .withRuntimeOptions({ data: { world: { bar: 'world' } } }) + .withHelpers(helpers) + .withMessage('@foo as a parameter retrieves template data') + .toCompileTo('Hello world'); }); it('nested parameter data does not fail with @world.bar', function() { - var template = CompilerContext.compile('{{hello @world.bar}}'); + var string = '{{hello @world.bar}}'; var helpers = { hello: function(noun) { return 'Hello ' + noun; } }; - var result = template( - {}, - { helpers: helpers, data: { foo: { bar: 'world' } } } - ); - equals( - 'Hello undefined', - result, - '@foo as a parameter retrieves template data' - ); + expectTemplate(string) + .withRuntimeOptions({ data: { foo: { bar: 'world' } } }) + .withHelpers(helpers) + .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}}'; - shouldThrow(function() { - CompilerContext.compile(string); - }, Error); + expectTemplate(string).toThrow(Error); }); it('data can be functions', function() { - var template = CompilerContext.compile('{{@hello}}'); - var result = template( - {}, - { - data: { - hello: function() { - return 'hello'; - } + var string = '{{@hello}}'; + var runtimeOptions = { + data: { + hello: function() { + return 'hello'; } } - ); - equals('hello', result); + }; + + expectTemplate(string) + .withRuntimeOptions(runtimeOptions) + .toCompileTo('hello'); }); + it('data can be functions with params', function() { - var template = CompilerContext.compile('{{@hello "hello"}}'); - var result = template( - {}, - { - data: { - hello: function(arg) { - return arg; - } + var string = '{{@hello "hello"}}'; + var runtimeOptions = { + data: { + hello: function(arg) { + return arg; } } - ); - equals('hello', result); + }; + + expectTemplate(string) + .withRuntimeOptions(runtimeOptions) + .toCompileTo('hello'); }); it('data is inherited downstream', function() { - var template = CompilerContext.compile( - '{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}', - { data: true } - ); + 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) { var frame = Handlebars.createFrame(options.data); @@ -167,15 +160,18 @@ describe('data', function() { } }; - var result = template( - { bar: { baz: 'hello world' } }, - { helpers: helpers, data: {} } - ); - equals('2hello world1', result, 'data variables are inherited downstream'); + 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 template = CompilerContext.compile('{{>myPartial}}', { data: true }); + var string = '{{>myPartial}}'; + var compileOptions = { data: true }; var partials = { myPartial: CompilerContext.compile('{{hello}}', { data: true }) @@ -187,15 +183,22 @@ describe('data', function() { } }; - var result = template( - { noun: 'cat' }, - { helpers: helpers, partials: partials, data: { adjective: 'happy' } } - ); - equals('happy cat', result, 'Data output by helper inside partial'); + var input = { noun: 'cat' }; + var runtimeOptions = { data: { adjective: 'happy' } }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withPartials(partials) + .withHelpers(helpers) + .withInput(input) + .withRuntimeOptions(runtimeOptions) + .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 template = CompilerContext.compile('{{hello world}}', { data: true }); + var string = '{{hello world}}'; + var compileOptions = { data: true }; var helpers = { hello: function(noun, options) { @@ -203,17 +206,23 @@ describe('data', function() { } }; - var result = template( - { exclaim: true, world: 'world' }, - { helpers: helpers, data: { adjective: 'happy' } } - ); - equals('happy world!', result, 'Data output by helper'); + var input = { exclaim: true, world: 'world' }; + var runtimeOptions = { data: { adjective: 'happy' } }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .withRuntimeOptions(runtimeOptions) + .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 template = CompilerContext.compile('{{#hello}}{{world}}{{/hello}}', { + var string = '{{#hello}}{{world}}{{/hello}}'; + var compileOptions = { data: true - }); + }; var helpers = { hello: function(options) { @@ -224,18 +233,21 @@ describe('data', function() { } }; - var result = template( - { exclaim: true }, - { helpers: helpers, data: { adjective: 'happy' } } - ); - equals('happy world!', result, 'Data output by helper'); + var input = { exclaim: true }; + var runtimeOptions = { data: { adjective: 'happy' } }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .withRuntimeOptions(runtimeOptions) + .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 template = CompilerContext.compile( - '{{#hello}}{{world ../zomg}}{{/hello}}', - { data: true } - ); + var string = '{{#hello}}{{world ../zomg}}{{/hello}}'; + var compileOptions = { data: true }; var helpers = { hello: function(options) { @@ -246,18 +258,21 @@ describe('data', function() { } }; - var result = template( - { exclaim: true, zomg: 'world' }, - { helpers: helpers, data: { adjective: 'happy' } } - ); - equals('happy world?', result, 'Data output by helper'); + var input = { exclaim: true, zomg: 'world' }; + var runtimeOptions = { data: { adjective: 'happy' } }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .withRuntimeOptions(runtimeOptions) + .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 template = CompilerContext.compile( - '{{#hello}}{{world ../zomg}}{{/hello}}', - { data: true } - ); + var string = '{{#hello}}{{world ../zomg}}{{/hello}}'; + var compileOptions = { data: true }; var helpers = { hello: function(options) { @@ -268,18 +283,21 @@ describe('data', function() { } }; - var result = template( - { exclaim: true, zomg: 'world' }, - { helpers: helpers, data: { adjective: 'happy', accessData: '#win' } } - ); - equals('#win happy world?', result, 'Data output by helper'); + var input = { exclaim: true, zomg: 'world' }; + var runtimeOptions = { data: { adjective: 'happy', accessData: '#win' } }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .withRuntimeOptions(runtimeOptions) + .withMessage('Data output by helper') + .toCompileTo('#win happy world?'); }); it('you can override inherited data when invoking a helper', function() { - var template = CompilerContext.compile( - '{{#hello}}{{world zomg}}{{/hello}}', - { data: true } - ); + var string = '{{#hello}}{{world zomg}}{{/hello}}'; + var compileOptions = { data: true }; var helpers = { hello: function(options) { @@ -293,18 +311,21 @@ describe('data', function() { } }; - var result = template( - { exclaim: true, zomg: 'planet' }, - { helpers: helpers, data: { adjective: 'happy' } } - ); - equals('sad world?', result, 'Overriden data output by helper'); + var input = { exclaim: true, zomg: 'planet' }; + var runtimeOptions = { data: { adjective: 'happy' } }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .withRuntimeOptions(runtimeOptions) + .withMessage('Overriden data output by helper') + .toCompileTo('sad world?'); }); it('you can override inherited data when invoking a helper with depth', function() { - var template = CompilerContext.compile( - '{{#hello}}{{world ../zomg}}{{/hello}}', - { data: true } - ); + var string = '{{#hello}}{{world ../zomg}}{{/hello}}'; + var compileOptions = { data: true }; var helpers = { hello: function(options) { @@ -315,50 +336,63 @@ describe('data', function() { } }; - var result = template( - { exclaim: true, zomg: 'world' }, - { helpers: helpers, data: { adjective: 'happy' } } - ); - equals('sad world?', result, 'Overriden data output by helper'); + var input = { exclaim: true, zomg: 'world' }; + var runtimeOptions = { data: { adjective: 'happy' } }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .withRuntimeOptions(runtimeOptions) + .withMessage('Overriden data output by helper') + .toCompileTo('sad world?'); }); describe('@root', function() { it('the root context can be looked up via @root', function() { - var template = CompilerContext.compile('{{@root.foo}}'); - var result = template({ foo: 'hello' }, { data: {} }); - equals('hello', result); + var string = '{{@root.foo}}'; + var input = { foo: 'hello' }; + + expectTemplate(string) + .withInput(input) + .withRuntimeOptions({ data: {} }) + .toCompileTo('hello'); - result = template({ foo: 'hello' }, {}); - equals('hello', result); + expectTemplate(string) + .withInput(input) + .toCompileTo('hello'); }); it('passed root values take priority', function() { - var template = CompilerContext.compile('{{@root.foo}}'); - var result = template({}, { data: { root: { foo: 'hello' } } }); - equals('hello', result); + var string = '{{@root.foo}}'; + var runtimeOptions = { data: { root: { foo: 'hello' } } }; + expectTemplate(string) + .withRuntimeOptions(runtimeOptions) + .toCompileTo('hello'); }); }); describe('nesting', function() { it('the root context can be looked up via @root', function() { - var template = CompilerContext.compile( - '{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}' - ); - var result = template( - { foo: 'hello' }, - { - helpers: { - helper: function(options) { - var frame = Handlebars.createFrame(options.data); - frame.depth = options.data.depth + 1; - return options.fn(this, { data: frame }); - } - }, - data: { - depth: 0 - } + var string = + '{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}'; + var input = { foo: 'hello' }; + var helpers = { + 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 } - ); - equals('2 1 0', result); + }; + expectTemplate(string) + .withInput(input) + .withHelpers(helpers) + .withRuntimeOptions(runtimeOptions) + .toCompileTo('2 1 0'); }); }); }); diff --git a/spec/env/common.js b/spec/env/common.js index 0ecebcf45..5997322f5 100644 --- a/spec/env/common.js +++ b/spec/env/common.js @@ -146,11 +146,27 @@ HandlebarsTestBench.prototype.withHelper = function(name, helperFunction) { return this; }; +HandlebarsTestBench.prototype.withHelpers = function(helperFunctions) { + var self = this; + Object.keys(helperFunctions).forEach(function(name) { + self.withHelper(name, helperFunctions[name]); + }); + return this; +}; + HandlebarsTestBench.prototype.withPartial = function(name, partialAsString) { this.partials[name] = partialAsString; return this; }; +HandlebarsTestBench.prototype.withPartials = function(partials) { + var self = this; + Object.keys(partials).forEach(function(name) { + self.withPartial(name, partials[name]); + }); + return this; +}; + HandlebarsTestBench.prototype.withCompileOptions = function(compileOptions) { this.compileOptions = compileOptions; return this; diff --git a/spec/helpers.js b/spec/helpers.js index 60140a096..b3eefa236 100644 --- a/spec/helpers.js +++ b/spec/helpers.js @@ -92,9 +92,7 @@ describe('helpers', function() { it('helper for nested raw block throw exception when with missing closing braces', function() { var string = '{{{{a}}}} {{{{/a'; - shouldThrow(function() { - Handlebars.compile(string)(); - }); + expectTemplate(string).toThrow(); }); }); @@ -203,40 +201,36 @@ describe('helpers', function() { it('block helper', function() { var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!'; - var template = CompilerContext.compile(string); - - var result = template( - { world: 'world' }, - { - helpers: { - goodbyes: function(options) { - return options.fn({ text: 'GOODBYE' }); - } - } + + var input = { world: 'world' }; + var helpers = { + goodbyes: function(options) { + return options.fn({ text: 'GOODBYE' }); } - ); - equal(result, 'GOODBYE! cruel world!', 'Block helper executed'); + }; + + 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 template = CompilerContext.compile(string); - - var result = template( - { name: 'Yehuda' }, - { - helpers: { - form: function(options) { - return '
' + options.fn(this) + '
'; - } - } + + var input = { name: 'Yehuda' }; + var helpers = { + form: function(options) { + return '
' + options.fn(this) + '
'; } - ); - equal( - result, - '

Yehuda

', - 'Block helper executed with current context' - ); + }; + + expectTemplate(string) + .withInput(input) + .withHelpers(helpers) + .withMessage('Block helper executed with current context') + .toCompileTo('

Yehuda

'); }); it('block helper should have context in this', function() { @@ -265,67 +259,59 @@ describe('helpers', function() { it('block helper passing a new context', function() { var string = '{{#form yehuda}}

{{name}}

{{/form}}'; - var template = CompilerContext.compile(string); - - var result = template( - { yehuda: { name: 'Yehuda' } }, - { - helpers: { - form: function(context, options) { - return '
' + options.fn(context) + '
'; - } - } + + var input = { yehuda: { name: 'Yehuda' } }; + var helpers = { + form: function(context, options) { + return '
' + options.fn(context) + '
'; } - ); - equal(result, '

Yehuda

', 'Context variable resolved'); + }; + + expectTemplate(string) + .withInput(input) + .withHelpers(helpers) + .withMessage('Context variable resolved') + .toCompileTo('

Yehuda

'); }); it('block helper passing a complex path context', function() { var string = '{{#form yehuda/cat}}

{{name}}

{{/form}}'; - var template = CompilerContext.compile(string); - - var result = template( - { yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } }, - { - helpers: { - form: function(context, options) { - return '
' + options.fn(context) + '
'; - } - } + + var input = { yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } }; + var helpers = { + form: function(context, options) { + return '
' + options.fn(context) + '
'; } - ); - equal( - result, - '

Harold

', - 'Complex path variable resolved' - ); + }; + + expectTemplate(string) + .withInput(input) + .withHelpers(helpers) + .withMessage('Complex path variable resolved') + .toCompileTo('

Harold

'); }); it('nested block helpers', function() { var string = '{{#form yehuda}}

{{name}}

{{#link}}Hello{{/link}}{{/form}}'; - var template = CompilerContext.compile(string); - var result = template( - { - yehuda: { name: 'Yehuda' } + var input = { + yehuda: { name: 'Yehuda' } + }; + var helpers = { + link: function(options) { + return '' + options.fn(this) + ''; }, - { - helpers: { - link: function(options) { - return '' + options.fn(this) + ''; - }, - form: function(context, options) { - return '
' + options.fn(context) + '
'; - } - } + form: function(context, options) { + return '
' + options.fn(context) + '
'; } - ); - equal( - result, - '

Yehuda

Hello
', - 'Both blocks executed' - ); + }; + + expectTemplate(string) + .withInput(input) + .withHelpers(helpers) + .withMessage('Both blocks executed') + .toCompileTo('

Yehuda

Hello
'); }); it('block helper inverted sections', function() { @@ -608,9 +594,7 @@ describe('helpers', function() { it('using a quote in the middle of a parameter raises an error', function() { var string = 'Message: {{hello wo"rld"}}'; - shouldThrow(function() { - CompilerContext.compile(string); - }, Error); + expectTemplate(string).toThrow(Error); }); it('escaping a String is possible', function() { @@ -699,9 +683,7 @@ describe('helpers', function() { describe('hash', function() { it('helpers can take an optional hash', function() { - var template = CompilerContext.compile( - '{{goodbye cruel="CRUEL" world="WORLD" times=12}}' - ); + var string = '{{goodbye cruel="CRUEL" world="WORLD" times=12}}'; var helpers = { goodbye: function(options) { @@ -719,8 +701,11 @@ describe('helpers', function() { var context = {}; - var result = template(context, { helpers: helpers }); - equals(result, 'GOODBYE CRUEL WORLD 12 TIMES', 'Helper output hash'); + 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() { @@ -738,23 +723,21 @@ describe('helpers', function() { var context = {}; - var template = CompilerContext.compile( - '{{goodbye cruel="CRUEL" world="WORLD" print=true}}' - ); - var result = template(context, { helpers: helpers }); - equals(result, 'GOODBYE CRUEL WORLD', 'Helper output hash'); - - template = CompilerContext.compile( - '{{goodbye cruel="CRUEL" world="WORLD" print=false}}' - ); - result = template(context, { helpers: helpers }); - equals(result, 'NOT PRINTING', 'Boolean helper parameter honored'); + expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" print=true}}') + .withHelpers(helpers) + .withInput(context) + .withMessage('Helper output hash') + .toCompileTo('GOODBYE CRUEL WORLD'); + + expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" print=false}}') + .withHelpers(helpers) + .withInput(context) + .withMessage('Boolean helper parameter honored') + .toCompileTo('NOT PRINTING'); }); it('block helpers can take an optional hash', function() { - var template = CompilerContext.compile( - '{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}' - ); + var string = '{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}'; var helpers = { goodbye: function(options) { @@ -770,14 +753,14 @@ describe('helpers', function() { } }; - var result = template({}, { helpers: helpers }); - equals(result, 'GOODBYE CRUEL world 12 TIMES', 'Hash parameters output'); + 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 template = CompilerContext.compile( - '{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}' - ); + var string = '{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}'; var helpers = { goodbye: function(options) { @@ -793,8 +776,10 @@ describe('helpers', function() { } }; - var result = template({}, { helpers: helpers }); - equals(result, 'GOODBYE CRUEL world 12 TIMES', 'Hash parameters output'); + 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() { @@ -810,28 +795,21 @@ describe('helpers', function() { } }; - var template = CompilerContext.compile( - '{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}' - ); - var result = template({}, { helpers: helpers }); - equals(result, 'GOODBYE CRUEL world', 'Boolean hash parameter honored'); + expectTemplate('{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}') + .withHelpers(helpers) + .withMessage('Boolean hash parameter honored') + .toCompileTo('GOODBYE CRUEL world'); - template = CompilerContext.compile( - '{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}' - ); - result = template({}, { helpers: helpers }); - equals(result, 'NOT PRINTING', 'Boolean hash parameter honored'); + expectTemplate('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}') + .withHelpers(helpers) + .withMessage('Boolean hash parameter honored') + .toCompileTo('NOT PRINTING'); }); }); describe('helperMissing', function() { it('if a context is not found, helperMissing is used', function() { - shouldThrow( - function() { - var template = CompilerContext.compile('{{hello}} {{link_to world}}'); - template({}); - }, - undefined, + expectTemplate('{{hello}} {{link_to world}}').toThrow( /Missing helper: "link_to"/ ); }); @@ -869,92 +847,110 @@ describe('helpers', function() { describe('knownHelpers', function() { it('Known helper should render helper', function() { - var template = CompilerContext.compile('{{hello}}', { + var string = '{{hello}}'; + var compileOptions = { knownHelpers: { hello: true } - }); + }; - var result = template( - {}, - { - helpers: { - hello: function() { - return 'foo'; - } - } + var helpers = { + hello: function() { + return 'foo'; } - ); - equal(result, 'foo', "'foo' should === '" + result); + }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .toCompileTo('foo'); }); it('Unknown helper in knownHelpers only mode should be passed as undefined', function() { - var template = CompilerContext.compile('{{typeof hello}}', { + var string = '{{typeof hello}}'; + var compileOptions = { knownHelpers: { typeof: true }, knownHelpersOnly: true - }); + }; - var result = template( - {}, - { - helpers: { - typeof: function(arg) { - return typeof arg; - }, - hello: function() { - return 'foo'; - } - } + var helpers = { + typeof: function(arg) { + return typeof arg; + }, + hello: function() { + return 'foo'; } - ); - equal(result, 'undefined', "'undefined' should === '" + result); + }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .toCompileTo('undefined'); }); it('Builtin helpers available in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{#unless foo}}bar{{/unless}}', { + var string = '{{#unless foo}}bar{{/unless}}'; + var compileOptions = { knownHelpersOnly: true - }); + }; - var result = template({}); - equal(result, 'bar', "'bar' should === '" + result); + expectTemplate(string) + .withCompileOptions(compileOptions) + .toCompileTo('bar'); }); it('Field lookup works in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{foo}}', { + var string = '{{foo}}'; + var compileOptions = { knownHelpersOnly: true - }); + }; - var result = template({ foo: 'bar' }); - equal(result, 'bar', "'bar' should === '" + result); + var input = { foo: 'bar' }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withInput(input) + .toCompileTo('bar'); }); it('Conditional blocks work in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{#foo}}bar{{/foo}}', { + var string = '{{#foo}}bar{{/foo}}'; + var compileOptions = { knownHelpersOnly: true - }); + }; - var result = template({ foo: 'baz' }); - equal(result, 'bar', "'bar' should === '" + result); + var input = { foo: 'baz' }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withInput(input) + .toCompileTo('bar'); }); it('Invert blocks work in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{^foo}}bar{{/foo}}', { + var string = '{{^foo}}bar{{/foo}}'; + var compileOptions = { knownHelpersOnly: true - }); + }; - var result = template({ foo: false }); - equal(result, 'bar', "'bar' should === '" + result); + var input = { foo: false }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withInput(input) + .toCompileTo('bar'); }); it('Functions are bound to the context in knownHelpers only mode', function() { - var template = CompilerContext.compile('{{foo}}', { + var string = '{{foo}}'; + var compileOptions = { knownHelpersOnly: true - }); - var result = template({ + }; + var input = { foo: function() { return this.bar; }, bar: 'bar' - }); - equal(result, 'bar', "'bar' should === '" + result); + }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withInput(input) + .toCompileTo('bar'); }); it('Unknown helper call in knownHelpers only mode should throw', function() { - shouldThrow(function() { - CompilerContext.compile('{{typeof hello}}', { knownHelpersOnly: true }); - }, Error); + expectTemplate('{{typeof hello}}') + .withCompileOptions({ knownHelpersOnly: true }) + .toThrow(Error); }); }); @@ -1024,12 +1020,16 @@ describe('helpers', function() { ); }); it('should include in known helper calls', function() { - var template = CompilerContext.compile('{{helper}}', { + var string = '{{helper}}'; + var compileOptions = { knownHelpers: { helper: true }, knownHelpersOnly: true - }); + }; - equal(template({}, { helpers: helpers }), 'ran: helper'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .toCompileTo('ran: helper'); }); it('should include full id', function() { @@ -1051,7 +1051,7 @@ describe('helpers', function() { describe('name conflicts', function() { it('helpers take precedence over same-named context properties', function() { - var template = CompilerContext.compile('{{goodbye}} {{cruel world}}'); + var string = '{{goodbye}} {{cruel world}}'; var helpers = { goodbye: function() { @@ -1068,14 +1068,15 @@ describe('helpers', function() { world: 'world' }; - var result = template(context, { helpers: helpers }); - equals(result, 'GOODBYE cruel WORLD', 'Helper executed'); + expectTemplate(string) + .withHelpers(helpers) + .withInput(context) + .withMessage('Helper executed') + .toCompileTo('GOODBYE cruel WORLD'); }); it('helpers take precedence over same-named context properties$', function() { - var template = CompilerContext.compile( - '{{#goodbye}} {{cruel world}}{{/goodbye}}' - ); + var string = '{{#goodbye}} {{cruel world}}{{/goodbye}}'; var helpers = { goodbye: function(options) { @@ -1092,14 +1093,15 @@ describe('helpers', function() { world: 'world' }; - var result = template(context, { helpers: helpers }); - equals(result, 'GOODBYE cruel WORLD', 'Helper executed'); + expectTemplate(string) + .withHelpers(helpers) + .withInput(context) + .withMessage('Helper executed') + .toCompileTo('GOODBYE cruel WORLD'); }); it('Scoped names take precedence over helpers', function() { - var template = CompilerContext.compile( - '{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}' - ); + var string = '{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}'; var helpers = { goodbye: function() { @@ -1116,18 +1118,15 @@ describe('helpers', function() { world: 'world' }; - var result = template(context, { helpers: helpers }); - equals( - result, - 'goodbye cruel WORLD cruel GOODBYE', - 'Helper not executed' - ); + expectTemplate(string) + .withHelpers(helpers) + .withInput(context) + .withMessage('Helper not executed') + .toCompileTo('goodbye cruel WORLD cruel GOODBYE'); }); it('Scoped names take precedence over block helpers', function() { - var template = CompilerContext.compile( - '{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}' - ); + var string = '{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}'; var helpers = { goodbye: function(options) { @@ -1144,8 +1143,11 @@ describe('helpers', function() { world: 'world' }; - var result = template(context, { helpers: helpers }); - equals(result, 'GOODBYE cruel WORLD goodbye', 'Helper executed'); + expectTemplate(string) + .withHelpers(helpers) + .withInput(context) + .withMessage('Helper executed') + .toCompileTo('GOODBYE cruel WORLD goodbye'); }); }); @@ -1237,95 +1239,43 @@ describe('helpers', function() { describe('built-in helpers malformed arguments ', function() { it('if helper - too few arguments', function() { - var template = CompilerContext.compile('{{#if}}{{/if}}'); - shouldThrow( - function() { - template({}); - }, - undefined, - /#if requires exactly one argument/ - ); + var string = '{{#if}}{{/if}}'; + expectTemplate(string).toThrow(/#if requires exactly one argument/); }); it('if helper - too many arguments, string', function() { - var template = CompilerContext.compile('{{#if test "string"}}{{/if}}'); - shouldThrow( - function() { - template({}); - }, - undefined, - /#if requires exactly one argument/ - ); + var string = '{{#if test "string"}}{{/if}}'; + expectTemplate(string).toThrow(/#if requires exactly one argument/); }); it('if helper - too many arguments, undefined', function() { - var template = CompilerContext.compile('{{#if test undefined}}{{/if}}'); - shouldThrow( - function() { - template({}); - }, - undefined, - /#if requires exactly one argument/ - ); + var string = '{{#if test undefined}}{{/if}}'; + expectTemplate(string).toThrow(/#if requires exactly one argument/); }); it('if helper - too many arguments, null', function() { - var template = CompilerContext.compile('{{#if test null}}{{/if}}'); - shouldThrow( - function() { - template({}); - }, - undefined, - /#if requires exactly one argument/ - ); + var string = '{{#if test null}}{{/if}}'; + expectTemplate(string).toThrow(/#if requires exactly one argument/); }); it('unless helper - too few arguments', function() { - var template = CompilerContext.compile('{{#unless}}{{/unless}}'); - shouldThrow( - function() { - template({}); - }, - undefined, - /#unless requires exactly one argument/ - ); + var string = '{{#unless}}{{/unless}}'; + expectTemplate(string).toThrow(/#unless requires exactly one argument/); }); it('unless helper - too many arguments', function() { - var template = CompilerContext.compile( - '{{#unless test null}}{{/unless}}' - ); - shouldThrow( - function() { - template({}); - }, - undefined, - /#unless requires exactly one argument/ - ); + var string = '{{#unless test null}}{{/unless}}'; + expectTemplate(string).toThrow(/#unless requires exactly one argument/); }); it('with helper - too few arguments', function() { - var template = CompilerContext.compile('{{#with}}{{/with}}'); - shouldThrow( - function() { - template({}); - }, - undefined, - /#with requires exactly one argument/ - ); + var string = '{{#with}}{{/with}}'; + expectTemplate(string).toThrow(/#with requires exactly one argument/); }); it('with helper - too many arguments', function() { - var template = CompilerContext.compile( - '{{#with test "string"}}{{/with}}' - ); - shouldThrow( - function() { - template({}); - }, - undefined, - /#with requires exactly one argument/ - ); + var string = '{{#with test "string"}}{{/with}}'; + expectTemplate(string).toThrow(/#with requires exactly one argument/); }); }); diff --git a/spec/partials.js b/spec/partials.js index d00d4147e..02dffc5a5 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -63,18 +63,11 @@ describe('partials', function() { return 'missing'; } }; - shouldThrow( - function() { - shouldCompileToWithPartials( - string, - [hash, helpers, { dude: partial }], - true, - 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' - ); - }, - Handlebars.Exception, - 'The partial missing could not be found' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .withPartial('dude', partial) + .toThrow(Handlebars.Exception, 'The partial missing could not be found'); }); it('partials with context', function() { @@ -142,10 +135,7 @@ describe('partials', function() { }); it('partials with duplicate parameters', function() { - shouldThrow( - function() { - CompilerContext.compile('Dudes: {{>dude dudes foo bar=baz}}'); - }, + expectTemplate('Dudes: {{>dude dudes foo bar=baz}}').toThrow( Error, 'Unsupported number of partial arguments: 2 - 1:7' ); @@ -190,11 +180,7 @@ describe('partials', function() { }); it('rendering undefined partial throws an exception', function() { - shouldThrow( - function() { - var template = CompilerContext.compile('{{> whatever}}'); - template(); - }, + expectTemplate('{{> whatever}}').toThrow( Handlebars.Exception, 'The partial whatever could not be found' ); @@ -212,11 +198,7 @@ describe('partials', function() { }); it('rendering template partial in vm mode throws an exception', function() { - shouldThrow( - function() { - var template = CompilerContext.compile('{{> whatever}}'); - template(); - }, + expectTemplate('{{> whatever}}').toThrow( Handlebars.Exception, 'The partial whatever could not be found' ); @@ -620,17 +602,9 @@ describe('partials', function() { {}, 'success' ); - shouldThrow( - function() { - shouldCompileTo( - '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}', - {}, - 'success' - ); - }, - Error, - /myPartial could not/ - ); + expectTemplate( + '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}' + ).toThrow(Error, /myPartial could not/); }); it('should override global partials', function() { shouldCompileTo( diff --git a/spec/regressions.js b/spec/regressions.js index f6147ad6e..aaa89cef7 100644 --- a/spec/regressions.js +++ b/spec/regressions.js @@ -104,16 +104,12 @@ describe('Regressions', function() { { name: 'Jane Doe', location: { city: 'New York' } } ]; - var template = CompilerContext.compile( - '{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}' - ); + var string = '{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}'; - var result = template(context); - equals( - result, - 'John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe', - 'It should output multiple times' - ); + expectTemplate(string) + .withInput(context) + .withMessage('It should output multiple times') + .toCompileTo('John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe'); }); it('GS-428: Nested if else rendering', function() { @@ -154,12 +150,8 @@ describe('Regressions', function() { }); it('GH-437: Matching escaping', function() { - shouldThrow(function() { - CompilerContext.compile('{{{a}}'); - }, Error); - shouldThrow(function() { - CompilerContext.compile('{{a}}}'); - }, Error); + expectTemplate('{{{a}}').toThrow(Error, /Parse error on/); + expectTemplate('{{a}}}').toThrow(Error, /Parse error on/); }); it('GH-676: Using array in escaping mustache fails', function() { @@ -216,12 +208,12 @@ describe('Regressions', function() { notData: [1] }; - var template = CompilerContext.compile( - '{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}' - ); + var string = + '{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}'; - var result = template(context); - equals(result, 'foo'); + expectTemplate(string) + .withInput(context) + .toCompileTo('foo'); }); it('GH-1021: Each empty string key', function() { diff --git a/spec/security.js b/spec/security.js index 1b345f0cc..68a60e15d 100644 --- a/spec/security.js +++ b/spec/security.js @@ -60,18 +60,10 @@ describe('security issues', function() { describe('without the option "allowExplicitCallOfHelperMissing"', function() { it('should throw an exception when calling "{{helperMissing}}" ', function() { - shouldThrow(function() { - var template = Handlebars.compile('{{helperMissing}}'); - template({}); - }, Error); + expectTemplate('{{helperMissing}}').toThrow(Error); }); it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { - shouldThrow(function() { - var template = Handlebars.compile( - '{{#helperMissing}}{{/helperMissing}}' - ); - template({}); - }, Error); + expectTemplate('{{#helperMissing}}{{/helperMissing}}').toThrow(Error); }); it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { var functionCalls = []; @@ -86,16 +78,14 @@ describe('security issues', function() { expect(functionCalls.length).to.equal(0); }); it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { - shouldThrow(function() { - var template = Handlebars.compile( - '{{#blockHelperMissing .}}{{/blockHelperMissing}}' - ); - template({ - fn: function() { - return 'functionInData'; - } - }); - }, Error); + var input = { + fn: function() { + return 'functionInData'; + } + }; + expectTemplate('{{#blockHelperMissing .}}{{/blockHelperMissing}}') + .withInput(input) + .toThrow(Error); }); }); diff --git a/spec/strict.js b/spec/strict.js index 680bbc0d8..0e02afe2e 100644 --- a/spec/strict.js +++ b/spec/strict.js @@ -3,140 +3,88 @@ var Exception = Handlebars.Exception; describe('strict', function() { describe('strict mode', function() { it('should error on missing property lookup', function() { - shouldThrow( - function() { - var template = CompilerContext.compile('{{hello}}', { strict: true }); - - template({}); - }, - Exception, - /"hello" not defined in/ - ); + expectTemplate('{{hello}}') + .withCompileOptions({ strict: true }) + .toThrow(Exception, /"hello" not defined in/); }); it('should error on missing child', function() { - var template = CompilerContext.compile('{{hello.bar}}', { strict: true }); - equals(template({ hello: { bar: 'foo' } }), 'foo'); + expectTemplate('{{hello.bar}}') + .withCompileOptions({ strict: true }) + .withInput({ hello: { bar: 'foo' } }) + .toCompileTo('foo'); - shouldThrow( - function() { - template({ hello: {} }); - }, - Exception, - /"bar" not defined in/ - ); + expectTemplate('{{hello.bar}}') + .withCompileOptions({ strict: true }) + .withInput({ hello: {} }) + .toThrow(Exception, /"bar" not defined in/); }); it('should handle explicit undefined', function() { - var template = CompilerContext.compile('{{hello.bar}}', { strict: true }); - - equals(template({ hello: { bar: undefined } }), ''); + expectTemplate('{{hello.bar}}') + .withCompileOptions({ strict: true }) + .withInput({ hello: { bar: undefined } }) + .toCompileTo(''); }); it('should error on missing property lookup in known helpers mode', function() { - shouldThrow( - function() { - var template = CompilerContext.compile('{{hello}}', { - strict: true, - knownHelpersOnly: true - }); - - template({}); - }, - Exception, - /"hello" not defined in/ - ); + expectTemplate('{{hello}}') + .withCompileOptions({ + strict: true, + knownHelpersOnly: true + }) + .toThrow(Exception, /"hello" not defined in/); }); it('should error on missing context', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{hello}}', { strict: true }); - - template(); - }, Error); + expectTemplate('{{hello}}') + .withCompileOptions({ strict: true }) + .toThrow(Error); }); it('should error on missing data lookup', function() { - var template = CompilerContext.compile('{{@hello}}', { strict: true }); - equals(template(undefined, { data: { hello: 'foo' } }), 'foo'); + var xt = expectTemplate('{{@hello}}').withCompileOptions({ + strict: true + }); + + xt.toThrow(Error); - shouldThrow(function() { - template(); - }, Error); + xt.withRuntimeOptions({ data: { hello: 'foo' } }).toCompileTo('foo'); }); it('should not run helperMissing for helper calls', function() { - shouldThrow( - function() { - var template = CompilerContext.compile('{{hello foo}}', { - strict: true - }); - - template({ foo: true }); - }, - Exception, - /"hello" not defined in/ - ); - - shouldThrow( - function() { - var template = CompilerContext.compile('{{#hello foo}}{{/hello}}', { - strict: true - }); - - template({ foo: true }); - }, - Exception, - /"hello" not defined in/ - ); + expectTemplate('{{hello foo}}') + .withCompileOptions({ strict: true }) + .withInput({ foo: true }) + .toThrow(Exception, /"hello" not defined in/); + + expectTemplate('{{#hello foo}}{{/hello}}') + .withCompileOptions({ strict: true }) + .withInput({ foo: true }) + .toThrow(Exception, /"hello" not defined in/); }); it('should throw on ambiguous blocks', function() { - shouldThrow( - function() { - var template = CompilerContext.compile('{{#hello}}{{/hello}}', { - strict: true - }); - - template({}); - }, - Exception, - /"hello" not defined in/ - ); - - shouldThrow( - function() { - var template = CompilerContext.compile('{{^hello}}{{/hello}}', { - strict: true - }); - - template({}); - }, - Exception, - /"hello" not defined in/ - ); - - shouldThrow( - function() { - var template = CompilerContext.compile( - '{{#hello.bar}}{{/hello.bar}}', - { strict: true } - ); - - template({ hello: {} }); - }, - Exception, - /"bar" not defined in/ - ); + expectTemplate('{{#hello}}{{/hello}}') + .withCompileOptions({ strict: true }) + .toThrow(Exception, /"hello" not defined in/); + + expectTemplate('{{^hello}}{{/hello}}') + .withCompileOptions({ strict: true }) + .toThrow(Exception, /"hello" not defined in/); + + expectTemplate('{{#hello.bar}}{{/hello.bar}}') + .withCompileOptions({ strict: true }) + .withInput({ hello: {} }) + .toThrow(Exception, /"bar" not defined in/); }); it('should allow undefined parameters when passed to helpers', function() { - var template = CompilerContext.compile( - '{{#unless foo}}success{{/unless}}', - { strict: true } - ); - equals(template({}), 'success'); + expectTemplate('{{#unless foo}}success{{/unless}}') + .withCompileOptions({ strict: true }) + .toCompileTo('success'); }); it('should allow undefined hash when passed to helpers', function() { - var template = CompilerContext.compile('{{helper value=@foo}}', { + var string = '{{helper value=@foo}}'; + var compileOptions = { strict: true - }); + }; var helpers = { helper: function(options) { equals('value' in options.hash, true); @@ -144,20 +92,16 @@ describe('strict', function() { return 'success'; } }; - equals(template({}, { helpers: helpers }), 'success'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .toCompileTo('success'); }); it('should show error location on missing property lookup', function() { - shouldThrow( - function() { - var template = CompilerContext.compile('\n\n\n {{hello}}', { - strict: true - }); - template({}); - }, - Exception, - '"hello" not defined in [object Object] - 4:5' - ); + expectTemplate('\n\n\n {{hello}}') + .withCompileOptions({ strict: true }) + .toThrow(Exception, '"hello" not defined in [object Object] - 4:5'); }); it('should error contains correct location properties on missing property lookup', function() { @@ -177,54 +121,39 @@ describe('strict', function() { describe('assume objects', function() { it('should ignore missing property', function() { - var template = CompilerContext.compile('{{hello}}', { - assumeObjects: true - }); - - equal(template({}), ''); + expectTemplate('{{hello}}') + .withCompileOptions({ assumeObjects: true }) + .toCompileTo(''); }); it('should ignore missing child', function() { - var template = CompilerContext.compile('{{hello.bar}}', { - assumeObjects: true - }); - - equal(template({ hello: {} }), ''); + expectTemplate('{{hello.bar}}') + .withCompileOptions({ assumeObjects: true }) + .withInput({ hello: {} }) + .toCompileTo(''); }); it('should error on missing object', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{hello.bar}}', { - assumeObjects: true - }); - - template({}); - }, Error); + expectTemplate('{{hello.bar}}') + .withCompileOptions({ assumeObjects: true }) + .toThrow(Error); }); it('should error on missing context', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{hello}}', { - assumeObjects: true - }); - - template(); - }, Error); + expectTemplate('{{hello}}') + .withCompileOptions({ assumeObjects: true }) + .withInput(undefined) + .toThrow(Error); }); it('should error on missing data lookup', function() { - shouldThrow(function() { - var template = CompilerContext.compile('{{@hello.bar}}', { - assumeObjects: true - }); - - template(); - }, Error); + expectTemplate('{{@hello.bar}}') + .withCompileOptions({ assumeObjects: true }) + .withInput(undefined) + .toThrow(Error); }); it('should execute blockHelperMissing', function() { - var template = CompilerContext.compile('{{^hello}}foo{{/hello}}', { - assumeObjects: true - }); - - equals(template({}), 'foo'); + expectTemplate('{{^hello}}foo{{/hello}}') + .withCompileOptions({ assumeObjects: true }) + .toCompileTo('foo'); }); }); }); diff --git a/spec/string-params.js b/spec/string-params.js index 9f83611a4..0ce211ffc 100644 --- a/spec/string-params.js +++ b/spec/string-params.js @@ -1,8 +1,9 @@ describe('string params mode', function() { it('arguments to helpers can be retrieved from options hash in string form', function() { - var template = CompilerContext.compile('{{wycats is.a slave.driver}}', { + var string = '{{wycats is.a slave.driver}}'; + var compileOptions = { stringParams: true - }); + }; var helpers = { wycats: function(passiveVoice, noun) { @@ -10,20 +11,16 @@ describe('string params mode', function() { } }; - var result = template({}, { helpers: helpers }); - - equals( - result, - 'HELP ME MY BOSS is.a slave.driver', - 'String parameters output' - ); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withMessage('String parameters output') + .toCompileTo('HELP ME MY BOSS is.a slave.driver'); }); it('when using block form, arguments to helpers can be retrieved from options hash in string form', function() { - var template = CompilerContext.compile( - '{{#wycats is.a slave.driver}}help :({{/wycats}}', - { stringParams: true } - ); + var string = '{{#wycats is.a slave.driver}}help :({{/wycats}}'; + var compileOptions = { stringParams: true }; var helpers = { wycats: function(passiveVoice, noun, options) { @@ -38,20 +35,16 @@ describe('string params mode', function() { } }; - var result = template({}, { helpers: helpers }); - - equals( - result, - 'HELP ME MY BOSS is.a slave.driver: help :(', - 'String parameters output' - ); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withMessage('String parameters output') + .toCompileTo('HELP ME MY BOSS is.a slave.driver: help :('); }); it('when inside a block in String mode, .. passes the appropriate context in the options hash', function() { - var template = CompilerContext.compile( - '{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}', - { stringParams: true } - ); + var string = '{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}'; + var compileOptions = { stringParams: true }; var helpers = { tomdale: function(desire, noun, options) { @@ -68,27 +61,23 @@ describe('string params mode', function() { } }; - var result = template( - { - dale: {}, + var input = { + dale: {}, - need: 'need-a' - }, - { helpers: helpers } - ); - - equals( - result, - 'STOP ME FROM READING HACKER NEWS I need-a dad.joke', - 'Proper context variable output' - ); + need: 'need-a' + }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .withMessage('Proper context variable output') + .toCompileTo('STOP ME FROM READING HACKER NEWS I need-a dad.joke'); }); it('information about the types is passed along', function() { - var template = CompilerContext.compile( - "{{tomdale 'need' dad.joke true false}}", - { stringParams: true } - ); + var string = "{{tomdale 'need' dad.joke true false}}"; + var compileOptions = { stringParams: true }; var helpers = { tomdale: function(desire, noun, trueBool, falseBool, options) { @@ -111,15 +100,15 @@ describe('string params mode', function() { } }; - var result = template({}, { helpers: helpers }); - equal(result, 'Helper called'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .toCompileTo('Helper called'); }); it('hash parameters get type information', function() { - var template = CompilerContext.compile( - "{{tomdale he.says desire='need' noun=dad.joke bool=true}}", - { stringParams: true } - ); + var string = "{{tomdale he.says desire='need' noun=dad.joke bool=true}}"; + var compileOptions = { stringParams: true }; var helpers = { tomdale: function(exclamation, options) { @@ -136,15 +125,16 @@ describe('string params mode', function() { } }; - var result = template({}, { helpers: helpers }); - equal(result, 'Helper called'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .toCompileTo('Helper called'); }); it('hash parameters get context information', function() { - var template = CompilerContext.compile( - "{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}", - { stringParams: true } - ); + var string = + "{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}"; + var compileOptions = { stringParams: true }; var context = { dale: {} }; @@ -165,15 +155,17 @@ describe('string params mode', function() { } }; - var result = template(context, { helpers: helpers }); - equal(result, 'Helper called'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(context) + .toCompileTo('Helper called'); }); it('when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper', function() { - var template = CompilerContext.compile( - '{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}', - { stringParams: true } - ); + var string = + '{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}'; + var compileOptions = { stringParams: true }; var helpers = { tomdale: function(desire, noun, options) { @@ -192,27 +184,24 @@ describe('string params mode', function() { } }; - var result = template( - { - dale: {}, + var input = { + dale: {}, - need: 'need-a' - }, - { helpers: helpers } - ); - - equals( - result, - 'STOP ME FROM READING HACKER NEWS I need-a dad.joke wot', - 'Proper context variable output' - ); + need: 'need-a' + }; + + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .withMessage('Proper context variable output') + .toCompileTo('STOP ME FROM READING HACKER NEWS I need-a dad.joke wot'); }); it('with nested block ambiguous', function() { - var template = CompilerContext.compile( - '{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}', - { stringParams: true } - ); + var string = + '{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}'; + var compileOptions = { stringParams: true }; var helpers = { with: function() { @@ -223,14 +212,15 @@ describe('string params mode', function() { } }; - var result = template({}, { helpers: helpers }); - equals(result, 'WITH'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .toCompileTo('WITH'); }); it('should handle DATA', function() { - var template = CompilerContext.compile('{{foo @bar}}', { - stringParams: true - }); + var string = '{{foo @bar}}'; + var compileOptions = { stringParams: true }; var helpers = { foo: function(bar, options) { @@ -240,7 +230,9 @@ describe('string params mode', function() { } }; - var result = template({}, { helpers: helpers }); - equal(result, 'Foo!'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .toCompileTo('Foo!'); }); }); diff --git a/spec/subexpressions.js b/spec/subexpressions.js index 228510511..584278831 100644 --- a/spec/subexpressions.js +++ b/spec/subexpressions.js @@ -189,10 +189,8 @@ describe('subexpressions', function() { }); it('in string params mode,', function() { - var template = CompilerContext.compile( - '{{snog (blorg foo x=y) yeah a=b}}', - { stringParams: true } - ); + var string = '{{snog (blorg foo x=y) yeah a=b}}'; + var compileOptions = { stringParams: true }; var helpers = { snog: function(a, b, options) { @@ -230,21 +228,21 @@ describe('subexpressions', function() { } }; - var result = template( - { - foo: {}, - yeah: {} - }, - { helpers: helpers } - ); + var input = { + foo: {}, + yeah: {} + }; - equals(result, 'fooyeah'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('fooyeah'); }); it('as hashes in string params mode', function() { - var template = CompilerContext.compile('{{blog fun=(bork)}}', { - stringParams: true - }); + var string = '{{blog fun=(bork)}}'; + var compileOptions = { stringParams: true }; var helpers = { blog: function(options) { @@ -256,8 +254,10 @@ describe('subexpressions', function() { } }; - var result = template({}, { helpers: helpers }); - equals(result, 'val is BORK'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .toCompileTo('val is BORK'); }); it('subexpression functions on the context', function() { @@ -285,8 +285,9 @@ describe('subexpressions', function() { return val + val; } }; - shouldThrow(function() { - shouldCompileTo(string, [context, helpers], 'LOLLOL!'); - }); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toThrow(); }); }); diff --git a/spec/track-ids.js b/spec/track-ids.js index f7ad7abda..72713d43b 100644 --- a/spec/track-ids.js +++ b/spec/track-ids.js @@ -5,7 +5,7 @@ describe('track ids', function() { }); it('should not include anything without the flag', function() { - var template = CompilerContext.compile('{{wycats is.a slave.driver}}'); + var string = '{{wycats is.a slave.driver}}'; var helpers = { wycats: function(passiveVoice, noun, options) { @@ -16,12 +16,13 @@ describe('track ids', function() { } }; - equals(template({}, { helpers: helpers }), 'success'); + expectTemplate(string) + .withHelpers(helpers) + .toCompileTo('success'); }); it('should include argument ids', function() { - var template = CompilerContext.compile('{{wycats is.a slave.driver}}', { - trackIds: true - }); + var string = '{{wycats is.a slave.driver}}'; + var compileOptions = { trackIds: true }; var helpers = { wycats: function(passiveVoice, noun, options) { @@ -41,16 +42,15 @@ describe('track ids', function() { } }; - equals( - template(context, { helpers: helpers }), - 'HELP ME MY BOSS is.a:foo slave.driver:bar' - ); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(context) + .toCompileTo('HELP ME MY BOSS is.a:foo slave.driver:bar'); }); it('should include hash ids', function() { - var template = CompilerContext.compile( - '{{wycats bat=is.a baz=slave.driver}}', - { trackIds: true } - ); + var string = '{{wycats bat=is.a baz=slave.driver}}'; + var compileOptions = { trackIds: true }; var helpers = { wycats: function(options) { @@ -70,16 +70,15 @@ describe('track ids', function() { } }; - equals( - template(context, { helpers: helpers }), - 'HELP ME MY BOSS is.a:foo slave.driver:bar' - ); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(context) + .toCompileTo('HELP ME MY BOSS is.a:foo slave.driver:bar'); }); it('should note ../ and ./ references', function() { - var template = CompilerContext.compile( - '{{wycats ./is.a ../slave.driver this.is.a this}}', - { trackIds: true } - ); + var string = '{{wycats ./is.a ../slave.driver this.is.a this}}'; + var compileOptions = { trackIds: true }; var helpers = { wycats: function(passiveVoice, noun, thiz, thiz2, options) { @@ -101,15 +100,15 @@ describe('track ids', function() { } }; - equals( - template(context, { helpers: helpers }), - 'HELP ME MY BOSS is.a:foo ../slave.driver:undefined' - ); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(context) + .toCompileTo('HELP ME MY BOSS is.a:foo ../slave.driver:undefined'); }); it('should note @data references', function() { - var template = CompilerContext.compile('{{wycats @is.a @slave.driver}}', { - trackIds: true - }); + var string = '{{wycats @is.a @slave.driver}}'; + var compileOptions = { trackIds: true }; var helpers = { wycats: function(passiveVoice, noun, options) { @@ -129,16 +128,16 @@ describe('track ids', function() { } }; - equals( - template({}, { helpers: helpers, data: context }), - 'HELP ME MY BOSS @is.a:foo @slave.driver:bar' - ); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withRuntimeOptions({ data: context }) + .toCompileTo('HELP ME MY BOSS @is.a:foo @slave.driver:bar'); }); it('should return null for constants', function() { - var template = CompilerContext.compile('{{wycats 1 "foo" key=false}}', { - trackIds: true - }); + var string = '{{wycats 1 "foo" key=false}}'; + var compileOptions = { trackIds: true }; var helpers = { wycats: function(passiveVoice, noun, options) { @@ -157,15 +156,15 @@ describe('track ids', function() { } }; - equals( - template(context, { helpers: helpers }), - 'HELP ME MY BOSS 1 foo false' - ); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(context) + .toCompileTo('HELP ME MY BOSS 1 foo false'); }); it('should return true for subexpressions', function() { - var template = CompilerContext.compile('{{wycats (sub)}}', { - trackIds: true - }); + var string = '{{wycats (sub)}}'; + var compileOptions = { trackIds: true }; var helpers = { sub: function() { @@ -178,14 +177,16 @@ describe('track ids', function() { } }; - equals(template(context, { helpers: helpers }), 'HELP ME MY BOSS 1'); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(context) + .toCompileTo('HELP ME MY BOSS 1'); }); it('should use block param paths', function() { - var template = CompilerContext.compile( - '{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}', - { trackIds: true } - ); + var string = '{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}'; + var compileOptions = { trackIds: true }; var helpers = { doIt: function(options) { @@ -211,10 +212,11 @@ describe('track ids', function() { } }; - equals( - template(context, { helpers: helpers }), - 'HELP ME MY BOSS zomg.a:foo slave.driver:bar' - ); + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(context) + .toCompileTo('HELP ME MY BOSS zomg.a:foo slave.driver:bar'); }); describe('builtin helpers', function() { @@ -229,119 +231,109 @@ describe('track ids', function() { describe('#each', function() { it('should track contextPath for arrays', function() { - var template = CompilerContext.compile( - '{{#each array}}{{wycats name}}{{/each}}', - { trackIds: true } - ); - - equals( - template( - { array: [{ name: 'foo' }, { name: 'bar' }] }, - { helpers: helpers } - ), - 'foo:array.0\nbar:array.1\n' - ); + var string = '{{#each array}}{{wycats name}}{{/each}}'; + var compileOptions = { trackIds: true }; + + var input = { array: [{ name: 'foo' }, { name: 'bar' }] }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('foo:array.0\nbar:array.1\n'); }); it('should track contextPath for keys', function() { - var template = CompilerContext.compile( - '{{#each object}}{{wycats name}}{{/each}}', - { trackIds: true } - ); - - equals( - template( - { object: { foo: { name: 'foo' }, bar: { name: 'bar' } } }, - { helpers: helpers } - ), - 'foo:object.foo\nbar:object.bar\n' - ); + var string = '{{#each object}}{{wycats name}}{{/each}}'; + var compileOptions = { trackIds: true }; + + var input = { object: { foo: { name: 'foo' }, bar: { name: 'bar' } } }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('foo:object.foo\nbar:object.bar\n'); }); it('should handle nesting', function() { - var template = CompilerContext.compile( - '{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}', - { trackIds: true } - ); - - equals( - template( - { array: [{ name: 'foo' }, { name: 'bar' }] }, - { helpers: helpers } - ), - 'foo:.array..0\nbar:.array..1\n' - ); + var string = '{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}'; + var compileOptions = { trackIds: true }; + + var input = { array: [{ name: 'foo' }, { name: 'bar' }] }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('foo:.array..0\nbar:.array..1\n'); }); it('should handle block params', function() { - var template = CompilerContext.compile( - '{{#each array as |value|}}{{blockParams value.name}}{{/each}}', - { trackIds: true } - ); - - equals( - template( - { array: [{ name: 'foo' }, { name: 'bar' }] }, - { helpers: helpers } - ), - 'foo:array.0.name\nbar:array.1.name\n' - ); + var string = + '{{#each array as |value|}}{{blockParams value.name}}{{/each}}'; + var compileOptions = { trackIds: true }; + + var input = { array: [{ name: 'foo' }, { name: 'bar' }] }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('foo:array.0.name\nbar:array.1.name\n'); }); }); describe('#with', function() { it('should track contextPath', function() { - var template = CompilerContext.compile( - '{{#with field}}{{wycats name}}{{/with}}', - { trackIds: true } - ); - - equals( - template({ field: { name: 'foo' } }, { helpers: helpers }), - 'foo:field\n' - ); + var string = '{{#with field}}{{wycats name}}{{/with}}'; + var compileOptions = { trackIds: true }; + + var input = { field: { name: 'foo' } }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('foo:field\n'); }); it('should handle nesting', function() { - var template = CompilerContext.compile( - '{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}', - { trackIds: true } - ); - - equals( - template({ bat: { field: { name: 'foo' } } }, { helpers: helpers }), - 'foo:bat.field\n' - ); + var string = + '{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}'; + var compileOptions = { trackIds: true }; + + var input = { bat: { field: { name: 'foo' } } }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('foo:bat.field\n'); }); }); describe('#blockHelperMissing', function() { it('should track contextPath for arrays', function() { - var template = CompilerContext.compile( - '{{#field}}{{wycats name}}{{/field}}', - { trackIds: true } - ); - - equals( - template({ field: [{ name: 'foo' }] }, { helpers: helpers }), - 'foo:field.0\n' - ); + var string = '{{#field}}{{wycats name}}{{/field}}'; + var compileOptions = { trackIds: true }; + + var input = { field: [{ name: 'foo' }] }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('foo:field.0\n'); }); it('should track contextPath for keys', function() { - var template = CompilerContext.compile( - '{{#field}}{{wycats name}}{{/field}}', - { trackIds: true } - ); - - equals( - template({ field: { name: 'foo' } }, { helpers: helpers }), - 'foo:field\n' - ); + var string = '{{#field}}{{wycats name}}{{/field}}'; + var compileOptions = { trackIds: true }; + + var input = { field: { name: 'foo' } }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('foo:field\n'); }); it('should handle nesting', function() { - var template = CompilerContext.compile( - '{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}', - { trackIds: true } - ); - - equals( - template({ bat: { field: { name: 'foo' } } }, { helpers: helpers }), - 'foo:bat.field\n' - ); + var string = '{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}'; + var compileOptions = { trackIds: true }; + + var input = { bat: { field: { name: 'foo' } } }; + expectTemplate(string) + .withCompileOptions(compileOptions) + .withHelpers(helpers) + .withInput(input) + .toCompileTo('foo:bat.field\n'); }); }); }); From f3baf5de5bd741f428b5364fa86c63adabe1bd55 Mon Sep 17 00:00:00 2001 From: John Boehr Date: Sun, 26 Apr 2020 09:06:01 -0700 Subject: [PATCH 02/11] In HandlebarsTestBench, pass `message` along to expect() --- spec/env/common.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/spec/env/common.js b/spec/env/common.js index 5997322f5..94936080f 100644 --- a/spec/env/common.js +++ b/spec/env/common.js @@ -183,19 +183,18 @@ HandlebarsTestBench.prototype.withMessage = function(message) { }; HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) { - expect(this._compileAndExecute()).to.equal(expectedOutputAsString); + expect(this._compileAndExecute()).to.equal( + expectedOutputAsString, + this.message + ); }; // see chai "to.throw" (https://www.chaijs.com/api/bdd/#method_throw) -HandlebarsTestBench.prototype.toThrow = function( - errorLike, - errMsgMatcher, - msg -) { +HandlebarsTestBench.prototype.toThrow = function(errorLike, errMsgMatcher) { var self = this; expect(function() { self._compileAndExecute(); - }).to.throw(errorLike, errMsgMatcher, msg); + }).to.throw(errorLike, errMsgMatcher, this.message); }; HandlebarsTestBench.prototype._compileAndExecute = function() { From 1b2ea43669ad67bfb19d0ca16ba551cf9e5b9dfd Mon Sep 17 00:00:00 2001 From: John Boehr Date: Sun, 26 Apr 2020 13:12:29 -0700 Subject: [PATCH 03/11] Convert partials - throw on missing partial to use expectTemplate --- spec/partials.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/spec/partials.js b/spec/partials.js index 02dffc5a5..3f3bf09f3 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -368,15 +368,14 @@ describe('partials', function() { it('throw on missing partial', function() { var compile = handlebarsEnv.compile; + var compileWithPartial = CompilerContext.compileWithPartial; handlebarsEnv.compile = undefined; - shouldThrow( - function() { - shouldCompileTo('{{> dude}}', [{}, {}, { dude: 'fail' }], ''); - }, - Error, - /The partial dude could not be compiled/ - ); + CompilerContext.compileWithPartial = CompilerContext.compile; + expectTemplate('{{> dude}}') + .withPartials({ dude: 'fail' }) + .toThrow(Error, /The partial dude could not be compiled/); handlebarsEnv.compile = compile; + CompilerContext.compileWithPartial = compileWithPartial; }); describe('partial blocks', function() { From 1d051fa05255bea4b8ae011b6c12e5482bcbfa9c Mon Sep 17 00:00:00 2001 From: John Boehr Date: Sun, 26 Apr 2020 13:14:42 -0700 Subject: [PATCH 04/11] Add withDecorator() and withDecorators() to HandlebarsTestBench --- spec/env/common.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spec/env/common.js b/spec/env/common.js index 94936080f..a122f4d6e 100644 --- a/spec/env/common.js +++ b/spec/env/common.js @@ -129,6 +129,7 @@ function HandlebarsTestBench(templateAsString) { this.templateAsString = templateAsString; this.helpers = {}; this.partials = {}; + this.decorators = {}; this.input = {}; this.message = 'Template' + templateAsString + ' does not evaluate to expected output'; @@ -167,6 +168,22 @@ HandlebarsTestBench.prototype.withPartials = function(partials) { return this; }; +HandlebarsTestBench.prototype.withDecorator = function( + name, + decoratorFunction +) { + this.decorators[name] = decoratorFunction; + return this; +}; + +HandlebarsTestBench.prototype.withDecorators = function(decorators) { + var self = this; + Object.keys(decorators).forEach(function(name) { + self.withDecorator(name, decorators[name]); + }); + return this; +}; + HandlebarsTestBench.prototype.withCompileOptions = function(compileOptions) { this.compileOptions = compileOptions; return this; @@ -217,5 +234,6 @@ HandlebarsTestBench.prototype._combineRuntimeOptions = function() { }); combinedRuntimeOptions.helpers = this.helpers; combinedRuntimeOptions.partials = this.partials; + combinedRuntimeOptions.decorators = this.decorators; return combinedRuntimeOptions; }; From f58b31ebabb5d0c1c9f20041003a555bd7b3b8de Mon Sep 17 00:00:00 2001 From: John Boehr Date: Sun, 26 Apr 2020 13:39:31 -0700 Subject: [PATCH 05/11] Convert partials - Multiple partial registration to expectTemplate --- spec/partials.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/spec/partials.js b/spec/partials.js index 3f3bf09f3..019c71d94 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -288,13 +288,11 @@ describe('partials', function() { var string = 'Dudes: {{> shared/dude}} {{> globalTest}}'; var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - shouldCompileToWithPartials( - string, - [hash], - true, - 'Dudes: Jeepers Creepers', - 'Partials can use globals or passed' - ); + expectTemplate(string) + .withInput(hash) + .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() { From 43d442798f4c9354a32c46728af862d59b7f838a Mon Sep 17 00:00:00 2001 From: John Boehr Date: Sun, 26 Apr 2020 14:17:13 -0700 Subject: [PATCH 06/11] Run mustache spec using expectTemplate() --- spec/spec.js | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/spec/spec.js b/spec/spec.js index ff4d7bc98..8b4ea99fe 100644 --- a/spec/spec.js +++ b/spec/spec.js @@ -40,22 +40,12 @@ describe('spec', function() { /* eslint-enable no-eval */ } it(name + ' - ' + test.name, function() { - if (test.partials) { - shouldCompileToWithPartials( - test.template, - [data, {}, test.partials, true], - true, - test.expected, - test.desc + ' "' + test.template + '"' - ); - } else { - shouldCompileTo( - test.template, - [data, {}, {}, true], - test.expected, - test.desc + ' "' + test.template + '"' - ); - } + expectTemplate(test.template) + .withInput(data) + .withPartials(test.partials || {}) + .withCompileOptions({ compat: true }) + .withMessage(test.desc + ' "' + test.template + '"') + .toCompileTo(test.expected); }); }); }); From 6b164bc5fd6bba653bbf15a4c634c8efa03fab4d Mon Sep 17 00:00:00 2001 From: John Boehr Date: Sun, 26 Apr 2020 14:19:28 -0700 Subject: [PATCH 07/11] Convert remaining shouldCompileTo() to expectTemplate() --- spec/basic.js | 704 +++++++++++++++++------------------ spec/blocks.js | 336 ++++++++--------- spec/builtins.js | 376 +++++++++++-------- spec/helpers.js | 483 ++++++++++++------------ spec/javascript-compiler.js | 20 +- spec/partials.js | 724 ++++++++++++++++-------------------- spec/regressions.js | 217 +++++------ spec/security.js | 36 +- spec/subexpressions.js | 84 +++-- spec/utils.js | 8 +- spec/whitespace-control.js | 215 ++++++----- 11 files changed, 1574 insertions(+), 1629 deletions(-) diff --git a/spec/basic.js b/spec/basic.js index 4ed1914de..1e4ce2b6b 100644 --- a/spec/basic.js +++ b/spec/basic.js @@ -6,117 +6,141 @@ beforeEach(function() { describe('basic context', function() { it('most basic', function() { - shouldCompileTo('{{foo}}', { foo: 'foo' }, 'foo'); + expectTemplate('{{foo}}') + .withInput({ foo: 'foo' }) + .toCompileTo('foo'); }); it('escaping', function() { - shouldCompileTo('\\{{foo}}', { foo: 'food' }, '{{foo}}'); - shouldCompileTo('content \\{{foo}}', { foo: 'food' }, 'content {{foo}}'); - shouldCompileTo('\\\\{{foo}}', { foo: 'food' }, '\\food'); - shouldCompileTo('content \\\\{{foo}}', { foo: 'food' }, 'content \\food'); - shouldCompileTo('\\\\ {{foo}}', { foo: 'food' }, '\\\\ food'); + 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'); }); it('compiling with a basic context', function() { - shouldCompileTo( - 'Goodbye\n{{cruel}}\n{{world}}!', - { cruel: 'cruel', world: 'world' }, - 'Goodbye\ncruel\nworld!', - 'It works if all the required keys are provided' - ); + expectTemplate('Goodbye\n{{cruel}}\n{{world}}!') + .withInput({ + cruel: 'cruel', + world: 'world' + }) + .withMessage('It works if all the required keys are provided') + .toCompileTo('Goodbye\ncruel\nworld!'); }); it('compiling with a string context', function() { - shouldCompileTo('{{.}}{{length}}', 'bye', 'bye3'); + expectTemplate('{{.}}{{length}}') + .withInput('bye') + .toCompileTo('bye3'); }); it('compiling with an undefined context', function() { - shouldCompileTo( - 'Goodbye\n{{cruel}}\n{{world.bar}}!', - undefined, - 'Goodbye\n\n!' - ); + expectTemplate('Goodbye\n{{cruel}}\n{{world.bar}}!') + .withInput(undefined) + .toCompileTo('Goodbye\n\n!'); - shouldCompileTo( - '{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}', - undefined, - 'Goodbye' - ); + expectTemplate('{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}') + .withInput(undefined) + .toCompileTo('Goodbye'); }); it('comments', function() { - shouldCompileTo( - '{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!', - { cruel: 'cruel', world: 'world' }, - 'Goodbye\ncruel\nworld!', - 'comments are ignored' - ); + expectTemplate('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!') + .withInput({ + cruel: 'cruel', + world: 'world' + }) + .withMessage('comments are ignored') + .toCompileTo('Goodbye\ncruel\nworld!'); - shouldCompileTo(' {{~! comment ~}} blah', {}, 'blah'); - shouldCompileTo(' {{~!-- long-comment --~}} blah', {}, 'blah'); - shouldCompileTo(' {{! comment ~}} blah', {}, ' blah'); - shouldCompileTo(' {{!-- long-comment --~}} blah', {}, ' blah'); - shouldCompileTo(' {{~! comment}} blah', {}, ' blah'); - shouldCompileTo(' {{~!-- long-comment --}} blah', {}, ' blah'); + 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' + ); }); it('boolean', function() { var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!'; - shouldCompileTo( - string, - { goodbye: true, world: 'world' }, - 'GOODBYE cruel world!', - 'booleans show the contents when true' - ); - - shouldCompileTo( - string, - { goodbye: false, world: 'world' }, - 'cruel world!', - 'booleans do not show the contents when false' - ); + expectTemplate(string) + .withInput({ + goodbye: true, + world: 'world' + }) + .withMessage('booleans show the contents when true') + .toCompileTo('GOODBYE cruel world!'); + + expectTemplate(string) + .withInput({ + goodbye: false, + world: 'world' + }) + .withMessage('booleans do not show the contents when false') + .toCompileTo('cruel world!'); }); it('zeros', function() { - shouldCompileTo( - 'num1: {{num1}}, num2: {{num2}}', - { num1: 42, num2: 0 }, - 'num1: 42, num2: 0' - ); - shouldCompileTo('num: {{.}}', 0, 'num: 0'); - shouldCompileTo('num: {{num1/num2}}', { num1: { num2: 0 } }, 'num: 0'); + expectTemplate('num1: {{num1}}, num2: {{num2}}') + .withInput({ + num1: 42, + 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 */ - shouldCompileTo( - 'val1: {{val1}}, val2: {{val2}}', - { val1: false, val2: new Boolean(false) }, - 'val1: false, val2: false' - ); - shouldCompileTo('val: {{.}}', false, 'val: false'); - shouldCompileTo( - 'val: {{val1/val2}}', - { val1: { val2: false } }, - 'val: false' - ); - - shouldCompileTo( - 'val1: {{{val1}}}, val2: {{{val2}}}', - { val1: false, val2: new Boolean(false) }, - 'val1: false, val2: false' - ); - shouldCompileTo( - 'val: {{{val1/val2}}}', - { val1: { val2: false } }, - 'val: false' - ); + expectTemplate('val1: {{val1}}, val2: {{val2}}') + .withInput({ + val1: false, + 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'); + + expectTemplate('val1: {{{val1}}}, val2: {{{val2}}}') + .withInput({ + val1: false, + val2: new Boolean(false) + }) + .toCompileTo('val1: false, val2: false'); + expectTemplate('val: {{{val1/val2}}}') + .withInput({ val1: { val2: false } }) + .toCompileTo('val: false'); /* eslint-enable */ }); it('should handle undefined and null', function() { - shouldCompileTo( - '{{awesome undefined null}}', - { + expectTemplate('{{awesome undefined null}}') + .withInput({ awesome: function(_undefined, _null, options) { return ( (_undefined === undefined) + @@ -126,95 +150,70 @@ describe('basic context', function() { typeof options ); } - }, - 'true true object' - ); - shouldCompileTo( - '{{undefined}}', - { + }) + .toCompileTo('true true object'); + expectTemplate('{{undefined}}') + .withInput({ undefined: function() { return 'undefined!'; } - }, - 'undefined!' - ); - shouldCompileTo( - '{{null}}', - { + }) + .toCompileTo('undefined!'); + expectTemplate('{{null}}') + .withInput({ null: function() { return 'null!'; } - }, - 'null!' - ); + }) + .toCompileTo('null!'); }); it('newlines', function() { - shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest"); - shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest"); + expectTemplate("Alan's\nTest").toCompileTo("Alan's\nTest"); + expectTemplate("Alan's\rTest").toCompileTo("Alan's\rTest"); }); it('escaping text', function() { - shouldCompileTo( - "Awesome's", - {}, - "Awesome's", - "text is escaped so that it doesn't get caught on single quotes" - ); - shouldCompileTo( - 'Awesome\\', - {}, - 'Awesome\\', - "text is escaped so that the closing quote can't be ignored" - ); - shouldCompileTo( - 'Awesome\\\\ foo', - {}, - 'Awesome\\\\ foo', - "text is escaped so that it doesn't mess up backslashes" - ); - shouldCompileTo( - 'Awesome {{foo}}', - { foo: '\\' }, - 'Awesome \\', - "text is escaped so that it doesn't mess up backslashes" - ); - shouldCompileTo( - " ' ' ", - {}, - " ' ' ", - 'double quotes never produce invalid javascript' - ); + expectTemplate("Awesome's") + .withMessage( + "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(" ' ' "); }); it('escaping expressions', function() { - shouldCompileTo( - '{{{awesome}}}', - { awesome: "&'\\<>" }, - "&'\\<>", - "expressions with 3 handlebars aren't escaped" - ); + expectTemplate('{{{awesome}}}') + .withInput({ awesome: "&'\\<>" }) + .withMessage("expressions with 3 handlebars aren't escaped") + .toCompileTo("&'\\<>"); - shouldCompileTo( - '{{&awesome}}', - { awesome: "&'\\<>" }, - "&'\\<>", - "expressions with {{& handlebars aren't escaped" - ); + expectTemplate('{{&awesome}}') + .withInput({ awesome: "&'\\<>" }) + .withMessage("expressions with {{& handlebars aren't escaped") + .toCompileTo("&'\\<>"); - shouldCompileTo( - '{{awesome}}', - { awesome: '&"\'`\\<>' }, - '&"'`\\<>', - 'by default expressions should be escaped' - ); + expectTemplate('{{awesome}}') + .withInput({ awesome: '&"\'`\\<>' }) + .withMessage('by default expressions should be escaped') + .toCompileTo('&"'`\\<>'); - shouldCompileTo( - '{{awesome}}', - { awesome: 'Escaped, looks like: <b>' }, - 'Escaped, <b> looks like: &lt;b&gt;', - 'escaping should properly handle amperstands' - ); + expectTemplate('{{awesome}}') + .withInput({ awesome: 'Escaped, looks like: <b>' }) + .withMessage('escaping should properly handle amperstands') + .toCompileTo('Escaped, <b> looks like: &lt;b&gt;'); }); it("functions returning safestrings shouldn't be escaped", function() { @@ -223,263 +222,222 @@ describe('basic context', function() { return new Handlebars.SafeString("&'\\<>"); } }; - shouldCompileTo( - '{{awesome}}', - hash, - "&'\\<>", - "functions returning safestrings aren't escaped" - ); + expectTemplate('{{awesome}}') + .withInput(hash) + .withMessage("functions returning safestrings aren't escaped") + .toCompileTo("&'\\<>"); }); it('functions', function() { - shouldCompileTo( - '{{awesome}}', - { + expectTemplate('{{awesome}}') + .withInput({ awesome: function() { return 'Awesome'; } - }, - 'Awesome', - 'functions are called and render their output' - ); - shouldCompileTo( - '{{awesome}}', - { + }) + .withMessage('functions are called and render their output') + .toCompileTo('Awesome'); + expectTemplate('{{awesome}}') + .withInput({ awesome: function() { return this.more; }, more: 'More awesome' - }, - 'More awesome', - 'functions are bound to the context' - ); + }) + .withMessage('functions are bound to the context') + .toCompileTo('More awesome'); }); it('functions with context argument', function() { - shouldCompileTo( - '{{awesome frank}}', - { + expectTemplate('{{awesome frank}}') + .withInput({ awesome: function(context) { return context; }, frank: 'Frank' - }, - 'Frank', - 'functions are called with context arguments' - ); + }) + .withMessage('functions are called with context arguments') + .toCompileTo('Frank'); }); it('pathed functions with context argument', function() { - shouldCompileTo( - '{{bar.awesome frank}}', - { + expectTemplate('{{bar.awesome frank}}') + .withInput({ bar: { awesome: function(context) { return context; } }, frank: 'Frank' - }, - 'Frank', - 'functions are called with context arguments' - ); + }) + .withMessage('functions are called with context arguments') + .toCompileTo('Frank'); }); it('depthed functions with context argument', function() { - shouldCompileTo( - '{{#with frank}}{{../awesome .}}{{/with}}', - { + expectTemplate('{{#with frank}}{{../awesome .}}{{/with}}') + .withInput({ awesome: function(context) { return context; }, frank: 'Frank' - }, - 'Frank', - 'functions are called with context arguments' - ); + }) + .withMessage('functions are called with context arguments') + .toCompileTo('Frank'); }); it('block functions with context argument', function() { - shouldCompileTo( - '{{#awesome 1}}inner {{.}}{{/awesome}}', - { + expectTemplate('{{#awesome 1}}inner {{.}}{{/awesome}}') + .withInput({ awesome: function(context, options) { return options.fn(context); } - }, - 'inner 1', - 'block functions are called with context and options' - ); + }) + .withMessage('block functions are called with context and options') + .toCompileTo('inner 1'); }); it('depthed block functions with context argument', function() { - shouldCompileTo( - '{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}', - { + expectTemplate( + '{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}' + ) + .withInput({ value: true, awesome: function(context, options) { return options.fn(context); } - }, - 'inner 1', - 'block functions are called with context and options' - ); + }) + .withMessage('block functions are called with context and options') + .toCompileTo('inner 1'); }); it('block functions without context argument', function() { - shouldCompileTo( - '{{#awesome}}inner{{/awesome}}', - { + expectTemplate('{{#awesome}}inner{{/awesome}}') + .withInput({ awesome: function(options) { return options.fn(this); } - }, - 'inner', - 'block functions are called with options' - ); + }) + .withMessage('block functions are called with options') + .toCompileTo('inner'); }); it('pathed block functions without context argument', function() { - shouldCompileTo( - '{{#foo.awesome}}inner{{/foo.awesome}}', - { + expectTemplate('{{#foo.awesome}}inner{{/foo.awesome}}') + .withInput({ foo: { awesome: function() { return this; } } - }, - 'inner', - 'block functions are called with options' - ); + }) + .withMessage('block functions are called with options') + .toCompileTo('inner'); }); it('depthed block functions without context argument', function() { - shouldCompileTo( - '{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}', - { + expectTemplate( + '{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}' + ) + .withInput({ value: true, awesome: function() { return this; } - }, - 'inner', - 'block functions are called with options' - ); + }) + .withMessage('block functions are called with options') + .toCompileTo('inner'); }); it('paths with hyphens', function() { - shouldCompileTo( - '{{foo-bar}}', - { 'foo-bar': 'baz' }, - 'baz', - 'Paths can contain hyphens (-)' - ); - shouldCompileTo( - '{{foo.foo-bar}}', - { foo: { 'foo-bar': 'baz' } }, - 'baz', - 'Paths can contain hyphens (-)' - ); - shouldCompileTo( - '{{foo/foo-bar}}', - { foo: { 'foo-bar': 'baz' } }, - 'baz', - 'Paths can contain hyphens (-)' - ); + expectTemplate('{{foo-bar}}') + .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 (-)') + .toCompileTo('baz'); }); it('nested paths', function() { - shouldCompileTo( - 'Goodbye {{alan/expression}} world!', - { alan: { expression: 'beautiful' } }, - 'Goodbye beautiful world!', - 'Nested paths access nested objects' - ); + expectTemplate('Goodbye {{alan/expression}} world!') + .withInput({ alan: { expression: 'beautiful' } }) + .withMessage('Nested paths access nested objects') + .toCompileTo('Goodbye beautiful world!'); }); it('nested paths with empty string value', function() { - shouldCompileTo( - 'Goodbye {{alan/expression}} world!', - { alan: { expression: '' } }, - 'Goodbye world!', - 'Nested paths access nested objects with empty string' - ); + expectTemplate('Goodbye {{alan/expression}} world!') + .withInput({ alan: { expression: '' } }) + .withMessage('Nested paths access nested objects with empty string') + .toCompileTo('Goodbye world!'); }); it('literal paths', function() { - shouldCompileTo( - 'Goodbye {{[@alan]/expression}} world!', - { '@alan': { expression: 'beautiful' } }, - 'Goodbye beautiful world!', - 'Literal paths can be used' - ); - shouldCompileTo( - 'Goodbye {{[foo bar]/expression}} world!', - { 'foo bar': { expression: 'beautiful' } }, - 'Goodbye beautiful world!', - 'Literal paths can be used' - ); + expectTemplate('Goodbye {{[@alan]/expression}} world!') + .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') + .toCompileTo('Goodbye beautiful world!'); }); it('literal references', function() { - shouldCompileTo( - 'Goodbye {{[foo bar]}} world!', - { 'foo bar': 'beautiful' }, - 'Goodbye beautiful world!' - ); - shouldCompileTo( - 'Goodbye {{"foo bar"}} world!', - { 'foo bar': 'beautiful' }, - 'Goodbye beautiful world!' - ); - shouldCompileTo( - "Goodbye {{'foo bar'}} world!", - { 'foo bar': 'beautiful' }, - 'Goodbye beautiful world!' - ); - shouldCompileTo( - 'Goodbye {{"foo[bar"}} world!', - { 'foo[bar': 'beautiful' }, - 'Goodbye beautiful world!' - ); - shouldCompileTo( - 'Goodbye {{"foo\'bar"}} world!', - { "foo'bar": 'beautiful' }, - 'Goodbye beautiful world!' - ); - shouldCompileTo( - "Goodbye {{'foo\"bar'}} world!", - { 'foo"bar': 'beautiful' }, - '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!'); + expectTemplate("Goodbye {{'foo\"bar'}} world!") + .withInput({ 'foo"bar': 'beautiful' }) + .toCompileTo('Goodbye beautiful world!'); }); it("that current context path ({{.}}) doesn't hit helpers", function() { - shouldCompileTo('test: {{.}}', [null, { helper: 'awesome' }], 'test: '); + expectTemplate('test: {{.}}') + .withInput(null) + .withHelpers({ helper: 'awesome' }) + .toCompileTo('test: '); }); it('complex but empty paths', function() { - shouldCompileTo('{{person/name}}', { person: { name: null } }, ''); - shouldCompileTo('{{person/name}}', { person: {} }, ''); + 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'] }; - shouldCompileTo( - string, - hash, - 'goodbyeGoodbyeGOODBYE', - 'This keyword in paths evaluates to current context' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('This keyword in paths evaluates to current context') + .toCompileTo('goodbyeGoodbyeGOODBYE'); string = '{{#hellos}}{{this/text}}{{/hellos}}'; hash = { hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }] }; - shouldCompileTo( - string, - hash, - 'helloHelloHELLO', - 'This keyword evaluates in more complex paths' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('This keyword evaluates in more complex paths') + .toCompileTo('helloHelloHELLO'); }); it('this keyword nested inside path', function() { @@ -488,8 +446,12 @@ describe('basic context', function() { 'Invalid path: text/this - 1:13' ); - shouldCompileTo('{{[this]}}', { this: 'bar' }, 'bar'); - shouldCompileTo('{{text/[this]}}', { text: { this: 'bar' } }, 'bar'); + expectTemplate('{{[this]}}') + .withInput({ this: 'bar' }) + .toCompileTo('bar'); + expectTemplate('{{text/[this]}}') + .withInput({ text: { this: 'bar' } }) + .toCompileTo('bar'); }); it('this keyword in helpers', function() { @@ -500,81 +462,83 @@ describe('basic context', function() { }; var string = '{{#goodbyes}}{{foo this}}{{/goodbyes}}'; var hash = { goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] }; - shouldCompileTo( - string, - [hash, helpers], - 'bar goodbyebar Goodbyebar GOODBYE', - 'This keyword in paths evaluates to current context' - ); + expectTemplate(string) + .withInput(hash) + .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' }] }; - shouldCompileTo( - string, - [hash, helpers], - 'bar hellobar Hellobar HELLO', - 'This keyword evaluates in more complex paths' - ); + expectTemplate(string) + .withInput(hash) + .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'); - shouldCompileTo( - '{{foo [this]}}', - { + expectTemplate('{{foo [this]}}') + .withInput({ foo: function(value) { return value; }, this: 'bar' - }, - 'bar' - ); - shouldCompileTo( - '{{foo text/[this]}}', - { + }) + .toCompileTo('bar'); + expectTemplate('{{foo text/[this]}}') + .withInput({ foo: function(value) { return value; }, text: { this: 'bar' } - }, - 'bar' - ); + }) + .toCompileTo('bar'); }); it('pass string literals', function() { - shouldCompileTo('{{"foo"}}', {}, ''); - shouldCompileTo('{{"foo"}}', { foo: 'bar' }, 'bar'); - shouldCompileTo( - '{{#"foo"}}{{.}}{{/"foo"}}', - { foo: ['bar', 'baz'] }, - 'barbaz' - ); + expectTemplate('{{"foo"}}').toCompileTo(''); + expectTemplate('{{"foo"}}') + .withInput({ foo: 'bar' }) + .toCompileTo('bar'); + expectTemplate('{{#"foo"}}{{.}}{{/"foo"}}') + .withInput({ + foo: ['bar', 'baz'] + }) + .toCompileTo('barbaz'); }); it('pass number literals', function() { - shouldCompileTo('{{12}}', {}, ''); - shouldCompileTo('{{12}}', { '12': 'bar' }, 'bar'); - shouldCompileTo('{{12.34}}', {}, ''); - shouldCompileTo('{{12.34}}', { '12.34': 'bar' }, 'bar'); - shouldCompileTo( - '{{12.34 1}}', - { + 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) { return 'bar' + arg; } - }, - 'bar1' - ); + }) + .toCompileTo('bar1'); }); it('pass boolean literals', function() { - shouldCompileTo('{{true}}', {}, ''); - shouldCompileTo('{{true}}', { '': 'foo' }, ''); - shouldCompileTo('{{false}}', { false: 'foo' }, 'foo'); + expectTemplate('{{true}}').toCompileTo(''); + expectTemplate('{{true}}') + .withInput({ '': 'foo' }) + .toCompileTo(''); + expectTemplate('{{false}}') + .withInput({ false: 'foo' }) + .toCompileTo('foo'); }); it('should handle literals in subexpression', function() { @@ -583,17 +547,13 @@ describe('basic context', function() { return arg; } }; - shouldCompileTo( - '{{foo (false)}}', - [ - { - false: function() { - return 'bar'; - } - }, - helpers - ], - 'bar' - ); + expectTemplate('{{foo (false)}}') + .withInput({ + false: function() { + return 'bar'; + } + }) + .withHelpers(helpers) + .toCompileTo('bar'); }); }); diff --git a/spec/blocks.js b/spec/blocks.js index 2e5516198..ab16fd9d3 100644 --- a/spec/blocks.js +++ b/spec/blocks.js @@ -5,19 +5,18 @@ describe('blocks', function() { goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], world: 'world' }; - shouldCompileTo( - string, - hash, - 'goodbye! Goodbye! GOODBYE! cruel world!', - 'Arrays iterate over the contents when not empty' - ); - - shouldCompileTo( - string, - { goodbyes: [], world: 'world' }, - 'cruel world!', - 'Arrays ignore the contents when empty' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('Arrays iterate over the contents when not empty') + .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); + + expectTemplate(string) + .withInput({ + goodbyes: [], + world: 'world' + }) + .withMessage('Arrays ignore the contents when empty') + .toCompileTo('cruel world!'); }); it('array without data', function() { @@ -27,11 +26,10 @@ describe('blocks', function() { goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], world: 'world' }; - shouldCompileTo( - string, - [hash, , , false], - 'goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE' - ); + expectTemplate(string) + .withInput(hash) + .withCompileOptions({ compat: false }) + .toCompileTo('goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE'); }); it('array with @index', function() { @@ -54,19 +52,18 @@ describe('blocks', function() { goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], world: 'world' }; - shouldCompileTo( - string, - hash, - 'cruel world!', - 'Arrays iterate over the contents when not empty' - ); - - shouldCompileTo( - string, - { goodbyes: [], world: 'world' }, - 'cruel world!', - 'Arrays ignore the contents when empty' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('Arrays iterate over the contents when not empty') + .toCompileTo('cruel world!'); + + expectTemplate(string) + .withInput({ + goodbyes: [], + world: 'world' + }) + .withMessage('Arrays ignore the contents when empty') + .toCompileTo('cruel world!'); }); it('block with complex lookup', function() { @@ -76,12 +73,14 @@ describe('blocks', function() { goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }] }; - shouldCompileTo( - string, - hash, - 'goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ', - 'Templates can access variables in contexts up the stack with relative path syntax' - ); + expectTemplate(string) + .withInput(hash) + .withMessage( + 'Templates can access variables in contexts up the stack with relative path syntax' + ) + .toCompileTo( + 'goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ' + ); }); it('multiple blocks with complex lookup', function() { @@ -91,7 +90,9 @@ describe('blocks', function() { goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }] }; - shouldCompileTo(string, hash, 'AlanAlanAlanAlanAlanAlan'); + expectTemplate(string) + .withInput(hash) + .toCompileTo('AlanAlanAlanAlanAlanAlan'); }); it('block with complex lookup using nested context', function() { @@ -108,7 +109,9 @@ describe('blocks', function() { outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }] }; - shouldCompileTo(string, hash, 'Goodbye cruel sad OMG!'); + expectTemplate(string) + .withInput(hash) + .toCompileTo('Goodbye cruel sad OMG!'); }); it('works with cached blocks', function() { @@ -133,61 +136,51 @@ describe('blocks', function() { var string = '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'; var hash = {}; - shouldCompileTo( - string, - hash, - 'Right On!', - "Inverted section rendered when value isn't set." - ); + expectTemplate(string) + .withInput(hash) + .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 }; - shouldCompileTo( - string, - hash, - 'Right On!', - 'Inverted section rendered when value is false.' - ); + expectTemplate(string) + .withInput(hash) + .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: [] }; - shouldCompileTo( - string, - hash, - 'Right On!', - 'Inverted section rendered when value is empty set.' - ); + expectTemplate(string) + .withInput(hash) + .withMessage('Inverted section rendered when value is empty set.') + .toCompileTo('Right On!'); }); it('block inverted sections', function() { - shouldCompileTo( - '{{#people}}{{name}}{{^}}{{none}}{{/people}}', - { none: 'No people' }, - 'No people' - ); + expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}') + .withInput({ none: 'No people' }) + .toCompileTo('No people'); }); it('chained inverted sections', function() { - shouldCompileTo( - '{{#people}}{{name}}{{else if none}}{{none}}{{/people}}', - { none: 'No people' }, - 'No people' - ); - shouldCompileTo( - '{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}', - { none: 'No people' }, - 'No people' - ); - shouldCompileTo( - '{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}', - { none: 'No people' }, - 'No people' - ); + 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( @@ -196,62 +189,55 @@ describe('blocks', function() { }); it('block inverted sections with empty arrays', function() { - shouldCompileTo( - '{{#people}}{{name}}{{^}}{{none}}{{/people}}', - { none: 'No people', people: [] }, - 'No people' - ); + expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}') + .withInput({ + none: 'No people', + people: [] + }) + .toCompileTo('No people'); }); }); describe('standalone sections', function() { it('block standalone else sections', function() { - shouldCompileTo( - '{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', - { none: 'No people' }, - 'No people\n' - ); - shouldCompileTo( - '{{#none}}\n{{.}}\n{{^}}\n{{none}}\n{{/none}}\n', - { none: 'No people' }, - 'No people\n' - ); - shouldCompileTo( - '{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', - { none: 'No people' }, - 'No people\n' - ); + 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() { - shouldCompileTo( - '{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', - [{ none: 'No people' }, {}, {}, { ignoreStandalone: true }], - '\nNo people\n\n' - ); - shouldCompileTo( - '{{#none}}\n{{.}}\n{{^}}\nFail\n{{/none}}\n', - [{ none: 'No people' }, {}, {}, { ignoreStandalone: true }], - '\nNo people\n\n' - ); + 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() { - shouldCompileTo( - '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n', - { none: 'No people' }, - 'No people\n' - ); - shouldCompileTo( - '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n', - { none: 'No people' }, - 'No people\n' - ); + 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() { - shouldCompileTo( - '{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.', - { data: [1, 3, 5] }, - '1\n3\n5\nOK.' - ); + expectTemplate('{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.') + .withInput({ + data: [1, 3, 5] + }) + .toCompileTo('1\n3\n5\nOK.'); }); }); @@ -261,11 +247,10 @@ describe('blocks', function() { '{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}'; var hash = { omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] }; - shouldCompileTo( - string, - [hash, undefined, undefined, true], - 'Goodbye cruel OMG!' - ); + expectTemplate(string) + .withInput(hash) + .withCompileOptions({ compat: true }) + .toCompileTo('Goodbye cruel OMG!'); }); it('block with deep recursive pathed lookup', function() { @@ -276,11 +261,10 @@ describe('blocks', function() { outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] }; - shouldCompileTo( - string, - [hash, undefined, undefined, true], - 'Goodbye cruel OMG!' - ); + expectTemplate(string) + .withInput(hash) + .withCompileOptions({ compat: true }) + .toCompileTo('Goodbye cruel OMG!'); }); it('block with missed recursive lookup', function() { var string = @@ -290,11 +274,10 @@ describe('blocks', function() { outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] }; - shouldCompileTo( - string, - [hash, undefined, undefined, true], - 'Goodbye cruel ' - ); + expectTemplate(string) + .withInput(hash) + .withCompileOptions({ compat: true }) + .toCompileTo('Goodbye cruel '); }); }); @@ -311,11 +294,10 @@ describe('blocks', function() { return fn; } }; - shouldCompileTo( - '{{#helper}}{{*decorator}}{{/helper}}', - { hash: {}, helpers: helpers, decorators: decorators }, - 'success' - ); + expectTemplate('{{#helper}}{{*decorator}}{{/helper}}') + .withHelpers(helpers) + .withDecorators(decorators) + .toCompileTo('success'); }); it('should apply allow undefined return', function() { var helpers = { @@ -328,11 +310,10 @@ describe('blocks', function() { fn.run = 'cess'; } }; - shouldCompileTo( - '{{#helper}}{{*decorator}}suc{{/helper}}', - { hash: {}, helpers: helpers, decorators: decorators }, - 'success' - ); + expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}') + .withHelpers(helpers) + .withDecorators(decorators) + .toCompileTo('success'); }); it('should apply block decorators', function() { @@ -347,11 +328,12 @@ describe('blocks', function() { return fn; } }; - shouldCompileTo( - '{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}', - { hash: {}, helpers: helpers, decorators: decorators }, - 'success' - ); + expectTemplate( + '{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}' + ) + .withHelpers(helpers) + .withDecorators(decorators) + .toCompileTo('success'); }); it('should support nested decorators', function() { var helpers = { @@ -368,11 +350,12 @@ describe('blocks', function() { props.nested = options.fn(); } }; - shouldCompileTo( - '{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}', - { hash: {}, helpers: helpers, decorators: decorators }, - 'success' - ); + expectTemplate( + '{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}' + ) + .withHelpers(helpers) + .withDecorators(decorators) + .toCompileTo('success'); }); it('should apply multiple decorators', function() { @@ -387,11 +370,12 @@ describe('blocks', function() { return fn; } }; - shouldCompileTo( - '{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}', - { hash: {}, helpers: helpers, decorators: decorators }, - 'success' - ); + expectTemplate( + '{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}' + ) + .withHelpers(helpers) + .withDecorators(decorators) + .toCompileTo('success'); }); it('should access parent variables', function() { @@ -406,11 +390,11 @@ describe('blocks', function() { return fn; } }; - shouldCompileTo( - '{{#helper}}{{*decorator foo}}{{/helper}}', - { hash: { foo: 'success' }, helpers: helpers, decorators: decorators }, - 'success' - ); + expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}') + .withHelpers(helpers) + .withDecorators(decorators) + .withInput({ foo: 'success' }) + .toCompileTo('success'); }); it('should work with root program', function() { var run; @@ -421,11 +405,10 @@ describe('blocks', function() { return fn; } }; - shouldCompileTo( - '{{*decorator "success"}}', - { hash: { foo: 'success' }, decorators: decorators }, - '' - ); + expectTemplate('{{*decorator "success"}}') + .withDecorators(decorators) + .withInput({ foo: 'success' }) + .toCompileTo(''); equals(run, true); }); it('should fail when accessing variables from root', function() { @@ -437,11 +420,10 @@ describe('blocks', function() { return fn; } }; - shouldCompileTo( - '{{*decorator foo}}', - { hash: { foo: 'fail' }, decorators: decorators }, - '' - ); + expectTemplate('{{*decorator foo}}') + .withDecorators(decorators) + .withInput({ foo: 'fail' }) + .toCompileTo(''); equals(run, true); }); diff --git a/spec/builtins.js b/spec/builtins.js index 8502bfe44..b54c5ca3a 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -2,148 +2,165 @@ describe('builtin helpers', function() { describe('#if', function() { it('if', function() { var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; - shouldCompileTo( - string, - { goodbye: true, world: 'world' }, - 'GOODBYE cruel world!', - 'if with boolean argument shows the contents when true' - ); - shouldCompileTo( - string, - { goodbye: 'dummy', world: 'world' }, - 'GOODBYE cruel world!', - 'if with string argument shows the contents' - ); - shouldCompileTo( - string, - { goodbye: false, world: 'world' }, - 'cruel world!', - 'if with boolean argument does not show the contents when false' - ); - shouldCompileTo( - string, - { world: 'world' }, - 'cruel world!', - 'if with undefined does not show the contents' - ); - shouldCompileTo( - string, - { goodbye: ['foo'], world: 'world' }, - 'GOODBYE cruel world!', - 'if with non-empty array shows the contents' - ); - shouldCompileTo( - string, - { goodbye: [], world: 'world' }, - 'cruel world!', - 'if with empty array does not show the contents' - ); - shouldCompileTo( - string, - { goodbye: 0, world: 'world' }, - 'cruel world!', - 'if with zero does not show the contents' - ); - shouldCompileTo( - '{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!', - { goodbye: 0, world: 'world' }, - 'GOODBYE cruel world!', - 'if with zero does not show the contents' - ); + expectTemplate(string) + .withInput({ + goodbye: true, + world: 'world' + }) + .withMessage('if with boolean argument shows the contents when true') + .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) + .withInput({ + goodbye: 'dummy', + world: 'world' + }) + .withMessage('if with string argument shows the contents') + .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) + .withInput({ + goodbye: false, + world: 'world' + }) + .withMessage( + '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'], + world: 'world' + }) + .withMessage('if with non-empty array shows the contents') + .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) + .withInput({ + goodbye: [], + world: 'world' + }) + .withMessage('if with empty array does not show the contents') + .toCompileTo('cruel world!'); + expectTemplate(string) + .withInput({ + goodbye: 0, + world: 'world' + }) + .withMessage('if with zero does not show the contents') + .toCompileTo('cruel world!'); + expectTemplate( + '{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!' + ) + .withInput({ + goodbye: 0, + world: 'world' + }) + .withMessage('if with zero does not show the contents') + .toCompileTo('GOODBYE cruel world!'); }); it('if with function argument', function() { var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; - shouldCompileTo( - string, - { + expectTemplate(string) + .withInput({ goodbye: function() { return true; }, world: 'world' - }, - 'GOODBYE cruel world!', - 'if with function shows the contents when function returns true' - ); - shouldCompileTo( - string, - { + }) + .withMessage( + 'if with function shows the contents when function returns true' + ) + .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) + .withInput({ goodbye: function() { return this.world; }, world: 'world' - }, - 'GOODBYE cruel world!', - 'if with function shows the contents when function returns string' - ); - shouldCompileTo( - string, - { + }) + .withMessage( + 'if with function shows the contents when function returns string' + ) + .toCompileTo('GOODBYE cruel world!'); + expectTemplate(string) + .withInput({ goodbye: function() { return false; }, world: 'world' - }, - 'cruel world!', - 'if with function does not show the contents when returns false' - ); - shouldCompileTo( - string, - { + }) + .withMessage( + 'if with function does not show the contents when returns false' + ) + .toCompileTo('cruel world!'); + expectTemplate(string) + .withInput({ goodbye: function() { return this.foo; }, world: 'world' - }, - 'cruel world!', - 'if with function does not show the contents when returns undefined' - ); + }) + .withMessage( + 'if with function does not show the contents when returns undefined' + ) + .toCompileTo('cruel world!'); }); it('should not change the depth list', function() { var string = '{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}'; - shouldCompileTo( - string, - { foo: { goodbye: true }, world: 'world' }, - 'GOODBYE cruel world!' - ); + expectTemplate(string) + .withInput({ + foo: { goodbye: true }, + world: 'world' + }) + .toCompileTo('GOODBYE cruel world!'); }); }); describe('#with', function() { it('with', function() { var string = '{{#with person}}{{first}} {{last}}{{/with}}'; - shouldCompileTo( - string, - { person: { first: 'Alan', last: 'Johnson' } }, - 'Alan Johnson' - ); + expectTemplate(string) + .withInput({ + person: { + first: 'Alan', + last: 'Johnson' + } + }) + .toCompileTo('Alan Johnson'); }); it('with with function argument', function() { var string = '{{#with person}}{{first}} {{last}}{{/with}}'; - shouldCompileTo( - string, - { + expectTemplate(string) + .withInput({ person: function() { - return { first: 'Alan', last: 'Johnson' }; + return { + first: 'Alan', + last: 'Johnson' + }; } - }, - 'Alan Johnson' - ); + }) + .toCompileTo('Alan Johnson'); }); it('with with else', function() { var string = '{{#with person}}Person is present{{else}}Person is not present{{/with}}'; - shouldCompileTo(string, {}, 'Person is not present'); + expectTemplate(string).toCompileTo('Person is not present'); }); it('with provides block parameter', function() { var string = '{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}'; - shouldCompileTo( - string, - { person: { first: 'Alan', last: 'Johnson' } }, - 'Alan Johnson' - ); + expectTemplate(string) + .withInput({ + person: { + first: 'Alan', + last: 'Johnson' + } + }) + .toCompileTo('Alan Johnson'); }); it('works when data is disabled', function() { expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}') @@ -170,18 +187,19 @@ describe('builtin helpers', function() { ], world: 'world' }; - shouldCompileTo( - string, - hash, - 'goodbye! Goodbye! GOODBYE! cruel world!', - 'each with array argument iterates over the contents when not empty' - ); - shouldCompileTo( - string, - { goodbyes: [], world: 'world' }, - 'cruel world!', - 'each with array argument ignores the contents when empty' - ); + 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: [], + world: 'world' + }) + .withMessage('each with array argument ignores the contents when empty') + .toCompileTo('cruel world!'); }); it('each without data', function() { @@ -194,23 +212,25 @@ describe('builtin helpers', function() { ], world: 'world' }; - shouldCompileTo( - string, - [hash, , , , false], - 'goodbye! Goodbye! GOODBYE! cruel world!' - ); + expectTemplate(string) + .withInput(hash) + .withRuntimeOptions({ data: false }) + .withCompileOptions({ data: false }) + .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); hash = { goodbyes: 'cruel', world: 'world' }; - shouldCompileTo( - '{{#each .}}{{.}}{{/each}}', - [hash, , , , false], - 'cruelworld' - ); + expectTemplate('{{#each .}}{{.}}{{/each}}') + .withInput(hash) + .withRuntimeOptions({ data: false }) + .withCompileOptions({ data: false }) + .toCompileTo('cruelworld'); }); it('each without context', function() { var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - shouldCompileTo(string, [, , , ,], 'cruel !'); + expectTemplate(string) + .withInput(undefined) + .toCompileTo('cruel !'); }); it('each with an object and @key', function() { @@ -238,7 +258,12 @@ describe('builtin helpers', function() { true, 'each with object argument iterates over the contents when not empty' ); - shouldCompileTo(string, { goodbyes: {}, world: 'world' }, 'cruel world!'); + expectTemplate(string) + .withInput({ + goodbyes: {}, + world: 'world' + }) + .toCompileTo('cruel world!'); }); it('each with @index', function() { @@ -428,18 +453,21 @@ describe('builtin helpers', function() { }, world: 'world' }; - shouldCompileTo( - string, - hash, - 'goodbye! Goodbye! GOODBYE! cruel world!', - 'each with array function argument iterates over the contents when not empty' - ); - shouldCompileTo( - string, - { goodbyes: [], world: 'world' }, - 'cruel world!', - 'each with array function argument ignores the contents when empty' - ); + expectTemplate(string) + .withInput(hash) + .withMessage( + 'each with array function argument iterates over the contents when not empty' + ) + .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); + expectTemplate(string) + .withInput({ + goodbyes: [], + world: 'world' + }) + .withMessage( + 'each with array function argument ignores the contents when empty' + ) + .toCompileTo('cruel world!'); }); it('each object when last key is an empty string', function() { @@ -511,18 +539,21 @@ describe('builtin helpers', function() { ]); var goodbyesEmpty = new Iterable([]); var hash = { goodbyes: goodbyes, world: 'world' }; - shouldCompileTo( - string, - hash, - 'goodbye! Goodbye! GOODBYE! cruel world!', - 'each with array argument iterates over the contents when not empty' - ); - shouldCompileTo( - string, - { goodbyes: goodbyesEmpty, world: 'world' }, - 'cruel world!', - 'each with array argument ignores the contents when empty' - ); + 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, + world: 'world' + }) + .withMessage( + 'each with array argument ignores the contents when empty' + ) + .toCompileTo('cruel world!'); }); } }); @@ -555,7 +586,10 @@ describe('builtin helpers', function() { logArg = arg; }; - shouldCompileTo(string, hash, '', 'log should not display'); + expectTemplate(string) + .withInput(hash) + .withMessage('log should not display') + .toCompileTo(''); equals(1, levelArg, 'should call log with 1'); equals('whee', logArg, "should call log with 'whee'"); }); @@ -569,7 +603,11 @@ describe('builtin helpers', function() { logArg = arg; }; - shouldCompileTo(string, [hash, , , , { level: '03' }], ''); + expectTemplate(string) + .withInput(hash) + .withRuntimeOptions({ data: { level: '03' } }) + .withCompileOptions({ data: true }) + .toCompileTo(''); equals('03', levelArg); equals('whee', logArg); }); @@ -591,7 +629,9 @@ describe('builtin helpers', function() { console.log = $log; }; - shouldCompileTo(string, hash, ''); + expectTemplate(string) + .withInput(hash) + .toCompileTo(''); equals(true, called); }); it('should log at data level', function() { @@ -605,7 +645,11 @@ describe('builtin helpers', function() { console.error = $error; }; - shouldCompileTo(string, [hash, , , , { level: '03' }], ''); + expectTemplate(string) + .withInput(hash) + .withRuntimeOptions({ data: { level: '03' } }) + .withCompileOptions({ data: true }) + .toCompileTo(''); equals(true, called); }); it('should handle missing logger', function() { @@ -620,7 +664,11 @@ describe('builtin helpers', function() { console.log = $log; }; - shouldCompileTo(string, [hash, , , , { level: '03' }], ''); + expectTemplate(string) + .withInput(hash) + .withRuntimeOptions({ data: { level: '03' } }) + .withCompileOptions({ data: true }) + .toCompileTo(''); equals(true, called); }); @@ -634,12 +682,20 @@ describe('builtin helpers', function() { called = true; }; - shouldCompileTo(string, [hash, , , , { level: 'error' }], ''); + expectTemplate(string) + .withInput(hash) + .withRuntimeOptions({ data: { level: 'error' } }) + .withCompileOptions({ data: true }) + .toCompileTo(''); equals(true, called); called = false; - shouldCompileTo(string, [hash, , , , { level: 'ERROR' }], ''); + expectTemplate(string) + .withInput(hash) + .withRuntimeOptions({ data: { level: 'ERROR' } }) + .withCompileOptions({ data: true }) + .toCompileTo(''); equals(true, called); }); it('should handle hash log levels', function() { @@ -652,7 +708,9 @@ describe('builtin helpers', function() { called = true; }; - shouldCompileTo(string, hash, ''); + expectTemplate(string) + .withInput(hash) + .toCompileTo(''); equals(true, called); }); it('should handle hash log levels', function() { @@ -665,7 +723,9 @@ describe('builtin helpers', function() { console.info = console.log = console.error = console.debug = $log; }; - shouldCompileTo(string, hash, ''); + expectTemplate(string) + .withInput(hash) + .toCompileTo(''); equals(false, called); }); it('should pass multiple log arguments', function() { @@ -681,7 +741,9 @@ describe('builtin helpers', function() { console.log = $log; }; - shouldCompileTo(string, hash, ''); + expectTemplate(string) + .withInput(hash) + .toCompileTo(''); equals(true, called); }); diff --git a/spec/helpers.js b/spec/helpers.js index b3eefa236..59960b384 100644 --- a/spec/helpers.js +++ b/spec/helpers.js @@ -12,11 +12,10 @@ describe('helpers', function() { ); } }; - shouldCompileTo( - string, - [hash, helpers], - 'Goodbye' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .toCompileTo('Goodbye'); }); it('helper for raw block gets raw content', function() { @@ -27,12 +26,11 @@ describe('helpers', function() { return options.fn(); } }; - shouldCompileTo( - string, - [hash, helpers], - ' {{test}} ', - 'raw block helper gets raw content' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .withMessage('raw block helper gets raw content') + .toCompileTo(' {{test}} '); }); it('helper for raw block gets parameters', function() { @@ -43,12 +41,11 @@ describe('helpers', function() { return options.fn() + a + b + c; } }; - shouldCompileTo( - string, - [hash, helpers], - ' {{test}} 123', - 'raw block helper gets raw content' - ); + 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() { @@ -58,7 +55,9 @@ describe('helpers', function() { return options.fn(); } }; - shouldCompileTo(template, [{}, helpers], expected); + expectTemplate(template) + .withHelpers(helpers) + .toCompileTo(expected); } it('helper for nested raw block gets raw content', function() { @@ -109,11 +108,10 @@ describe('helpers', function() { return out; } }; - shouldCompileTo( - string, - [hash, helpers], - 'Goodbye Alan! goodbye Alan! GOODBYE Alan! ' - ); + 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}}'; @@ -128,11 +126,10 @@ describe('helpers', function() { return out; } }; - shouldCompileTo( - string, - [hash, helpers], - 'Goodbye Alan! goodbye Alan! GOODBYE Alan! ' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .toCompileTo('Goodbye Alan! goodbye Alan! GOODBYE Alan! '); }); it('helper with complex lookup and nested template', function() { @@ -155,12 +152,10 @@ describe('helpers', function() { ); } }; - shouldCompileToWithPartials( - string, - [hash, helpers], - false, - 'Goodbye' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .toCompileTo('Goodbye'); }); it('helper with complex lookup and nested template in VM+Compiler', function() { @@ -183,20 +178,22 @@ describe('helpers', function() { ); } }; - shouldCompileToWithPartials( - string, - [hash, helpers], - true, - 'Goodbye' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .toCompileTo('Goodbye'); }); it('helper returning undefined value', function() { - shouldCompileTo(' {{nothere}}', [{}, { nothere: function() {} }], ' '); - shouldCompileTo( - ' {{#nothere}}{{/nothere}}', - [{}, { nothere: function() {} }], - ' ' - ); + expectTemplate(' {{nothere}}') + .withHelpers({ + nothere: function() {} + }) + .toCompileTo(' '); + expectTemplate(' {{#nothere}}{{/nothere}}') + .withHelpers({ + nothere: function() {} + }) + .toCompileTo(' '); }); it('block helper', function() { @@ -246,15 +243,16 @@ describe('helpers', function() { ] }; - shouldCompileTo( - source, - [data, { link: link }], - '' - ); + expectTemplate(source) + .withInput(data) + .withHelpers({ link: link }) + .toCompileTo( + '' + ); }); it('block helper for undefined value', function() { - shouldCompileTo("{{#empty}}shouldn't render{{/empty}}", {}, ''); + expectTemplate("{{#empty}}shouldn't render{{/empty}}").toCompileTo(''); }); it('block helper passing a new context', function() { @@ -342,24 +340,21 @@ describe('helpers', function() { // the meaning here may be kind of hard to catch, but list.not is always called, // so we should see the output of both - shouldCompileTo( - string, - [hash, { list: list }], - '
  • Alan
  • Yehuda
', - 'an inverse wrapper is passed in as a new context' - ); - shouldCompileTo( - string, - [empty, { list: list }], - "

Nobody's here

", - 'an inverse wrapper can be optionally called' - ); - shouldCompileTo( - messageString, - [rootMessage, { list: list }], - '

Nobody's here

', - 'the context of an inverse is the parent of the block' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers({ list: list }) + .withMessage('an inverse wrapper is passed in as a new context') + .toCompileTo('
  • Alan
  • Yehuda
'); + expectTemplate(string) + .withInput(empty) + .withHelpers({ list: list }) + .withMessage('an inverse wrapper can be optionally called') + .toCompileTo("

Nobody's here

"); + expectTemplate(messageString) + .withInput(rootMessage) + .withHelpers({ list: list }) + .withMessage('the context of an inverse is the parent of the block') + .toCompileTo('

Nobody's here

'); }); it('pathed lambas with parameters', function() { @@ -374,84 +369,70 @@ describe('helpers', function() { return 'fail'; } }; - shouldCompileTo('{{./helper 1}}', [hash, helpers], 'winning'); - shouldCompileTo('{{hash/helper 1}}', [hash, helpers], 'winning'); + expectTemplate('{{./helper 1}}') + .withInput(hash) + .withHelpers(helpers) + .toCompileTo('winning'); + expectTemplate('{{hash/helper 1}}') + .withInput(hash) + .withHelpers(helpers) + .toCompileTo('winning'); }); describe('helpers hash', function() { it('providing a helpers hash', function() { - shouldCompileTo( - 'Goodbye {{cruel}} {{world}}!', - [ - { cruel: 'cruel' }, - { - world: function() { - return 'world'; - } + expectTemplate('Goodbye {{cruel}} {{world}}!') + .withInput({ cruel: 'cruel' }) + .withHelpers({ + world: function() { + return 'world'; } - ], - 'Goodbye cruel world!', - 'helpers hash is available' - ); - - shouldCompileTo( - 'Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!', - [ - { iter: [{ cruel: 'cruel' }] }, - { - world: function() { - return 'world'; - } + }) + .withMessage('helpers hash is available') + .toCompileTo('Goodbye cruel world!'); + + expectTemplate('Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!') + .withInput({ iter: [{ cruel: 'cruel' }] }) + .withHelpers({ + world: function() { + return 'world'; } - ], - 'Goodbye cruel world!', - 'helpers hash is available inside other blocks' - ); + }) + .withMessage('helpers hash is available inside other blocks') + .toCompileTo('Goodbye cruel world!'); }); it('in cases of conflict, helpers win', function() { - shouldCompileTo( - '{{{lookup}}}', - [ - { lookup: 'Explicit' }, - { - lookup: function() { - return 'helpers'; - } + expectTemplate('{{{lookup}}}') + .withInput({ lookup: 'Explicit' }) + .withHelpers({ + lookup: function() { + return 'helpers'; } - ], - 'helpers', - 'helpers hash has precedence escaped expansion' - ); - shouldCompileTo( - '{{lookup}}', - [ - { lookup: 'Explicit' }, - { - lookup: function() { - return 'helpers'; - } + }) + .withMessage('helpers hash has precedence escaped expansion') + .toCompileTo('helpers'); + expectTemplate('{{lookup}}') + .withInput({ lookup: 'Explicit' }) + .withHelpers({ + lookup: function() { + return 'helpers'; } - ], - 'helpers', - 'helpers hash has precedence simple expansion' - ); + }) + .withMessage('helpers hash has precedence simple expansion') + .toCompileTo('helpers'); }); it('the helpers hash is available is nested contexts', function() { - shouldCompileTo( - '{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}', - [ - { outer: { inner: { unused: [] } } }, - { - helper: function() { - return 'helper'; - } + expectTemplate('{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}') + .withInput({ outer: { inner: { unused: [] } } }) + .withHelpers({ + helper: function() { + return 'helper'; } - ], - 'helper', - 'helpers hash is available in nested contexts.' - ); + }) + .withMessage('helpers hash is available in nested contexts.') + .toCompileTo('helper'); }); it('the helper hash should augment the global hash', function() { @@ -459,18 +440,16 @@ describe('helpers', function() { return 'found it!'; }); - shouldCompileTo( - '{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}', - [ - { cruel: 'cruel' }, - { - world: function() { - return 'world!'; - } + expectTemplate( + '{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}' + ) + .withInput({ cruel: 'cruel' }) + .withHelpers({ + world: function() { + return 'world!'; } - ], - 'found it! Goodbye cruel world!!' - ); + }) + .toCompileTo('found it! Goodbye cruel world!!'); }); }); @@ -499,11 +478,11 @@ describe('helpers', function() { } }); - shouldCompileTo( - '{{testHelper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}', - [{ cruel: 'cruel' }], - 'found it! Goodbye cruel world!!' - ); + expectTemplate( + '{{testHelper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}' + ) + .withInput({ cruel: 'cruel' }) + .toCompileTo('found it! Goodbye cruel world!!'); }); it('fails with multiple and args', function() { shouldThrow( @@ -539,12 +518,10 @@ describe('helpers', function() { return 'Hello ' + times + ' ' + times2 + ' times'; } }; - shouldCompileTo( - string, - [{}, helpers], - 'Message: Hello -1.2 1.2 times', - 'template with a negative integer literal' - ); + 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() { @@ -557,12 +534,10 @@ describe('helpers', function() { return 'Hello ' + times + ' times'; } }; - shouldCompileTo( - string, - [{}, helpers], - 'Message: Hello -12 times', - 'template with a negative integer literal' - ); + expectTemplate(string) + .withHelpers(helpers) + .withMessage('template with a negative integer literal') + .toCompileTo('Message: Hello -12 times'); }); describe('String literal parameters', function() { @@ -584,12 +559,10 @@ describe('helpers', function() { ); } }; - shouldCompileTo( - string, - [{}, helpers], - 'Message: Hello world 12 times: true false', - 'template with a simple String literal' - ); + 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() { @@ -604,12 +577,10 @@ describe('helpers', function() { return 'Hello ' + param; } }; - shouldCompileTo( - string, - [{}, helpers], - 'Message: Hello "world"', - 'template with an escaped String literal' - ); + expectTemplate(string) + .withHelpers(helpers) + .withMessage('template with an escaped String literal') + .toCompileTo('Message: Hello "world"'); }); it("it works with ' marks", function() { @@ -619,12 +590,10 @@ describe('helpers', function() { return 'Hello ' + param; } }; - shouldCompileTo( - string, - [{}, helpers], - "Message: Hello Alan's world", - "template with a ' mark" - ); + expectTemplate(string) + .withHelpers(helpers) + .withMessage("template with a ' mark") + .toCompileTo("Message: Hello Alan's world"); }); }); @@ -638,12 +607,10 @@ describe('helpers', function() { return 'Hello ' + times + ' times'; } }; - shouldCompileTo( - string, - [{}, helpers], - 'Message: Hello -12 times', - 'template with a negative integer literal' - ); + expectTemplate(string) + .withHelpers(helpers) + .withMessage('template with a negative integer literal') + .toCompileTo('Message: Hello -12 times'); }); describe('multiple parameters', function() { @@ -655,12 +622,11 @@ describe('helpers', function() { return 'Goodbye ' + cruel + ' ' + world; } }; - shouldCompileTo( - string, - [hash, helpers], - 'Message: Goodbye cruel world', - 'regular helpers with multiple params' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .withMessage('regular helpers with multiple params') + .toCompileTo('Message: Goodbye cruel world'); }); it('block multi-params work', function() { @@ -672,12 +638,11 @@ describe('helpers', function() { return options.fn({ greeting: 'Goodbye', adj: cruel, noun: world }); } }; - shouldCompileTo( - string, - [hash, helpers], - 'Message: Goodbye cruel world', - 'block helpers with multiple params' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .withMessage('block helpers with multiple params') + .toCompileTo('Message: Goodbye cruel world'); }); }); @@ -826,7 +791,10 @@ describe('helpers', function() { } }; - shouldCompileTo(string, [context, helpers], 'Hello world'); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toCompileTo('Hello world'); }); it('if a value is not found, custom helperMissing is used', function() { @@ -841,7 +809,10 @@ describe('helpers', function() { } }; - shouldCompileTo(string, [context, helpers], 'Hello winning'); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toCompileTo('Hello winning'); }); }); @@ -962,7 +933,9 @@ describe('helpers', function() { return true; } }; - shouldCompileTo(string, data, 'yep'); + expectTemplate(string) + .withInput(data) + .toCompileTo('yep'); }); it('lambdas resolved by blockHelperMissing are bound to the context', function() { var string = '{{#truthy}}yep{{/truthy}}'; @@ -974,7 +947,9 @@ describe('helpers', function() { return false; } }; - shouldCompileTo(string, boundData, ''); + expectTemplate(string) + .withInput(boundData) + .toCompileTo(''); }); }); @@ -993,31 +968,34 @@ describe('helpers', function() { }; it('should include in ambiguous mustache calls', function() { - shouldCompileTo('{{helper}}', [context, helpers], 'ran: helper'); + expectTemplate('{{helper}}') + .withInput(context) + .withHelpers(helpers) + .toCompileTo('ran: helper'); }); it('should include in helper mustache calls', function() { - shouldCompileTo('{{helper 1}}', [context, helpers], 'ran: helper'); + expectTemplate('{{helper 1}}') + .withInput(context) + .withHelpers(helpers) + .toCompileTo('ran: helper'); }); it('should include in ambiguous block calls', function() { - shouldCompileTo( - '{{#helper}}{{/helper}}', - [context, helpers], - 'ran: helper' - ); + expectTemplate('{{#helper}}{{/helper}}') + .withInput(context) + .withHelpers(helpers) + .toCompileTo('ran: helper'); }); it('should include in simple block calls', function() { - shouldCompileTo( - '{{#./helper}}{{/./helper}}', - [context, helpers], - 'missing: ./helper' - ); + expectTemplate('{{#./helper}}{{/./helper}}') + .withInput(context) + .withHelpers(helpers) + .toCompileTo('missing: ./helper'); }); it('should include in helper block calls', function() { - shouldCompileTo( - '{{#helper 1}}{{/helper}}', - [context, helpers], - 'ran: helper' - ); + expectTemplate('{{#helper 1}}{{/helper}}') + .withInput(context) + .withHelpers(helpers) + .toCompileTo('ran: helper'); }); it('should include in known helper calls', function() { var string = '{{helper}}'; @@ -1033,19 +1011,17 @@ describe('helpers', function() { }); it('should include full id', function() { - shouldCompileTo( - '{{#foo.helper}}{{/foo.helper}}', - [{ foo: {} }, helpers], - 'missing: foo.helper' - ); + expectTemplate('{{#foo.helper}}{{/foo.helper}}') + .withInput({ foo: {} }) + .withHelpers(helpers) + .toCompileTo('missing: foo.helper'); }); it('should include full id if a hash is passed', function() { - shouldCompileTo( - '{{#foo.helper bar=baz}}{{/foo.helper}}', - [{ foo: {} }, helpers], - 'helper missing: foo.helper' - ); + expectTemplate('{{#foo.helper bar=baz}}{{/foo.helper}}') + .withInput({ foo: {} }) + .withHelpers(helpers) + .toCompileTo('helper missing: foo.helper'); }); }); @@ -1160,11 +1136,10 @@ describe('helpers', function() { return options.fn({ value: 'bar' }, { blockParams: [1, 2] }); } }; - shouldCompileTo( - '{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}', - [hash, helpers], - '1foo' - ); + expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}') + .withInput(hash) + .withHelpers(helpers) + .toCompileTo('1foo'); }); it('should take presedence over helper values', function() { var hash = {}; @@ -1177,11 +1152,10 @@ describe('helpers', function() { return options.fn({}, { blockParams: [1, 2] }); } }; - shouldCompileTo( - '{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}', - [hash, helpers], - '1foo' - ); + 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' }; @@ -1194,11 +1168,12 @@ describe('helpers', function() { return options.fn(this, { blockParams: [1, 2] }); } }; - shouldCompileTo( - '{{#goodbyes as |value|}}{{./value}}{{/goodbyes}}{{value}}', - [hash, helpers], - 'barfoo' - ); + expectTemplate( + '{{#goodbyes as |value|}}{{./value}}{{/goodbyes}}{{value}}' + ) + .withInput(hash) + .withHelpers(helpers) + .toCompileTo('barfoo'); }); it('should take presednece over parent block params', function() { var hash = { value: 'foo' }, @@ -1214,11 +1189,12 @@ describe('helpers', function() { ); } }; - shouldCompileTo( - '{{#goodbyes as |value|}}{{#goodbyes}}{{value}}{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{/goodbyes}}{{/goodbyes}}{{value}}', - [hash, helpers], - '13foo' - ); + 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() { @@ -1229,11 +1205,12 @@ describe('helpers', function() { return options.fn({ value: 'bar' }, { blockParams: [1, 2] }); } }; - shouldCompileTo( - '{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}', - [hash, helpers], - '1foo' - ); + expectTemplate( + '{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}' + ) + .withInput(hash) + .withHelpers(helpers) + .toCompileTo('1foo'); }); }); diff --git a/spec/javascript-compiler.js b/spec/javascript-compiler.js index e97cbb083..ed2dc8c56 100644 --- a/spec/javascript-compiler.js +++ b/spec/javascript-compiler.js @@ -20,14 +20,18 @@ describe('javascript-compiler api', function() { return parent + '.bar_' + name; }; /* eslint-disable camelcase */ - shouldCompileTo('{{foo}}', { bar_foo: 'food' }, 'food'); + expectTemplate('{{foo}}') + .withInput({ bar_foo: 'food' }) + .toCompileTo('food'); /* eslint-enable camelcase */ }); // Tests nameLookup dot vs. bracket behavior. Bracket is required in certain cases // to avoid errors in older browsers. it('should handle reserved words', function() { - shouldCompileTo('{{foo}} {{~null~}}', { foo: 'food' }, 'food'); + expectTemplate('{{foo}} {{~null~}}') + .withInput({ foo: 'food' }) + .toCompileTo('food'); }); }); describe('#compilerInfo', function() { @@ -49,7 +53,9 @@ describe('javascript-compiler api', function() { throw new Error("It didn't work"); } }; - shouldCompileTo('{{foo}} ', { foo: 'food' }, 'food '); + expectTemplate('{{foo}} ') + .withInput({ foo: 'food' }) + .toCompileTo('food '); }); }); describe('buffer', function() { @@ -70,7 +76,9 @@ describe('javascript-compiler api', function() { handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer = function() { return this.quotedString('foo_'); }; - shouldCompileTo('{{foo}} ', { foo: 'food' }, 'foo_food '); + expectTemplate('{{foo}} ') + .withInput({ foo: 'food' }) + .toCompileTo('foo_food '); }); it('should allow append buffer override', function() { handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function( @@ -78,7 +86,9 @@ describe('javascript-compiler api', function() { ) { return $superAppend.call(this, [string, ' + "_foo"']); }; - shouldCompileTo('{{foo}}', { foo: 'food' }, 'food_foo'); + expectTemplate('{{foo}}') + .withInput({ foo: 'food' }) + .toCompileTo('food_foo'); }); }); diff --git a/spec/partials.js b/spec/partials.js index 019c71d94..a98bfd23f 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -8,18 +8,16 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }], - true, - 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' - ); - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }, , false], - true, - 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .withRuntimeOptions({ data: false }) + .withCompileOptions({ data: false }) + .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); }); it('dynamic partials', function() { @@ -36,18 +34,18 @@ describe('partials', function() { return 'dude'; } }; - shouldCompileToWithPartials( - string, - [hash, helpers, { dude: partial }], - true, - 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' - ); - shouldCompileToWithPartials( - string, - [hash, helpers, { dude: partial }, , false], - true, - 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ' - ); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .withPartials({ dude: partial }) + .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); + expectTemplate(string) + .withInput(hash) + .withHelpers(helpers) + .withPartials({ dude: partial }) + .withRuntimeOptions({ data: false }) + .withCompileOptions({ data: false }) + .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); }); it('failing dynamic partials', function() { var string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}'; @@ -79,13 +77,11 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }], - true, - 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ', - 'Partials can be passed a context' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .withMessage('Partials can be passed a context') + .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) '); }); it('partials with no context', function() { @@ -96,42 +92,36 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - 'Dudes: {{#dudes}}{{>dude}}{{/dudes}}', - [hash, {}, { dude: partial }, { explicitPartialContext: true }], - true, - 'Dudes: () () ' - ); - shouldCompileToWithPartials( - 'Dudes: {{#dudes}}{{>dude name="foo"}}{{/dudes}}', - [hash, {}, { dude: partial }, { explicitPartialContext: true }], - true, - 'Dudes: foo () foo () ' - ); + expectTemplate('Dudes: {{#dudes}}{{>dude}}{{/dudes}}') + .withInput(hash) + .withPartials({ dude: partial }) + .withCompileOptions({ explicitPartialContext: true }) + .toCompileTo('Dudes: () () '); + expectTemplate('Dudes: {{#dudes}}{{>dude name="foo"}}{{/dudes}}') + .withInput(hash) + .withPartials({ dude: partial }) + .withCompileOptions({ explicitPartialContext: true }) + .toCompileTo('Dudes: foo () foo () '); }); it('partials with string context', function() { var string = 'Dudes: {{>dude "dudes"}}'; var partial = '{{.}}'; var hash = {}; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }], - true, - 'Dudes: dudes' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .toCompileTo('Dudes: dudes'); }); it('partials with undefined context', function() { var string = 'Dudes: {{>dude dudes}}'; var partial = '{{foo}} Empty'; var hash = {}; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }], - true, - 'Dudes: Empty' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .toCompileTo('Dudes: Empty'); }); it('partials with duplicate parameters', function() { @@ -151,13 +141,11 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }], - true, - 'Dudes: barYehuda (http://yehuda) barAlan (http://alan) ', - 'Basic partials output based on current context.' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .withMessage('Basic partials output based on current context.') + .toCompileTo('Dudes: barYehuda (http://yehuda) barAlan (http://alan) '); }); it('partial in a partial', function() { @@ -170,13 +158,16 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: dude, url: url }], - true, - 'Dudes: Yehuda http://yehuda Alan http://alan ', - 'Partials are rendered inside of other partials' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ + dude: dude, + url: url + }) + .withMessage('Partials are rendered inside of other partials') + .toCompileTo( + 'Dudes: Yehuda http://yehuda Alan http://alan ' + ); }); it('rendering undefined partial throws an exception', function() { @@ -215,51 +206,44 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileTo( - string, - [hash, {}, { dude: partial }], - 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ', - 'Function partials output based in VM.' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ 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' }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: dude }], - true, - 'Dudes: Jeepers Creepers', - 'Regular selectors can follow a partial' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: dude }) + .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' }; - shouldCompileToWithPartials( - string, - [hash, {}, { 'shared/dude': dude }], - true, - 'Dudes: Jeepers', - 'Partials can use literal paths' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ 'shared/dude': dude }) + .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' }; - shouldCompileToWithPartials( - string, - [hash, {}, { 'shared/dude.thing': dude }], - true, - 'Dudes: Jeepers', - 'Partials can use literal with points in paths' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ 'shared/dude.thing': dude }) + .withMessage('Partials can use literal with points in paths') + .toCompileTo('Dudes: Jeepers'); }); it('Global Partials', function() { @@ -268,13 +252,11 @@ describe('partials', function() { var string = 'Dudes: {{> shared/dude}} {{> globalTest}}'; var dude = '{{name}}'; var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - shouldCompileToWithPartials( - string, - [hash, {}, { 'shared/dude': dude }], - true, - 'Dudes: Jeepers Creepers', - 'Partials can use globals or passed' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ 'shared/dude': dude }) + .withMessage('Partials can use globals or passed') + .toCompileTo('Dudes: Jeepers Creepers'); handlebarsEnv.unregisterPartial('globalTest'); equals(handlebarsEnv.partials.globalTest, undefined); @@ -299,52 +281,44 @@ describe('partials', function() { var string = 'Dudes: {{> 404}}'; var dude = '{{name}}'; var hash = { name: 'Jeepers', anotherDude: 'Creepers' }; - shouldCompileToWithPartials( - string, - [hash, {}, { 404: dude }], - true, - 'Dudes: Jeepers', - 'Partials can use literal paths' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ 404: dude }) + .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' }; - shouldCompileToWithPartials( - string, - [hash, {}, { '404/asdf?.bar': dude }], - true, - 'Dudes: Jeepers', - 'Partials can use literal paths' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ '404/asdf?.bar': dude }) + .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' }; - shouldCompileToWithPartials( - string, - [hash, {}, { '+404/asdf?.bar': dude }], - true, - 'Dudes: Jeepers', - 'Partials can use literal paths' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ '+404/asdf?.bar': dude }) + .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' }; - shouldCompileToWithPartials( - string, - [hash, {}, { '+404/asdf?.bar': dude }], - true, - 'Dudes: Jeepers', - 'Partials can use literal paths' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ '+404/asdf?.bar': dude }) + .withMessage('Partials can use literal paths') + .toCompileTo('Dudes: Jeepers'); }); it('should handle empty partial', function() { @@ -356,12 +330,10 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }], - true, - 'Dudes: ' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .toCompileTo('Dudes: '); }); it('throw on missing partial', function() { @@ -378,305 +350,231 @@ describe('partials', function() { describe('partial blocks', function() { it('should render partial block as default', function() { - shouldCompileToWithPartials( - '{{#> dude}}success{{/dude}}', - [{}, {}, {}], - true, - 'success' - ); + expectTemplate('{{#> dude}}success{{/dude}}').toCompileTo('success'); }); it('should execute default block with proper context', function() { - shouldCompileToWithPartials( - '{{#> dude context}}{{value}}{{/dude}}', - [{ context: { value: 'success' } }, {}, {}], - true, - 'success' - ); + expectTemplate('{{#> dude context}}{{value}}{{/dude}}') + .withInput({ context: { value: 'success' } }) + .toCompileTo('success'); }); it('should propagate block parameters to default block', function() { - shouldCompileToWithPartials( - '{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}', - [{ context: { value: 'success' } }, {}, {}], - true, - 'success' - ); + expectTemplate( + '{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}' + ) + .withInput({ context: { value: 'success' } }) + .toCompileTo('success'); }); it('should not use partial block if partial exists', function() { - shouldCompileToWithPartials( - '{{#> dude}}fail{{/dude}}', - [{}, {}, { dude: 'success' }], - true, - 'success' - ); + expectTemplate('{{#> dude}}fail{{/dude}}') + .withPartials({ dude: 'success' }) + .toCompileTo('success'); }); it('should render block from partial', function() { - shouldCompileToWithPartials( - '{{#> dude}}success{{/dude}}', - [{}, {}, { dude: '{{> @partial-block }}' }], - true, - 'success' - ); + expectTemplate('{{#> dude}}success{{/dude}}') + .withPartials({ dude: '{{> @partial-block }}' }) + .toCompileTo('success'); }); it('should be able to render the partial-block twice', function() { - shouldCompileToWithPartials( - '{{#> dude}}success{{/dude}}', - [{}, {}, { dude: '{{> @partial-block }} {{> @partial-block }}' }], - true, - 'success success' - ); + expectTemplate('{{#> dude}}success{{/dude}}') + .withPartials({ dude: '{{> @partial-block }} {{> @partial-block }}' }) + .toCompileTo('success success'); }); it('should render block from partial with context', function() { - shouldCompileToWithPartials( - '{{#> dude}}{{value}}{{/dude}}', - [ - { context: { value: 'success' } }, - {}, - { dude: '{{#with context}}{{> @partial-block }}{{/with}}' } - ], - true, - 'success' - ); + expectTemplate('{{#> dude}}{{value}}{{/dude}}') + .withInput({ context: { value: 'success' } }) + .withPartials({ + dude: '{{#with context}}{{> @partial-block }}{{/with}}' + }) + .toCompileTo('success'); }); it('should be able to access the @data frame from a partial-block', function() { - shouldCompileToWithPartials( - '{{#> dude}}in-block: {{@root/value}}{{/dude}}', - [ - { value: 'success' }, - {}, - { - dude: - 'before-block: {{@root/value}} {{> @partial-block }}' - } - ], - true, - 'before-block: success in-block: success' - ); + expectTemplate('{{#> dude}}in-block: {{@root/value}}{{/dude}}') + .withInput({ value: 'success' }) + .withPartials({ + dude: + 'before-block: {{@root/value}} {{> @partial-block }}' + }) + .toCompileTo('before-block: success in-block: success'); }); it('should allow the #each-helper to be used along with partial-blocks', function() { - shouldCompileToWithPartials( - '', - [ - { value: ['a', 'b', 'c'] }, - {}, - { - list: - '{{#each .}}{{> @partial-block}}{{/each}}' - } - ], - true, - '' - ); + expectTemplate( + '' + ) + .withInput({ + value: ['a', 'b', 'c'] + }) + .withPartials({ + list: + '{{#each .}}{{> @partial-block}}{{/each}}' + }) + .toCompileTo( + '' + ); }); it('should render block from partial with context (twice)', function() { - shouldCompileToWithPartials( - '{{#> dude}}{{value}}{{/dude}}', - [ - { context: { value: 'success' } }, - {}, - { - dude: - '{{#with context}}{{> @partial-block }} {{> @partial-block }}{{/with}}' - } - ], - true, - 'success success' - ); + expectTemplate('{{#> dude}}{{value}}{{/dude}}') + .withInput({ context: { value: 'success' } }) + .withPartials({ + dude: + '{{#with context}}{{> @partial-block }} {{> @partial-block }}{{/with}}' + }) + .toCompileTo('success success'); }); it('should render block from partial with context', function() { - shouldCompileToWithPartials( - '{{#> dude}}{{../context/value}}{{/dude}}', - [ - { context: { value: 'success' } }, - {}, - { dude: '{{#with context}}{{> @partial-block }}{{/with}}' } - ], - true, - 'success' - ); + expectTemplate('{{#> dude}}{{../context/value}}{{/dude}}') + .withInput({ context: { value: 'success' } }) + .withPartials({ + dude: '{{#with context}}{{> @partial-block }}{{/with}}' + }) + .toCompileTo('success'); }); it('should render block from partial with block params', function() { - shouldCompileToWithPartials( - '{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}', - [ - { context: { value: 'success' } }, - {}, - { dude: '{{> @partial-block }}' } - ], - true, - 'success' - ); + expectTemplate( + '{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}' + ) + .withInput({ context: { value: 'success' } }) + .withPartials({ dude: '{{> @partial-block }}' }) + .toCompileTo('success'); }); it('should render nested partial blocks', function() { - shouldCompileToWithPartials( - '', - [ - { value: 'success' }, - {}, - { - outer: - '{{#> nested}}{{> @partial-block}}{{/nested}}', - nested: '{{> @partial-block}}' - } - ], - true, - '' - ); + expectTemplate('') + .withInput({ value: 'success' }) + .withPartials({ + outer: + '{{#> nested}}{{> @partial-block}}{{/nested}}', + nested: '{{> @partial-block}}' + }) + .toCompileTo( + '' + ); }); it('should render nested partial blocks at different nesting levels', function() { - shouldCompileToWithPartials( - '', - [ - { value: 'success' }, - {}, - { - outer: - '{{#> nested}}{{> @partial-block}}{{/nested}}{{> @partial-block}}', - nested: '{{> @partial-block}}' - } - ], - true, - '' - ); + expectTemplate('') + .withInput({ value: 'success' }) + .withPartials({ + outer: + '{{#> nested}}{{> @partial-block}}{{/nested}}{{> @partial-block}}', + nested: '{{> @partial-block}}' + }) + .toCompileTo( + '' + ); }); it('should render nested partial blocks at different nesting levels (twice)', function() { - shouldCompileToWithPartials( - '', - [ - { value: 'success' }, - {}, - { - outer: - '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}{{> @partial-block}}+{{> @partial-block}}', - nested: '{{> @partial-block}}' - } - ], - true, - '' - ); + expectTemplate('') + .withInput({ value: 'success' }) + .withPartials({ + outer: + '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}{{> @partial-block}}+{{> @partial-block}}', + nested: '{{> @partial-block}}' + }) + .toCompileTo( + '' + ); }); it('should render nested partial blocks (twice at each level)', function() { - shouldCompileToWithPartials( - '', - [ - { value: 'success' }, - {}, - { - outer: - '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}', - nested: '{{> @partial-block}}{{> @partial-block}}' - } - ], - true, - '' - ); + expectTemplate('') + .withInput({ value: 'success' }) + .withPartials({ + outer: + '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}', + nested: '{{> @partial-block}}{{> @partial-block}}' + }) + .toCompileTo( + '' + ); }); }); describe('inline partials', function() { it('should define inline partials for template', function() { - shouldCompileTo( - '{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', - {}, - 'success' - ); + expectTemplate( + '{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}' + ).toCompileTo('success'); }); it('should overwrite multiple partials in the same template', function() { - shouldCompileTo( - '{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', - {}, - 'success' - ); + expectTemplate( + '{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}' + ).toCompileTo('success'); }); it('should define inline partials for block', function() { - shouldCompileTo( - '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}', - {}, - 'success' - ); + expectTemplate( + '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}' + ).toCompileTo('success'); expectTemplate( '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}' ).toThrow(Error, /myPartial could not/); }); it('should override global partials', function() { - shouldCompileTo( - '{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', - { - hash: {}, - partials: { - myPartial: function() { - return 'fail'; - } + expectTemplate( + '{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}' + ) + .withPartials({ + myPartial: function() { + return 'fail'; } - }, - 'success' - ); + }) + .toCompileTo('success'); }); it('should override template partials', function() { - shouldCompileTo( - '{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}', - {}, - 'success' - ); + expectTemplate( + '{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}' + ).toCompileTo('success'); }); it('should override partials down the entire stack', function() { - shouldCompileTo( - '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}', - {}, - 'success' - ); + expectTemplate( + '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}' + ).toCompileTo('success'); }); it('should define inline partials for partial call', function() { - shouldCompileToWithPartials( - '{{#*inline "myPartial"}}success{{/inline}}{{> dude}}', - [{}, {}, { dude: '{{> myPartial }}' }], - true, - 'success' - ); + expectTemplate('{{#*inline "myPartial"}}success{{/inline}}{{> dude}}') + .withPartials({ dude: '{{> myPartial }}' }) + .toCompileTo('success'); }); it('should define inline partials in partial block call', function() { - shouldCompileToWithPartials( - '{{#> dude}}{{#*inline "myPartial"}}success{{/inline}}{{/dude}}', - [{}, {}, { dude: '{{> myPartial }}' }], - true, - 'success' - ); + expectTemplate( + '{{#> dude}}{{#*inline "myPartial"}}success{{/inline}}{{/dude}}' + ) + .withPartials({ dude: '{{> myPartial }}' }) + .toCompileTo('success'); }); it('should render nested inline partials', function() { - shouldCompileToWithPartials( + expectTemplate( '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}}{{/inner}}{{/inline}}' + '{{#*inline "inner"}}{{>@partial-block}}{{/inline}}' + - '{{#>outer}}{{value}}{{/outer}}', - [{ value: 'success' }, {}, {}], - true, - 'success' - ); + '{{#>outer}}{{value}}{{/outer}}' + ) + .withInput({ value: 'success' }) + .toCompileTo('success'); }); it('should render nested inline partials with partial-blocks on different nesting levels', function() { - shouldCompileToWithPartials( + expectTemplate( '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}}{{/inner}}{{>@partial-block}}{{/inline}}' + '{{#*inline "inner"}}{{>@partial-block}}{{/inline}}' + - '{{#>outer}}{{value}}{{/outer}}', - [{ value: 'success' }, {}, {}], - true, - 'successsuccess' - ); + '{{#>outer}}{{value}}{{/outer}}' + ) + .withInput({ value: 'success' }) + .toCompileTo( + 'successsuccess' + ); }); it('should render nested inline partials (twice at each level)', function() { - shouldCompileToWithPartials( + expectTemplate( '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}} {{>@partial-block}}{{/inner}}{{/inline}}' + '{{#*inline "inner"}}{{>@partial-block}}{{>@partial-block}}{{/inline}}' + - '{{#>outer}}{{value}}{{/outer}}', - [{ value: 'success' }, {}, {}], - true, - 'success successsuccess success' - ); + '{{#>outer}}{{value}}{{/outer}}' + ) + .withInput({ value: 'success' }) + .toCompileTo( + 'success successsuccess success' + ); }); }); @@ -699,12 +597,10 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: dude }], - true, - 'Dudes:\n Yehuda\n Alan\n' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: dude }) + .toCompileTo('Dudes:\n Yehuda\n Alan\n'); }); it('nested indented partials', function() { var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}'; @@ -716,12 +612,15 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: dude, url: url }], - true, - 'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ + dude: dude, + url: url + }) + .toCompileTo( + 'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n' + ); }); it('prevent nested indented partials', function() { var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}'; @@ -733,12 +632,16 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: dude, url: url }, { preventIndent: true }], - true, - 'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ + dude: dude, + url: url + }) + .withCompileOptions({ preventIndent: true }) + .toCompileTo( + 'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n' + ); }); }); @@ -753,12 +656,13 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }, true], - true, - 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .withCompileOptions({ compat: true }) + .toCompileTo( + 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' + ); }); it('partials can access parents with custom context', function() { var string = 'Dudes: {{#dudes}}{{> dude "test"}}{{/dudes}}'; @@ -770,12 +674,13 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }, true], - true, - 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .withCompileOptions({ compat: true }) + .toCompileTo( + 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' + ); }); it('partials can access parents without data', function() { var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}'; @@ -787,12 +692,14 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }, true, false], - true, - 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .withRuntimeOptions({ data: false }) + .withCompileOptions({ data: false, compat: true }) + .toCompileTo( + 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' + ); }); it('partials inherit compat', function() { var string = 'Dudes: {{> dude}}'; @@ -804,12 +711,13 @@ describe('partials', function() { { name: 'Alan', url: 'http://alan' } ] }; - shouldCompileToWithPartials( - string, - [hash, {}, { dude: partial }, true], - true, - 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' - ); + expectTemplate(string) + .withInput(hash) + .withPartials({ dude: partial }) + .withCompileOptions({ compat: true }) + .toCompileTo( + 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' + ); }); }); }); diff --git a/spec/regressions.js b/spec/regressions.js index aaa89cef7..6da9672d1 100644 --- a/spec/regressions.js +++ b/spec/regressions.js @@ -14,48 +14,42 @@ describe('Regressions', function() { ] }; var string = '{{#books}}{{title}}{{author.name}}{{/books}}'; - shouldCompileTo( - string, - data, - 'The origin of speciesCharles DarwinLazarillo de Tormes', - 'Renders without an undefined property error' - ); + expectTemplate(string) + .withInput(data) + .withMessage('Renders without an undefined property error') + .toCompileTo('The origin of speciesCharles DarwinLazarillo de Tormes'); }); it("GH-150: Inverted sections print when they shouldn't", function() { var string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}'; - shouldCompileTo( - string, - {}, - 'not set :: ', - "inverted sections run when property isn't present in context" - ); - shouldCompileTo( - string, - { set: undefined }, - 'not set :: ', - 'inverted sections run when property is undefined' - ); - shouldCompileTo( - string, - { set: false }, - 'not set :: ', - 'inverted sections run when property is false' - ); - shouldCompileTo( - string, - { set: true }, - ' :: set', - "inverted sections don't run when property is true" - ); + expectTemplate(string) + .withMessage( + "inverted sections run when property isn't present in context" + ) + .toCompileTo('not set :: '); + expectTemplate(string) + .withInput({ set: undefined }) + .withMessage('inverted sections run when property is undefined') + .toCompileTo('not set :: '); + expectTemplate(string) + .withInput({ set: false }) + .withMessage('inverted sections run when property is false') + .toCompileTo('not set :: '); + expectTemplate(string) + .withInput({ set: true }) + .withMessage("inverted sections don't run when property is true") + .toCompileTo(' :: set'); }); it('GH-158: Using array index twice, breaks the template', function() { var string = '{{arr.[0]}}, {{arr.[1]}}'; var data = { arr: [1, 2] }; - shouldCompileTo(string, data, '1, 2', 'it works as expected'); + expectTemplate(string) + .withInput(data) + .withMessage('it works as expected') + .toCompileTo('1, 2'); }); it("bug reported by @fat where lambdas weren't being properly resolved", function() { @@ -95,7 +89,9 @@ describe('Regressions', function() { '
  • @dhg
  • \n' + '
  • @sayrer
  • \n' + '.\n'; - shouldCompileTo(string, data, output); + expectTemplate(string) + .withInput(data) + .toCompileTo(output); }); it('GH-408: Multiple loops fail', function() { @@ -127,23 +123,31 @@ describe('Regressions', function() { } }; - shouldCompileTo(succeedingTemplate, [{}, helpers], ' Expected '); - shouldCompileTo(failingTemplate, [{}, helpers], ' Expected '); + expectTemplate(succeedingTemplate) + .withHelpers(helpers) + .toCompileTo(' Expected '); + expectTemplate(failingTemplate) + .withHelpers(helpers) + .toCompileTo(' Expected '); }); it('GH-458: Scoped this identifier', function() { - shouldCompileTo('{{./foo}}', { foo: 'bar' }, 'bar'); + expectTemplate('{{./foo}}') + .withInput({ foo: 'bar' }) + .toCompileTo('bar'); }); it('GH-375: Unicode line terminators', function() { - shouldCompileTo('\u2028', {}, '\u2028'); + expectTemplate('\u2028').toCompileTo('\u2028'); }); it('GH-534: Object prototype aliases', function() { /* eslint-disable no-extend-native */ Object.prototype[0xd834] = true; - shouldCompileTo('{{foo}}', { foo: 'bar' }, 'bar'); + expectTemplate('{{foo}}') + .withInput({ foo: 'bar' }) + .toCompileTo('bar'); delete Object.prototype[0xd834]; /* eslint-enable no-extend-native */ @@ -158,7 +162,10 @@ describe('Regressions', function() { var string = '{{arr}}'; var data = { arr: [1, 2] }; - shouldCompileTo(string, data, data.arr.toString(), 'it works as expected'); + expectTemplate(string) + .withInput(data) + .withMessage('it works as expected') + .toCompileTo(data.arr.toString()); }); it('Mustache man page', function() { @@ -171,24 +178,27 @@ describe('Regressions', function() { in_ca: true }; - shouldCompileTo( - string, - data, - 'Hello Chris. You have just won $10000! Well, $6000, after taxes.', - 'the hello world mustache example works' - ); + expectTemplate(string) + .withInput(data) + .withMessage('the hello world mustache example works') + .toCompileTo( + 'Hello Chris. You have just won $10000! Well, $6000, after taxes.' + ); }); it('GH-731: zero context rendering', function() { - shouldCompileTo( - '{{#foo}} This is {{bar}} ~ {{/foo}}', - { foo: 0, bar: 'OK' }, - ' This is ~ ' - ); + expectTemplate('{{#foo}} This is {{bar}} ~ {{/foo}}') + .withInput({ + foo: 0, + bar: 'OK' + }) + .toCompileTo(' This is ~ '); }); it('GH-820: zero pathed rendering', function() { - shouldCompileTo('{{foo.bar}}', { foo: 0 }, ''); + expectTemplate('{{foo.bar}}') + .withInput({ foo: 0 }) + .toCompileTo(''); }); it('GH-837: undefined values for helpers', function() { @@ -198,7 +208,9 @@ describe('Regressions', function() { } }; - shouldCompileTo('{{str bar.baz}}', [{}, helpers], 'undefined'); + expectTemplate('{{str bar.baz}}') + .withHelpers(helpers) + .toCompileTo('undefined'); }); it('GH-926: Depths and de-dupe', function() { @@ -223,11 +235,9 @@ describe('Regressions', function() { value: 10000 }; - shouldCompileTo( - '{{#each data}}Key: {{@key}}\n{{/each}}', - { data: data }, - 'Key: \nKey: name\nKey: value\n' - ); + expectTemplate('{{#each data}}Key: {{@key}}\n{{/each}}') + .withInput({ data: data }) + .toCompileTo('Key: \nKey: name\nKey: value\n'); }); it('GH-1054: Should handle simple safe string responses', function() { @@ -241,23 +251,19 @@ describe('Regressions', function() { } }; - shouldCompileToWithPartials( - root, - [{}, helpers, partials], - true, - '' - ); + expectTemplate(root) + .withHelpers(helpers) + .withPartials(partials) + .toCompileTo(''); }); it('GH-1065: Sparse arrays', function() { var array = []; array[1] = 'foo'; array[3] = 'bar'; - shouldCompileTo( - '{{#each array}}{{@index}}{{.}}{{/each}}', - { array: array }, - '1foo3bar' - ); + expectTemplate('{{#each array}}{{@index}}{{.}}{{/each}}') + .withInput({ array: array }) + .toCompileTo('1foo3bar'); }); it('GH-1093: Undefined helper context', function() { @@ -276,11 +282,10 @@ describe('Regressions', function() { } }; - shouldCompileTo( - '{{#each obj}}{{{helper}}}{{.}}{{/each}}', - [{ obj: obj }, helpers], - 'notfoundbat' - ); + expectTemplate('{{#each obj}}{{{helper}}}{{.}}{{/each}}') + .withInput({ obj: obj }) + .withHelpers(helpers) + .toCompileTo('notfoundbat'); }); it('should support multiple levels of inline partials', function() { @@ -291,12 +296,9 @@ describe('Regressions', function() { layout: '{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}' }; - shouldCompileToWithPartials( - string, - [{}, {}, partials], - true, - 'doctypelayoutsubcontent' - ); + expectTemplate(string) + .withPartials(partials) + .toCompileTo('doctypelayoutsubcontent'); }); it('GH-1089: should support failover content in multiple levels of inline partials', function() { var string = '{{#> layout}}{{/layout}}'; @@ -305,12 +307,9 @@ describe('Regressions', function() { layout: '{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}' }; - shouldCompileToWithPartials( - string, - [{}, {}, partials], - true, - 'doctypelayoutsubcontent' - ); + expectTemplate(string) + .withPartials(partials) + .toCompileTo('doctypelayoutsubcontent'); }); it('GH-1099: should support greater than 3 nested levels of inline partials', function() { var string = '{{#> layout}}Outer{{/layout}}'; @@ -318,7 +317,9 @@ describe('Regressions', function() { layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}', inner: '' }; - shouldCompileToWithPartials(string, [{}, {}, partials], true, 'Outer'); + expectTemplate(string) + .withPartials(partials) + .toCompileTo('Outer'); }); it('GH-1135 : Context handling within each iteration', function() { @@ -333,14 +334,15 @@ describe('Regressions', function() { } }; - shouldCompileTo( + expectTemplate( '{{#each array}}\n' + ' 1. IF: {{#if true}}{{../name}}-{{../../name}}-{{../../../name}}{{/if}}\n' + ' 2. MYIF: {{#myif true}}{{../name}}={{../../name}}={{../../../name}}{{/myif}}\n' + - '{{/each}}', - [obj, helpers], - ' 1. IF: John--\n' + ' 2. MYIF: John==\n' - ); + '{{/each}}' + ) + .withInput(obj) + .withHelpers(helpers) + .toCompileTo(' 1. IF: John--\n' + ' 2. MYIF: John==\n'); }); it('GH-1186: Support block params for existing programs', function() { @@ -349,18 +351,25 @@ describe('Regressions', function() { '{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' + '{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}'; - shouldCompileTo(string, { listOne: ['a'], listTwo: ['b'] }, 'ab', ''); + expectTemplate(string) + .withInput({ + listOne: ['a'], + listTwo: ['b'] + }) + .withMessage('') + .toCompileTo('ab'); }); it('GH-1319: "unless" breaks when "each" value equals "null"', function() { var string = '{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}'; - shouldCompileTo( - string, - { value: 'parent', list: [null, 'a'] }, - 'parent=parent parent=parent ', - '' - ); + expectTemplate(string) + .withInput({ + value: 'parent', + list: [null, 'a'] + }) + .withMessage('') + .toCompileTo('parent=parent parent=parent '); }); it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', function() { @@ -370,12 +379,9 @@ describe('Regressions', function() { '{{#if @partial-block}} block {{> @partial-block}} block {{/if}}', partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}' }; - shouldCompileToWithPartials( - string, - [{}, {}, partials], - true, - 'template block partial block template' - ); + expectTemplate(string) + .withPartials(partials) + .toCompileTo('template block partial block template'); }); describe('GH-1561: 4.3.x should still work with precompiled templates from 4.0.0 <= x < 4.3.0', function() { @@ -481,7 +487,10 @@ describe('Regressions', function() { } }; - shouldCompileTo('{{helpa length="foo"}}', [obj, helpers], 'foo'); + expectTemplate('{{helpa length="foo"}}') + .withInput(obj) + .withHelpers(helpers) + .toCompileTo('foo'); }); describe('GH-1598: Performance degradation for partials since v4.3.0', function() { diff --git a/spec/security.js b/spec/security.js index 68a60e15d..e01dc3db7 100644 --- a/spec/security.js +++ b/spec/security.js @@ -20,36 +20,18 @@ describe('security issues', function() { }); it('should allow the "constructor" property to be accessed if it is an "ownProperty"', function() { - shouldCompileTo( - '{{constructor.name}}', - { - constructor: { - name: 'here we go' - } - }, - 'here we go' - ); - shouldCompileTo( - '{{lookup (lookup this "constructor") "name"}}', - { - constructor: { - name: 'here we go' - } - }, - 'here we go' - ); + expectTemplate('{{constructor.name}}') + .withInput({ constructor: { name: 'here we go' } }) + .toCompileTo('here we go'); + expectTemplate('{{lookup (lookup this "constructor") "name"}}') + .withInput({ constructor: { name: 'here we go' } }) + .toCompileTo('here we go'); }); it('should allow the "constructor" property to be accessed if it is an "own property"', function() { - shouldCompileTo( - '{{lookup (lookup this "constructor") "name"}}', - { - constructor: { - name: 'here we go' - } - }, - 'here we go' - ); + expectTemplate('{{lookup (lookup this "constructor") "name"}}') + .withInput({ constructor: { name: 'here we go' } }) + .toCompileTo('here we go'); }); }); diff --git a/spec/subexpressions.js b/spec/subexpressions.js index 584278831..8f041816a 100644 --- a/spec/subexpressions.js +++ b/spec/subexpressions.js @@ -10,7 +10,10 @@ describe('subexpressions', function() { return 'LOL'; } }; - shouldCompileTo(string, [context, helpers], 'LOLLOL!'); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toCompileTo('LOLLOL!'); }); it('helper w args', function() { @@ -25,7 +28,10 @@ describe('subexpressions', function() { return x === y; } }; - shouldCompileTo(string, [context, helpers], 'val is true'); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toCompileTo('val is true'); }); it('mixed paths and helpers', function() { @@ -40,7 +46,10 @@ describe('subexpressions', function() { return x === y; } }; - shouldCompileTo(string, [context, helpers], 'val is foo!, true and bar!'); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toCompileTo('val is foo!, true and bar!'); }); it('supports much nesting', function() { @@ -55,7 +64,10 @@ describe('subexpressions', function() { return x === y; } }; - shouldCompileTo(string, [context, helpers], 'val is true'); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toCompileTo('val is true'); }); it('GH-800 : Complex subexpressions', function() { @@ -69,15 +81,26 @@ describe('subexpressions', function() { } }; - shouldCompileTo( - "{{dash 'abc' (concat a b)}}", - [context, helpers], - 'abc-ab' - ); - shouldCompileTo('{{dash d (concat a b)}}', [context, helpers], 'd-ab'); - shouldCompileTo('{{dash c.c (concat a b)}}', [context, helpers], 'c-ab'); - shouldCompileTo('{{dash (concat a b) c.c}}', [context, helpers], 'ab-c'); - shouldCompileTo('{{dash (concat a e.e) c.c}}', [context, helpers], 'ae-c'); + expectTemplate("{{dash 'abc' (concat a b)}}") + .withInput(context) + .withHelpers(helpers) + .toCompileTo('abc-ab'); + expectTemplate('{{dash d (concat a b)}}') + .withInput(context) + .withHelpers(helpers) + .toCompileTo('d-ab'); + expectTemplate('{{dash c.c (concat a b)}}') + .withInput(context) + .withHelpers(helpers) + .toCompileTo('c-ab'); + expectTemplate('{{dash (concat a b) c.c}}') + .withInput(context) + .withHelpers(helpers) + .toCompileTo('ab-c'); + expectTemplate('{{dash (concat a e.e) c.c}}') + .withInput(context) + .withHelpers(helpers) + .toCompileTo('ae-c'); }); it('provides each nested helper invocation its own options hash', function() { @@ -93,7 +116,9 @@ describe('subexpressions', function() { return x === y; } }; - shouldCompileTo(string, [{}, helpers], 'true'); + expectTemplate(string) + .withHelpers(helpers) + .toCompileTo('true'); }); it('with hashes', function() { @@ -108,7 +133,10 @@ describe('subexpressions', function() { return x === y; } }; - shouldCompileTo(string, [context, helpers], 'val is true'); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toCompileTo('val is true'); }); it('as hashes', function() { @@ -122,7 +150,9 @@ describe('subexpressions', function() { return x === y; } }; - shouldCompileTo(string, [{}, helpers], 'val is true'); + expectTemplate(string) + .withHelpers(helpers) + .toCompileTo('val is true'); }); it('multiple subexpressions in a hash', function() { @@ -146,11 +176,9 @@ describe('subexpressions', function() { return new Handlebars.SafeString(defaultString); } }; - shouldCompileTo( - string, - [{}, helpers], - '' - ); + expectTemplate(string) + .withHelpers(helpers) + .toCompileTo(''); }); it('multiple subexpressions in a hash with context', function() { @@ -181,11 +209,10 @@ describe('subexpressions', function() { return new Handlebars.SafeString(defaultString); } }; - shouldCompileTo( - string, - [context, helpers], - '' - ); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toCompileTo(''); }); it('in string params mode,', function() { @@ -272,7 +299,10 @@ describe('subexpressions', function() { return val + val; } }; - shouldCompileTo(string, [context, helpers], 'LOLLOL!'); + expectTemplate(string) + .withInput(context) + .withHelpers(helpers) + .toCompileTo('LOLLOL!'); }); it("subexpressions can't just be property lookups", function() { diff --git a/spec/utils.js b/spec/utils.js index 667aa0ed3..f4a4e3e4f 100644 --- a/spec/utils.js +++ b/spec/utils.js @@ -15,11 +15,9 @@ describe('utils', function() { it('it should not escape SafeString properties', function() { var name = new Handlebars.SafeString('Sean O'Malley'); - shouldCompileTo( - '{{name}}', - [{ name: name }], - 'Sean O'Malley' - ); + expectTemplate('{{name}}') + .withInput({ name: name }) + .toCompileTo('Sean O'Malley'); }); }); diff --git a/spec/whitespace-control.js b/spec/whitespace-control.js index b1a6db045..0e7f74221 100644 --- a/spec/whitespace-control.js +++ b/spec/whitespace-control.js @@ -2,125 +2,152 @@ describe('whitespace control', function() { it('should strip whitespace around mustache calls', function() { var hash = { foo: 'bar<' }; - shouldCompileTo(' {{~foo~}} ', hash, 'bar<'); - shouldCompileTo(' {{~foo}} ', hash, 'bar< '); - shouldCompileTo(' {{foo~}} ', hash, ' bar<'); - - shouldCompileTo(' {{~&foo~}} ', hash, 'bar<'); - shouldCompileTo(' {{~{foo}~}} ', hash, 'bar<'); - - shouldCompileTo('1\n{{foo~}} \n\n 23\n{{bar}}4', {}, '1\n23\n4'); + expectTemplate(' {{~foo~}} ') + .withInput(hash) + .toCompileTo('bar<'); + expectTemplate(' {{~foo}} ') + .withInput(hash) + .toCompileTo('bar< '); + expectTemplate(' {{foo~}} ') + .withInput(hash) + .toCompileTo(' bar<'); + + expectTemplate(' {{~&foo~}} ') + .withInput(hash) + .toCompileTo('bar<'); + expectTemplate(' {{~{foo}~}} ') + .withInput(hash) + .toCompileTo('bar<'); + + expectTemplate('1\n{{foo~}} \n\n 23\n{{bar}}4').toCompileTo('1\n23\n4'); }); describe('blocks', function() { it('should strip whitespace around simple block calls', function() { var hash = { foo: 'bar<' }; - shouldCompileTo(' {{~#if foo~}} bar {{~/if~}} ', hash, 'bar'); - shouldCompileTo(' {{#if foo~}} bar {{/if~}} ', hash, ' bar '); - shouldCompileTo(' {{~#if foo}} bar {{~/if}} ', hash, ' bar '); - shouldCompileTo(' {{#if foo}} bar {{/if}} ', hash, ' bar '); - - shouldCompileTo( - ' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', - hash, - 'bar' - ); - shouldCompileTo( - ' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ', - hash, - ' abara ' - ); + expectTemplate(' {{~#if foo~}} bar {{~/if~}} ') + .withInput(hash) + .toCompileTo('bar'); + expectTemplate(' {{#if foo~}} bar {{/if~}} ') + .withInput(hash) + .toCompileTo(' bar '); + expectTemplate(' {{~#if foo}} bar {{~/if}} ') + .withInput(hash) + .toCompileTo(' bar '); + expectTemplate(' {{#if foo}} bar {{/if}} ') + .withInput(hash) + .toCompileTo(' bar '); + + expectTemplate(' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ') + .withInput(hash) + .toCompileTo('bar'); + expectTemplate(' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ') + .withInput(hash) + .toCompileTo(' abara '); }); it('should strip whitespace around inverse block calls', function() { var hash = {}; - shouldCompileTo(' {{~^if foo~}} bar {{~/if~}} ', hash, 'bar'); - shouldCompileTo(' {{^if foo~}} bar {{/if~}} ', hash, ' bar '); - shouldCompileTo(' {{~^if foo}} bar {{~/if}} ', hash, ' bar '); - shouldCompileTo(' {{^if foo}} bar {{/if}} ', hash, ' bar '); - - shouldCompileTo( - ' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', - hash, - 'bar' - ); + expectTemplate(' {{~^if foo~}} bar {{~/if~}} ') + .withInput(hash) + .toCompileTo('bar'); + expectTemplate(' {{^if foo~}} bar {{/if~}} ') + .withInput(hash) + .toCompileTo(' bar '); + expectTemplate(' {{~^if foo}} bar {{~/if}} ') + .withInput(hash) + .toCompileTo(' bar '); + expectTemplate(' {{^if foo}} bar {{/if}} ') + .withInput(hash) + .toCompileTo(' bar '); + + expectTemplate(' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ') + .withInput(hash) + .toCompileTo('bar'); }); it('should strip whitespace around complex block calls', function() { var hash = { foo: 'bar<' }; - shouldCompileTo('{{#if foo~}} bar {{~^~}} baz {{~/if}}', hash, 'bar'); - shouldCompileTo('{{#if foo~}} bar {{^~}} baz {{/if}}', hash, 'bar '); - shouldCompileTo('{{#if foo}} bar {{~^~}} baz {{~/if}}', hash, ' bar'); - shouldCompileTo('{{#if foo}} bar {{^~}} baz {{/if}}', hash, ' bar '); - - shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'bar'); - - shouldCompileTo( - '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', - hash, - 'bar' - ); - shouldCompileTo( - '\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', - hash, - 'bar<' - ); + expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}') + .withInput(hash) + .toCompileTo('bar'); + expectTemplate('{{#if foo~}} bar {{^~}} baz {{/if}}') + .withInput(hash) + .toCompileTo('bar '); + expectTemplate('{{#if foo}} bar {{~^~}} baz {{~/if}}') + .withInput(hash) + .toCompileTo(' bar'); + expectTemplate('{{#if foo}} bar {{^~}} baz {{/if}}') + .withInput(hash) + .toCompileTo(' bar '); + + expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}') + .withInput(hash) + .toCompileTo('bar'); + + expectTemplate( + '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n' + ) + .withInput(hash) + .toCompileTo('bar'); + expectTemplate( + '\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n' + ) + .withInput(hash) + .toCompileTo('bar<'); hash = {}; - shouldCompileTo('{{#if foo~}} bar {{~^~}} baz {{~/if}}', hash, 'baz'); - shouldCompileTo('{{#if foo}} bar {{~^~}} baz {{/if}}', hash, 'baz '); - shouldCompileTo('{{#if foo~}} bar {{~^}} baz {{~/if}}', hash, ' baz'); - shouldCompileTo('{{#if foo~}} bar {{~^}} baz {{/if}}', hash, ' baz '); - - shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'baz'); - - shouldCompileTo( - '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', - hash, - 'baz' - ); + expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}') + .withInput(hash) + .toCompileTo('baz'); + expectTemplate('{{#if foo}} bar {{~^~}} baz {{/if}}') + .withInput(hash) + .toCompileTo('baz '); + expectTemplate('{{#if foo~}} bar {{~^}} baz {{~/if}}') + .withInput(hash) + .toCompileTo(' baz'); + expectTemplate('{{#if foo~}} bar {{~^}} baz {{/if}}') + .withInput(hash) + .toCompileTo(' baz '); + + expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}') + .withInput(hash) + .toCompileTo('baz'); + + expectTemplate( + '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n' + ) + .withInput(hash) + .toCompileTo('baz'); }); }); it('should strip whitespace around partials', function() { - shouldCompileToWithPartials( - 'foo {{~> dude~}} ', - [{}, {}, { dude: 'bar' }], - true, - 'foobar' - ); - shouldCompileToWithPartials( - 'foo {{> dude~}} ', - [{}, {}, { dude: 'bar' }], - true, - 'foo bar' - ); - shouldCompileToWithPartials( - 'foo {{> dude}} ', - [{}, {}, { dude: 'bar' }], - true, - 'foo bar ' - ); - - shouldCompileToWithPartials( - 'foo\n {{~> dude}} ', - [{}, {}, { dude: 'bar' }], - true, - 'foobar' - ); - shouldCompileToWithPartials( - 'foo\n {{> dude}} ', - [{}, {}, { dude: 'bar' }], - true, - 'foo\n bar' - ); + expectTemplate('foo {{~> dude~}} ') + .withPartials({ dude: 'bar' }) + .toCompileTo('foobar'); + expectTemplate('foo {{> dude~}} ') + .withPartials({ dude: 'bar' }) + .toCompileTo('foo bar'); + expectTemplate('foo {{> dude}} ') + .withPartials({ dude: 'bar' }) + .toCompileTo('foo bar '); + + expectTemplate('foo\n {{~> dude}} ') + .withPartials({ dude: 'bar' }) + .toCompileTo('foobar'); + expectTemplate('foo\n {{> dude}} ') + .withPartials({ dude: 'bar' }) + .toCompileTo('foo\n bar'); }); it('should only strip whitespace once', function() { var hash = { foo: 'bar' }; - shouldCompileTo(' {{~foo~}} {{foo}} {{foo}} ', hash, 'barbar bar '); + expectTemplate(' {{~foo~}} {{foo}} {{foo}} ') + .withInput(hash) + .toCompileTo('barbar bar '); }); }); From dd909e95e23d91e025d9f6e9cdc87e994b646a48 Mon Sep 17 00:00:00 2001 From: John Boehr Date: Sun, 26 Apr 2020 17:46:13 -0700 Subject: [PATCH 08/11] Apply formatting requested by @nknapp --- spec/basic.js | 105 +++-- spec/blocks.js | 365 +++++++-------- spec/builtins.js | 434 +++++++++--------- spec/data.js | 346 +++++---------- spec/helpers.js | 886 ++++++++++++++----------------------- spec/partials.js | 405 ++++++++--------- spec/regressions.js | 288 ++++++------ spec/security.js | 18 +- spec/strict.js | 33 +- spec/string-params.js | 321 ++++++-------- spec/subexpressions.js | 405 ++++++++--------- spec/track-ids.js | 434 ++++++++---------- spec/whitespace-control.js | 80 ++-- 13 files changed, 1791 insertions(+), 2329 deletions(-) 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 '
    ' + options.fn(this) + '
    '; - } - }; - - expectTemplate(string) - .withInput(input) - .withHelpers(helpers) + }) .withMessage('Block helper executed with current context') .toCompileTo('

    Yehuda

    '); }); it('block helper should have context in this', function() { - var source = - '
      {{#people}}
    • {{#link}}{{name}}{{/link}}
    • {{/people}}
    '; function link(options) { return '' + options.fn(this) + ''; } - var data = { - people: [ - { name: 'Alan', id: 1 }, - { name: 'Yehuda', id: 2 } - ] - }; - expectTemplate(source) - .withInput(data) - .withHelpers({ link: link }) + expectTemplate( + '
      {{#people}}
    • {{#link}}{{name}}{{/link}}
    • {{/people}}
    ' + ) + .withInput({ + people: [ + { name: 'Alan', id: 1 }, + { name: 'Yehuda', id: 2 } + ] + }) + .withHelper('link', link) .toCompileTo( '' ); @@ -256,58 +208,38 @@ describe('helpers', function() { }); it('block helper passing a new context', function() { - var string = '{{#form yehuda}}

    {{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 '
    ' + options.fn(context) + '
    '; - } - }; - - expectTemplate(string) - .withInput(input) - .withHelpers(helpers) + }) .withMessage('Context variable resolved') .toCompileTo('

    Yehuda

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

    Harold

    '); }); 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 '
    ' + options.fn(context) + '
    '; - } - }; - - expectTemplate(string) - .withInput(input) - .withHelpers(helpers) + }) .withMessage('Both blocks executed') .toCompileTo('

    Yehuda

    Hello
    '); }); @@ -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('
    • Alan
    • Yehuda
    '); + expectTemplate(string) - .withInput(empty) + .withInput({ people: [] }) .withHelpers({ list: list }) .withMessage('an inverse wrapper can be optionally called') .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..df092267d 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,29 @@ 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}}') + .withPartial('dude', '{{foo}} Empty') .toCompileTo('Dudes: Empty'); }); @@ -132,37 +122,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 +179,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 +221,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 +237,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 +304,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 +330,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 +371,7 @@ describe('partials', function() { '' ); }); + it('should render block from partial with context (twice)', function() { expectTemplate('{{#> dude}}{{value}}{{/dude}}') .withInput({ context: { value: 'success' } }) @@ -424,6 +381,7 @@ describe('partials', function() { }) .toCompileTo('success success'); }); + it('should render block from partial with context', function() { expectTemplate('{{#> dude}}{{../context/value}}{{/dude}}') .withInput({ context: { value: 'success' } }) @@ -432,6 +390,7 @@ describe('partials', function() { }) .toCompileTo('success'); }); + it('should render block from partial with block params', function() { expectTemplate( '{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}' @@ -440,6 +399,7 @@ describe('partials', function() { .withPartials({ dude: '{{> @partial-block }}' }) .toCompileTo('success'); }); + it('should render nested partial blocks', function() { expectTemplate('') .withInput({ value: 'success' }) @@ -452,6 +412,7 @@ describe('partials', function() { '' ); }); + it('should render nested partial blocks at different nesting levels', function() { expectTemplate('') .withInput({ value: 'success' }) @@ -464,6 +425,7 @@ describe('partials', function() { '' ); }); + it('should render nested partial blocks at different nesting levels (twice)', function() { expectTemplate('') .withInput({ value: 'success' }) @@ -476,6 +438,7 @@ describe('partials', function() { '' ); }); + it('should render nested partial blocks (twice at each level)', function() { expectTemplate('') .withInput({ value: 'success' }) @@ -498,19 +461,23 @@ describe('partials', function() { '{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}' ).toCompileTo('success'); }); + it('should overwrite multiple partials in the same template', function() { expectTemplate( '{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}' ).toCompileTo('success'); }); + it('should define inline partials for block', function() { expectTemplate( '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}' ).toCompileTo('success'); + expectTemplate( '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}' ).toThrow(Error, /myPartial could not/); }); + it('should override global partials', function() { expectTemplate( '{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}' @@ -522,11 +489,13 @@ describe('partials', function() { }) .toCompileTo('success'); }); + it('should override template partials', function() { expectTemplate( '{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}' ).toCompileTo('success'); }); + it('should override partials down the entire stack', function() { expectTemplate( '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}' @@ -538,6 +507,7 @@ describe('partials', function() { .withPartials({ dude: '{{> myPartial }}' }) .toCompileTo('success'); }); + it('should define inline partials in partial block call', function() { expectTemplate( '{{#> dude}}{{#*inline "myPartial"}}success{{/inline}}{{/dude}}' @@ -545,6 +515,7 @@ describe('partials', function() { .withPartials({ dude: '{{> myPartial }}' }) .toCompileTo('success'); }); + it('should render nested inline partials', function() { expectTemplate( '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}}{{/inner}}{{/inline}}' + @@ -554,6 +525,7 @@ describe('partials', function() { .withInput({ value: 'success' }) .toCompileTo('success'); }); + it('should render nested inline partials with partial-blocks on different nesting levels', function() { expectTemplate( '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}}{{/inner}}{{>@partial-block}}{{/inline}}' + @@ -565,6 +537,7 @@ describe('partials', function() { 'successsuccess' ); }); + it('should render nested inline partials (twice at each level)', function() { expectTemplate( '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}} {{>@partial-block}}{{/inner}}{{/inline}}' + @@ -589,54 +562,45 @@ describe('partials', function() { describe('standalone partials', function() { it('indented partials', function() { - var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}'; - var dude = '{{name}}\n'; - var hash = { - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: dude }) + expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}') + .withInput({ + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withPartial('dude', '{{name}}\n') .toCompileTo('Dudes:\n Yehuda\n Alan\n'); }); + it('nested indented partials', function() { - var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}'; - var dude = '{{name}}\n {{> url}}'; - var url = '{{url}}!\n'; - var hash = { - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) + expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}') + .withInput({ + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) .withPartials({ - dude: dude, - url: url + dude: '{{name}}\n {{> url}}', + url: '{{url}}!\n' }) .toCompileTo( 'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n' ); }); + it('prevent nested indented partials', function() { - var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}'; - var dude = '{{name}}\n {{> url}}'; - var url = '{{url}}!\n'; - var hash = { - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) + expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}') + .withInput({ + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) .withPartials({ - dude: dude, - url: url + dude: '{{name}}\n {{> url}}', + url: '{{url}}!\n' }) .withCompileOptions({ preventIndent: true }) .toCompileTo( @@ -647,73 +611,66 @@ describe('partials', function() { describe('compat mode', function() { it('partials can access parents', function() { - var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}'; - var partial = '{{name}} ({{url}}) {{root}} '; - var hash = { - root: 'yes', - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}') + .withInput({ + root: 'yes', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withPartials({ dude: '{{name}} ({{url}}) {{root}} ' }) .withCompileOptions({ compat: true }) .toCompileTo( 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' ); }); + it('partials can access parents with custom context', function() { - var string = 'Dudes: {{#dudes}}{{> dude "test"}}{{/dudes}}'; - var partial = '{{name}} ({{url}}) {{root}} '; - var hash = { - root: 'yes', - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{#dudes}}{{> dude "test"}}{{/dudes}}') + .withInput({ + root: 'yes', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withPartials({ dude: '{{name}} ({{url}}) {{root}} ' }) .withCompileOptions({ compat: true }) .toCompileTo( 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' ); }); + it('partials can access parents without data', function() { - var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}'; - var partial = '{{name}} ({{url}}) {{root}} '; - var hash = { - root: 'yes', - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}') + .withInput({ + root: 'yes', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withPartials({ dude: '{{name}} ({{url}}) {{root}} ' }) .withRuntimeOptions({ data: false }) .withCompileOptions({ data: false, compat: true }) .toCompileTo( 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' ); }); + it('partials inherit compat', function() { - var string = 'Dudes: {{> dude}}'; - var partial = '{{#dudes}}{{name}} ({{url}}) {{root}} {{/dudes}}'; - var hash = { - root: 'yes', - dudes: [ - { name: 'Yehuda', url: 'http://yehuda' }, - { name: 'Alan', url: 'http://alan' } - ] - }; - expectTemplate(string) - .withInput(hash) - .withPartials({ dude: partial }) + expectTemplate('Dudes: {{> dude}}') + .withInput({ + root: 'yes', + dudes: [ + { name: 'Yehuda', url: 'http://yehuda' }, + { name: 'Alan', url: 'http://alan' } + ] + }) + .withPartials({ + dude: '{{#dudes}}{{name}} ({{url}}) {{root}} {{/dudes}}' + }) .withCompileOptions({ compat: true }) .toCompileTo( 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ' diff --git a/spec/regressions.js b/spec/regressions.js index 6da9672d1..86d4634d2 100644 --- a/spec/regressions.js +++ b/spec/regressions.js @@ -1,21 +1,19 @@ describe('Regressions', function() { it('GH-94: Cannot read property of undefined', function() { - var data = { - books: [ - { - title: 'The origin of species', - author: { - name: 'Charles Darwin' + expectTemplate('{{#books}}{{title}}{{author.name}}{{/books}}') + .withInput({ + books: [ + { + title: 'The origin of species', + author: { + name: 'Charles Darwin' + } + }, + { + title: 'Lazarillo de Tormes' } - }, - { - title: 'Lazarillo de Tormes' - } - ] - }; - var string = '{{#books}}{{title}}{{author.name}}{{/books}}'; - expectTemplate(string) - .withInput(data) + ] + }) .withMessage('Renders without an undefined property error') .toCompileTo('The origin of speciesCharles DarwinLazarillo de Tormes'); }); @@ -28,14 +26,17 @@ describe('Regressions', function() { "inverted sections run when property isn't present in context" ) .toCompileTo('not set :: '); + expectTemplate(string) .withInput({ set: undefined }) .withMessage('inverted sections run when property is undefined') .toCompileTo('not set :: '); + expectTemplate(string) .withInput({ set: false }) .withMessage('inverted sections run when property is false') .toCompileTo('not set :: '); + expectTemplate(string) .withInput({ set: true }) .withMessage("inverted sections don't run when property is true") @@ -43,11 +44,8 @@ describe('Regressions', function() { }); it('GH-158: Using array index twice, breaks the template', function() { - var string = '{{arr.[0]}}, {{arr.[1]}}'; - var data = { arr: [1, 2] }; - - expectTemplate(string) - .withInput(data) + expectTemplate('{{arr.[0]}}, {{arr.[1]}}') + .withInput({ arr: [1, 2] }) .withMessage('it works as expected') .toCompileTo('1, 2'); }); @@ -67,6 +65,7 @@ describe('Regressions', function() { '\n' + 'Nothing to check out...\n' + '{{/hasThings}}'; + var data = { thing: function() { return 'blah'; @@ -89,21 +88,20 @@ describe('Regressions', function() { '
  • @dhg
  • \n' + '
  • @sayrer
  • \n' + '.\n'; + expectTemplate(string) .withInput(data) .toCompileTo(output); }); it('GH-408: Multiple loops fail', function() { - var context = [ - { name: 'John Doe', location: { city: 'Chicago' } }, - { name: 'Jane Doe', location: { city: 'New York' } } - ]; - - var string = '{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}'; - - expectTemplate(string) - .withInput(context) + expectTemplate( + '{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}' + ) + .withInput([ + { name: 'John Doe', location: { city: 'Chicago' } }, + { name: 'Jane Doe', location: { city: 'New York' } } + ]) .withMessage('It should output multiple times') .toCompileTo('John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe'); }); @@ -126,6 +124,7 @@ describe('Regressions', function() { expectTemplate(succeedingTemplate) .withHelpers(helpers) .toCompileTo(' Expected '); + expectTemplate(failingTemplate) .withHelpers(helpers) .toCompileTo(' Expected '); @@ -159,27 +158,24 @@ describe('Regressions', function() { }); it('GH-676: Using array in escaping mustache fails', function() { - var string = '{{arr}}'; var data = { arr: [1, 2] }; - expectTemplate(string) + expectTemplate('{{arr}}') .withInput(data) .withMessage('it works as expected') .toCompileTo(data.arr.toString()); }); it('Mustache man page', function() { - var string = - 'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}'; - var data = { - name: 'Chris', - value: 10000, - taxed_value: 10000 - 10000 * 0.4, - in_ca: true - }; - - expectTemplate(string) - .withInput(data) + expectTemplate( + 'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}' + ) + .withInput({ + name: 'Chris', + value: 10000, + taxed_value: 10000 - 10000 * 0.4, + in_ca: true + }) .withMessage('the hello world mustache example works') .toCompileTo( 'Hello Chris. You have just won $10000! Well, $6000, after taxes.' @@ -202,58 +198,49 @@ describe('Regressions', function() { }); it('GH-837: undefined values for helpers', function() { - var helpers = { - str: function(value) { - return value + ''; - } - }; - expectTemplate('{{str bar.baz}}') - .withHelpers(helpers) + .withHelpers({ + str: function(value) { + return value + ''; + } + }) .toCompileTo('undefined'); }); it('GH-926: Depths and de-dupe', function() { - var context = { - name: 'foo', - data: [1], - notData: [1] - }; - - var string = - '{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}'; - - expectTemplate(string) - .withInput(context) + expectTemplate( + '{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}' + ) + .withInput({ + name: 'foo', + data: [1], + notData: [1] + }) .toCompileTo('foo'); }); it('GH-1021: Each empty string key', function() { - var data = { - '': 'foo', - name: 'Chris', - value: 10000 - }; - expectTemplate('{{#each data}}Key: {{@key}}\n{{/each}}') - .withInput({ data: data }) + .withInput({ + data: { + '': 'foo', + name: 'Chris', + value: 10000 + } + }) .toCompileTo('Key: \nKey: name\nKey: value\n'); }); it('GH-1054: Should handle simple safe string responses', function() { - var root = '{{#wrap}}{{>partial}}{{/wrap}}'; - var partials = { - partial: '{{#wrap}}{{/wrap}}' - }; - var helpers = { - wrap: function(options) { - return new Handlebars.SafeString(options.fn()); - } - }; - - expectTemplate(root) - .withHelpers(helpers) - .withPartials(partials) + expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}') + .withHelpers({ + wrap: function(options) { + return new Handlebars.SafeString(options.fn()); + } + }) + .withPartials({ + partial: '{{#wrap}}{{/wrap}}' + }) .toCompileTo(''); }); @@ -267,91 +254,81 @@ describe('Regressions', function() { }); it('GH-1093: Undefined helper context', function() { - var obj = { foo: undefined, bar: 'bat' }; - var helpers = { - helper: function() { - // It's valid to execute a block against an undefined context, but - // helpers can not do so, so we expect to have an empty object here; - for (var name in this) { - if (Object.prototype.hasOwnProperty.call(this, name)) { - return 'found'; + expectTemplate('{{#each obj}}{{{helper}}}{{.}}{{/each}}') + .withInput({ obj: { foo: undefined, bar: 'bat' } }) + .withHelpers({ + helper: function() { + // It's valid to execute a block against an undefined context, but + // helpers can not do so, so we expect to have an empty object here; + for (var name in this) { + if (Object.prototype.hasOwnProperty.call(this, name)) { + return 'found'; + } } + // And to make IE happy, check for the known string as length is not enumerated. + return this === 'bat' ? 'found' : 'not'; } - // And to make IE happy, check for the known string as length is not enumerated. - return this === 'bat' ? 'found' : 'not'; - } - }; - - expectTemplate('{{#each obj}}{{{helper}}}{{.}}{{/each}}') - .withInput({ obj: obj }) - .withHelpers(helpers) + }) .toCompileTo('notfoundbat'); }); it('should support multiple levels of inline partials', function() { - var string = - '{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}'; - var partials = { - doctype: 'doctype{{> content}}', - layout: - '{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}' - }; - expectTemplate(string) - .withPartials(partials) + expectTemplate( + '{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}' + ) + .withPartials({ + doctype: 'doctype{{> content}}', + layout: + '{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}' + }) .toCompileTo('doctypelayoutsubcontent'); }); + it('GH-1089: should support failover content in multiple levels of inline partials', function() { - var string = '{{#> layout}}{{/layout}}'; - var partials = { - doctype: 'doctype{{> content}}', - layout: - '{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}' - }; - expectTemplate(string) - .withPartials(partials) + expectTemplate('{{#> layout}}{{/layout}}') + .withPartials({ + doctype: 'doctype{{> content}}', + layout: + '{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}' + }) .toCompileTo('doctypelayoutsubcontent'); }); + it('GH-1099: should support greater than 3 nested levels of inline partials', function() { - var string = '{{#> layout}}Outer{{/layout}}'; - var partials = { - layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}', - inner: '' - }; - expectTemplate(string) - .withPartials(partials) + expectTemplate('{{#> layout}}Outer{{/layout}}') + .withPartials({ + layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}', + inner: '' + }) .toCompileTo('Outer'); }); it('GH-1135 : Context handling within each iteration', function() { - var obj = { array: [1], name: 'John' }; - var helpers = { - myif: function(conditional, options) { - if (conditional) { - return options.fn(this); - } else { - return options.inverse(this); - } - } - }; - expectTemplate( '{{#each array}}\n' + ' 1. IF: {{#if true}}{{../name}}-{{../../name}}-{{../../../name}}{{/if}}\n' + ' 2. MYIF: {{#myif true}}{{../name}}={{../../name}}={{../../../name}}{{/myif}}\n' + '{{/each}}' ) - .withInput(obj) - .withHelpers(helpers) + .withInput({ array: [1], name: 'John' }) + .withHelpers({ + myif: function(conditional, options) { + if (conditional) { + return options.fn(this); + } else { + return options.inverse(this); + } + } + }) .toCompileTo(' 1. IF: John--\n' + ' 2. MYIF: John==\n'); }); it('GH-1186: Support block params for existing programs', function() { - var string = + expectTemplate( '{{#*inline "test"}}{{> @partial-block }}{{/inline}}' + - '{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' + - '{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}'; - - expectTemplate(string) + '{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' + + '{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}' + ) .withInput({ listOne: ['a'], listTwo: ['b'] @@ -361,9 +338,9 @@ describe('Regressions', function() { }); it('GH-1319: "unless" breaks when "each" value equals "null"', function() { - var string = - '{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}'; - expectTemplate(string) + expectTemplate( + '{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}' + ) .withInput({ value: 'parent', list: [null, 'a'] @@ -373,14 +350,12 @@ describe('Regressions', function() { }); it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', function() { - var string = 'template {{>partial}} template'; - var partials = { - partialWithBlock: - '{{#if @partial-block}} block {{> @partial-block}} block {{/if}}', - partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}' - }; - expectTemplate(string) - .withPartials(partials) + expectTemplate('template {{>partial}} template') + .withPartials({ + partialWithBlock: + '{{#if @partial-block}} block {{> @partial-block}} block {{/if}}', + partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}' + }) .toCompileTo('template block partial block template'); }); @@ -480,16 +455,13 @@ describe('Regressions', function() { }); it('should allow hash with protected array names', function() { - var obj = { array: [1], name: 'John' }; - var helpers = { - helpa: function(options) { - return options.hash.length; - } - }; - expectTemplate('{{helpa length="foo"}}') - .withInput(obj) - .withHelpers(helpers) + .withInput({ array: [1], name: 'John' }) + .withHelpers({ + helpa: function(options) { + return options.hash.length; + } + }) .toCompileTo('foo'); }); diff --git a/spec/security.js b/spec/security.js index e01dc3db7..25aabeb7b 100644 --- a/spec/security.js +++ b/spec/security.js @@ -23,6 +23,7 @@ describe('security issues', function() { expectTemplate('{{constructor.name}}') .withInput({ constructor: { name: 'here we go' } }) .toCompileTo('here we go'); + expectTemplate('{{lookup (lookup this "constructor") "name"}}') .withInput({ constructor: { name: 'here we go' } }) .toCompileTo('here we go'); @@ -44,9 +45,11 @@ describe('security issues', function() { it('should throw an exception when calling "{{helperMissing}}" ', function() { expectTemplate('{{helperMissing}}').toThrow(Error); }); + it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { expectTemplate('{{#helperMissing}}{{/helperMissing}}').toThrow(Error); }); + it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { var functionCalls = []; expect(function() { @@ -59,14 +62,14 @@ describe('security issues', function() { }).to.throw(Error); expect(functionCalls.length).to.equal(0); }); + it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { - var input = { - fn: function() { - return 'functionInData'; - } - }; expectTemplate('{{#blockHelperMissing .}}{{/blockHelperMissing}}') - .withInput(input) + .withInput({ + fn: function() { + return 'functionInData'; + } + }) .toThrow(Error); }); }); @@ -76,12 +79,14 @@ describe('security issues', function() { var template = Handlebars.compile('{{helperMissing}}'); template({}, { allowCallsToHelperMissing: true }); }); + it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { var template = Handlebars.compile( '{{#helperMissing}}{{/helperMissing}}' ); template({}, { allowCallsToHelperMissing: true }); }); + it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { var functionCalls = []; var template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); @@ -95,6 +100,7 @@ describe('security issues', function() { ); equals(functionCalls.length, 1); }); + it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { var template = Handlebars.compile( '{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}' diff --git a/spec/strict.js b/spec/strict.js index 0e02afe2e..8e48fab5e 100644 --- a/spec/strict.js +++ b/spec/strict.js @@ -7,6 +7,7 @@ describe('strict', function() { .withCompileOptions({ strict: true }) .toThrow(Exception, /"hello" not defined in/); }); + it('should error on missing child', function() { expectTemplate('{{hello.bar}}') .withCompileOptions({ strict: true }) @@ -18,12 +19,14 @@ describe('strict', function() { .withInput({ hello: {} }) .toThrow(Exception, /"bar" not defined in/); }); + it('should handle explicit undefined', function() { expectTemplate('{{hello.bar}}') .withCompileOptions({ strict: true }) .withInput({ hello: { bar: undefined } }) .toCompileTo(''); }); + it('should error on missing property lookup in known helpers mode', function() { expectTemplate('{{hello}}') .withCompileOptions({ @@ -32,6 +35,7 @@ describe('strict', function() { }) .toThrow(Exception, /"hello" not defined in/); }); + it('should error on missing context', function() { expectTemplate('{{hello}}') .withCompileOptions({ strict: true }) @@ -59,6 +63,7 @@ describe('strict', function() { .withInput({ foo: true }) .toThrow(Exception, /"hello" not defined in/); }); + it('should throw on ambiguous blocks', function() { expectTemplate('{{#hello}}{{/hello}}') .withCompileOptions({ strict: true }) @@ -81,20 +86,17 @@ describe('strict', function() { }); it('should allow undefined hash when passed to helpers', function() { - var string = '{{helper value=@foo}}'; - var compileOptions = { - strict: true - }; - var helpers = { - helper: function(options) { - equals('value' in options.hash, true); - equals(options.hash.value, undefined); - return 'success'; - } - }; - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate('{{helper value=@foo}}') + .withCompileOptions({ + strict: true + }) + .withHelpers({ + helper: function(options) { + equals('value' in options.hash, true); + equals(options.hash.value, undefined); + return 'success'; + } + }) .toCompileTo('success'); }); @@ -125,17 +127,20 @@ describe('strict', function() { .withCompileOptions({ assumeObjects: true }) .toCompileTo(''); }); + it('should ignore missing child', function() { expectTemplate('{{hello.bar}}') .withCompileOptions({ assumeObjects: true }) .withInput({ hello: {} }) .toCompileTo(''); }); + it('should error on missing object', function() { expectTemplate('{{hello.bar}}') .withCompileOptions({ assumeObjects: true }) .toThrow(Error); }); + it('should error on missing context', function() { expectTemplate('{{hello}}') .withCompileOptions({ assumeObjects: true }) diff --git a/spec/string-params.js b/spec/string-params.js index 0ce211ffc..c4b9a27b7 100644 --- a/spec/string-params.js +++ b/spec/string-params.js @@ -1,141 +1,117 @@ describe('string params mode', function() { it('arguments to helpers can be retrieved from options hash in string form', function() { - var string = '{{wycats is.a slave.driver}}'; - var compileOptions = { - stringParams: true - }; - - var helpers = { - wycats: function(passiveVoice, noun) { - return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun; - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate('{{wycats is.a slave.driver}}') + .withCompileOptions({ + stringParams: true + }) + .withHelpers({ + wycats: function(passiveVoice, noun) { + return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun; + } + }) .withMessage('String parameters output') .toCompileTo('HELP ME MY BOSS is.a slave.driver'); }); it('when using block form, arguments to helpers can be retrieved from options hash in string form', function() { - var string = '{{#wycats is.a slave.driver}}help :({{/wycats}}'; - var compileOptions = { stringParams: true }; - - var helpers = { - wycats: function(passiveVoice, noun, options) { - return ( - 'HELP ME MY BOSS ' + - passiveVoice + - ' ' + - noun + - ': ' + - options.fn(this) - ); - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate('{{#wycats is.a slave.driver}}help :({{/wycats}}') + .withCompileOptions({ + stringParams: true + }) + .withHelpers({ + wycats: function(passiveVoice, noun, options) { + return ( + 'HELP ME MY BOSS ' + + passiveVoice + + ' ' + + noun + + ': ' + + options.fn(this) + ); + } + }) .withMessage('String parameters output') .toCompileTo('HELP ME MY BOSS is.a slave.driver: help :('); }); it('when inside a block in String mode, .. passes the appropriate context in the options hash', function() { - var string = '{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}'; - var compileOptions = { stringParams: true }; - - var helpers = { - tomdale: function(desire, noun, options) { - return ( - 'STOP ME FROM READING HACKER NEWS I ' + - options.contexts[0][desire] + - ' ' + - noun - ); - }, - - with: function(context, options) { - return options.fn(options.contexts[0][context]); - } - }; - - var input = { - dale: {}, - - need: 'need-a' - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) - .withInput(input) + expectTemplate('{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}') + .withCompileOptions({ + stringParams: true + }) + .withHelpers({ + tomdale: function(desire, noun, options) { + return ( + 'STOP ME FROM READING HACKER NEWS I ' + + options.contexts[0][desire] + + ' ' + + noun + ); + }, + with: function(context, options) { + return options.fn(options.contexts[0][context]); + } + }) + .withInput({ + dale: {}, + + need: 'need-a' + }) .withMessage('Proper context variable output') .toCompileTo('STOP ME FROM READING HACKER NEWS I need-a dad.joke'); }); it('information about the types is passed along', function() { - var string = "{{tomdale 'need' dad.joke true false}}"; - var compileOptions = { stringParams: true }; - - var helpers = { - tomdale: function(desire, noun, trueBool, falseBool, options) { - equal(options.types[0], 'StringLiteral', 'the string type is passed'); - equal( - options.types[1], - 'PathExpression', - 'the expression type is passed' - ); - equal( - options.types[2], - 'BooleanLiteral', - 'the expression type is passed' - ); - equal(desire, 'need', 'the string form is passed for strings'); - equal(noun, 'dad.joke', 'the string form is passed for expressions'); - equal(trueBool, true, 'raw booleans are passed through'); - equal(falseBool, false, 'raw booleans are passed through'); - return 'Helper called'; - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate("{{tomdale 'need' dad.joke true false}}") + .withCompileOptions({ + stringParams: true + }) + .withHelpers({ + tomdale: function(desire, noun, trueBool, falseBool, options) { + equal(options.types[0], 'StringLiteral', 'the string type is passed'); + equal( + options.types[1], + 'PathExpression', + 'the expression type is passed' + ); + equal( + options.types[2], + 'BooleanLiteral', + 'the expression type is passed' + ); + equal(desire, 'need', 'the string form is passed for strings'); + equal(noun, 'dad.joke', 'the string form is passed for expressions'); + equal(trueBool, true, 'raw booleans are passed through'); + equal(falseBool, false, 'raw booleans are passed through'); + return 'Helper called'; + } + }) .toCompileTo('Helper called'); }); it('hash parameters get type information', function() { - var string = "{{tomdale he.says desire='need' noun=dad.joke bool=true}}"; - var compileOptions = { stringParams: true }; - - var helpers = { - tomdale: function(exclamation, options) { - equal(exclamation, 'he.says'); - equal(options.types[0], 'PathExpression'); - - equal(options.hashTypes.desire, 'StringLiteral'); - equal(options.hashTypes.noun, 'PathExpression'); - equal(options.hashTypes.bool, 'BooleanLiteral'); - equal(options.hash.desire, 'need'); - equal(options.hash.noun, 'dad.joke'); - equal(options.hash.bool, true); - return 'Helper called'; - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate("{{tomdale he.says desire='need' noun=dad.joke bool=true}}") + .withCompileOptions({ + stringParams: true + }) + .withHelpers({ + tomdale: function(exclamation, options) { + equal(exclamation, 'he.says'); + equal(options.types[0], 'PathExpression'); + + equal(options.hashTypes.desire, 'StringLiteral'); + equal(options.hashTypes.noun, 'PathExpression'); + equal(options.hashTypes.bool, 'BooleanLiteral'); + equal(options.hash.desire, 'need'); + equal(options.hash.noun, 'dad.joke'); + equal(options.hash.bool, true); + return 'Helper called'; + } + }) .toCompileTo('Helper called'); }); it('hash parameters get context information', function() { - var string = - "{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}"; - var compileOptions = { stringParams: true }; - var context = { dale: {} }; var helpers = { @@ -155,84 +131,77 @@ describe('string params mode', function() { } }; - expectTemplate(string) - .withCompileOptions(compileOptions) + expectTemplate( + "{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}" + ) + .withCompileOptions({ stringParams: true }) .withHelpers(helpers) .withInput(context) .toCompileTo('Helper called'); }); it('when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper', function() { - var string = - '{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}'; - var compileOptions = { stringParams: true }; - - var helpers = { - tomdale: function(desire, noun, options) { - return ( - 'STOP ME FROM READING HACKER NEWS I ' + - options.contexts[0][desire] + - ' ' + - noun + - ' ' + - options.fn(this) - ); - }, - - with: function(context, options) { - return options.fn(options.contexts[0][context]); - } - }; - - var input = { - dale: {}, - - need: 'need-a' - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) - .withInput(input) + expectTemplate( + '{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}' + ) + .withCompileOptions({ + stringParams: true + }) + .withHelpers({ + tomdale: function(desire, noun, options) { + return ( + 'STOP ME FROM READING HACKER NEWS I ' + + options.contexts[0][desire] + + ' ' + + noun + + ' ' + + options.fn(this) + ); + }, + + with: function(context, options) { + return options.fn(options.contexts[0][context]); + } + }) + .withInput({ + dale: {}, + + need: 'need-a' + }) .withMessage('Proper context variable output') .toCompileTo('STOP ME FROM READING HACKER NEWS I need-a dad.joke wot'); }); it('with nested block ambiguous', function() { - var string = - '{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}'; - var compileOptions = { stringParams: true }; - - var helpers = { - with: function() { - return 'WITH'; - }, - view: function() { - return 'VIEW'; - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate( + '{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}' + ) + .withCompileOptions({ + stringParams: true + }) + .withHelpers({ + with: function() { + return 'WITH'; + }, + view: function() { + return 'VIEW'; + } + }) .toCompileTo('WITH'); }); it('should handle DATA', function() { - var string = '{{foo @bar}}'; - var compileOptions = { stringParams: true }; - - var helpers = { - foo: function(bar, options) { - equal(bar, '@bar'); - equal(options.types[0], 'PathExpression'); - return 'Foo!'; - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate('{{foo @bar}}') + .withCompileOptions({ + stringParams: true + }) + .withHelpers({ + foo: function(bar, options) { + equal(bar, '@bar'); + equal(options.types[0], 'PathExpression'); + return 'Foo!'; + } + }) .toCompileTo('Foo!'); }); }); diff --git a/spec/subexpressions.js b/spec/subexpressions.js index 8f041816a..21b93aab9 100644 --- a/spec/subexpressions.js +++ b/spec/subexpressions.js @@ -1,72 +1,56 @@ describe('subexpressions', function() { it('arg-less helper', function() { - var string = '{{foo (bar)}}!'; - var context = {}; - var helpers = { - foo: function(val) { - return val + val; - }, - bar: function() { - return 'LOL'; - } - }; - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + expectTemplate('{{foo (bar)}}!') + .withHelpers({ + foo: function(val) { + return val + val; + }, + bar: function() { + return 'LOL'; + } + }) .toCompileTo('LOLLOL!'); }); it('helper w args', function() { - var string = '{{blog (equal a b)}}'; - - var context = { bar: 'LOL' }; - var helpers = { - blog: function(val) { - return 'val is ' + val; - }, - equal: function(x, y) { - return x === y; - } - }; - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + expectTemplate('{{blog (equal a b)}}') + .withInput({ bar: 'LOL' }) + .withHelpers({ + blog: function(val) { + return 'val is ' + val; + }, + equal: function(x, y) { + return x === y; + } + }) .toCompileTo('val is true'); }); it('mixed paths and helpers', function() { - var string = '{{blog baz.bat (equal a b) baz.bar}}'; - - var context = { bar: 'LOL', baz: { bat: 'foo!', bar: 'bar!' } }; - var helpers = { - blog: function(val, that, theOther) { - return 'val is ' + val + ', ' + that + ' and ' + theOther; - }, - equal: function(x, y) { - return x === y; - } - }; - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + expectTemplate('{{blog baz.bat (equal a b) baz.bar}}') + .withInput({ bar: 'LOL', baz: { bat: 'foo!', bar: 'bar!' } }) + .withHelpers({ + blog: function(val, that, theOther) { + return 'val is ' + val + ', ' + that + ' and ' + theOther; + }, + equal: function(x, y) { + return x === y; + } + }) .toCompileTo('val is foo!, true and bar!'); }); it('supports much nesting', function() { - var string = '{{blog (equal (equal true true) true)}}'; - - var context = { bar: 'LOL' }; - var helpers = { - blog: function(val) { - return 'val is ' + val; - }, - equal: function(x, y) { - return x === y; - } - }; - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + expectTemplate('{{blog (equal (equal true true) true)}}') + .withInput({ bar: 'LOL' }) + .withHelpers({ + blog: function(val) { + return 'val is ' + val; + }, + equal: function(x, y) { + return x === y; + } + }) .toCompileTo('val is true'); }); @@ -85,18 +69,22 @@ describe('subexpressions', function() { .withInput(context) .withHelpers(helpers) .toCompileTo('abc-ab'); + expectTemplate('{{dash d (concat a b)}}') .withInput(context) .withHelpers(helpers) .toCompileTo('d-ab'); + expectTemplate('{{dash c.c (concat a b)}}') .withInput(context) .withHelpers(helpers) .toCompileTo('c-ab'); + expectTemplate('{{dash (concat a b) c.c}}') .withInput(context) .withHelpers(helpers) .toCompileTo('ab-c'); + expectTemplate('{{dash (concat a e.e) c.c}}') .withInput(context) .withHelpers(helpers) @@ -104,8 +92,6 @@ describe('subexpressions', function() { }); it('provides each nested helper invocation its own options hash', function() { - var string = '{{equal (equal true true) true}}'; - var lastOptions = null; var helpers = { equal: function(x, y, options) { @@ -116,208 +102,177 @@ describe('subexpressions', function() { return x === y; } }; - expectTemplate(string) + expectTemplate('{{equal (equal true true) true}}') .withHelpers(helpers) .toCompileTo('true'); }); it('with hashes', function() { - var string = "{{blog (equal (equal true true) true fun='yes')}}"; - - var context = { bar: 'LOL' }; - var helpers = { - blog: function(val) { - return 'val is ' + val; - }, - equal: function(x, y) { - return x === y; - } - }; - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + expectTemplate("{{blog (equal (equal true true) true fun='yes')}}") + .withInput({ bar: 'LOL' }) + .withHelpers({ + blog: function(val) { + return 'val is ' + val; + }, + equal: function(x, y) { + return x === y; + } + }) .toCompileTo('val is true'); }); it('as hashes', function() { - var string = "{{blog fun=(equal (blog fun=1) 'val is 1')}}"; - - var helpers = { - blog: function(options) { - return 'val is ' + options.hash.fun; - }, - equal: function(x, y) { - return x === y; - } - }; - expectTemplate(string) - .withHelpers(helpers) + expectTemplate("{{blog fun=(equal (blog fun=1) 'val is 1')}}") + .withHelpers({ + blog: function(options) { + return 'val is ' + options.hash.fun; + }, + equal: function(x, y) { + return x === y; + } + }) .toCompileTo('val is true'); }); it('multiple subexpressions in a hash', function() { - var string = - '{{input aria-label=(t "Name") placeholder=(t "Example User")}}'; - - var helpers = { - input: function(options) { - var hash = options.hash; - var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); - var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); - return new Handlebars.SafeString( - '' - ); - }, - t: function(defaultString) { - return new Handlebars.SafeString(defaultString); - } - }; - expectTemplate(string) - .withHelpers(helpers) + expectTemplate( + '{{input aria-label=(t "Name") placeholder=(t "Example User")}}' + ) + .withHelpers({ + input: function(options) { + var hash = options.hash; + var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); + var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); + return new Handlebars.SafeString( + '' + ); + }, + t: function(defaultString) { + return new Handlebars.SafeString(defaultString); + } + }) .toCompileTo(''); }); it('multiple subexpressions in a hash with context', function() { - var string = - '{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}'; - - var context = { - item: { - field: 'Name', - placeholder: 'Example User' - } - }; - - var helpers = { - input: function(options) { - var hash = options.hash; - var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); - var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); - return new Handlebars.SafeString( - '' - ); - }, - t: function(defaultString) { - return new Handlebars.SafeString(defaultString); - } - }; - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + expectTemplate( + '{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}' + ) + .withInput({ + item: { + field: 'Name', + placeholder: 'Example User' + } + }) + .withHelpers({ + input: function(options) { + var hash = options.hash; + var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); + var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); + return new Handlebars.SafeString( + '' + ); + }, + t: function(defaultString) { + return new Handlebars.SafeString(defaultString); + } + }) .toCompileTo(''); }); it('in string params mode,', function() { - var string = '{{snog (blorg foo x=y) yeah a=b}}'; - var compileOptions = { stringParams: true }; - - var helpers = { - snog: function(a, b, options) { - equals(a, 'foo'); - equals( - options.types.length, - 2, - 'string params for outer helper processed correctly' - ); - equals( - options.types[0], - 'SubExpression', - 'string params for outer helper processed correctly' - ); - equals( - options.types[1], - 'PathExpression', - 'string params for outer helper processed correctly' - ); - return a + b; - }, + expectTemplate('{{snog (blorg foo x=y) yeah a=b}}') + .withCompileOptions({ stringParams: true }) + .withHelpers({ + snog: function(a, b, options) { + equals(a, 'foo'); + equals( + options.types.length, + 2, + 'string params for outer helper processed correctly' + ); + equals( + options.types[0], + 'SubExpression', + 'string params for outer helper processed correctly' + ); + equals( + options.types[1], + 'PathExpression', + 'string params for outer helper processed correctly' + ); + return a + b; + }, - blorg: function(a, options) { - equals( - options.types.length, - 1, - 'string params for inner helper processed correctly' - ); - equals( - options.types[0], - 'PathExpression', - 'string params for inner helper processed correctly' - ); - return a; - } - }; - - var input = { - foo: {}, - yeah: {} - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) - .withInput(input) + blorg: function(a, options) { + equals( + options.types.length, + 1, + 'string params for inner helper processed correctly' + ); + equals( + options.types[0], + 'PathExpression', + 'string params for inner helper processed correctly' + ); + return a; + } + }) + .withInput({ + foo: {}, + yeah: {} + }) .toCompileTo('fooyeah'); }); it('as hashes in string params mode', function() { - var string = '{{blog fun=(bork)}}'; - var compileOptions = { stringParams: true }; - - var helpers = { - blog: function(options) { - equals(options.hashTypes.fun, 'SubExpression'); - return 'val is ' + options.hash.fun; - }, - bork: function() { - return 'BORK'; - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate('{{blog fun=(bork)}}') + .withCompileOptions({ stringParams: true }) + .withHelpers({ + blog: function(options) { + equals(options.hashTypes.fun, 'SubExpression'); + return 'val is ' + options.hash.fun; + }, + bork: function() { + return 'BORK'; + } + }) .toCompileTo('val is BORK'); }); it('subexpression functions on the context', function() { - var string = '{{foo (bar)}}!'; - var context = { - bar: function() { - return 'LOL'; - } - }; - var helpers = { - foo: function(val) { - return val + val; - } - }; - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + expectTemplate('{{foo (bar)}}!') + .withInput({ + bar: function() { + return 'LOL'; + } + }) + .withHelpers({ + foo: function(val) { + return val + val; + } + }) .toCompileTo('LOLLOL!'); }); it("subexpressions can't just be property lookups", function() { - var string = '{{foo (bar)}}!'; - var context = { - bar: 'LOL' - }; - var helpers = { - foo: function(val) { - return val + val; - } - }; - expectTemplate(string) - .withInput(context) - .withHelpers(helpers) + expectTemplate('{{foo (bar)}}!') + .withInput({ + bar: 'LOL' + }) + .withHelpers({ + foo: function(val) { + return val + val; + } + }) .toThrow(); }); }); diff --git a/spec/track-ids.js b/spec/track-ids.js index 72713d43b..033e7b176 100644 --- a/spec/track-ids.js +++ b/spec/track-ids.js @@ -5,216 +5,182 @@ describe('track ids', function() { }); it('should not include anything without the flag', function() { - var string = '{{wycats is.a slave.driver}}'; - - var helpers = { - wycats: function(passiveVoice, noun, options) { - equal(options.ids, undefined); - equal(options.hashIds, undefined); - - return 'success'; - } - }; - - expectTemplate(string) - .withHelpers(helpers) + expectTemplate('{{wycats is.a slave.driver}}') + .withHelpers({ + wycats: function(passiveVoice, noun, options) { + equal(options.ids, undefined); + equal(options.hashIds, undefined); + + return 'success'; + } + }) .toCompileTo('success'); }); - it('should include argument ids', function() { - var string = '{{wycats is.a slave.driver}}'; - var compileOptions = { trackIds: true }; - - var helpers = { - wycats: function(passiveVoice, noun, options) { - equal(options.ids[0], 'is.a'); - equal(options.ids[1], 'slave.driver'); - - return ( - 'HELP ME MY BOSS ' + - options.ids[0] + - ':' + - passiveVoice + - ' ' + - options.ids[1] + - ':' + - noun - ); - } - }; - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + it('should include argument ids', function() { + expectTemplate('{{wycats is.a slave.driver}}') + .withCompileOptions({ trackIds: true }) + .withHelpers({ + wycats: function(passiveVoice, noun, options) { + equal(options.ids[0], 'is.a'); + equal(options.ids[1], 'slave.driver'); + + return ( + 'HELP ME MY BOSS ' + + options.ids[0] + + ':' + + passiveVoice + + ' ' + + options.ids[1] + + ':' + + noun + ); + } + }) .withInput(context) .toCompileTo('HELP ME MY BOSS is.a:foo slave.driver:bar'); }); - it('should include hash ids', function() { - var string = '{{wycats bat=is.a baz=slave.driver}}'; - var compileOptions = { trackIds: true }; - var helpers = { - wycats: function(options) { - equal(options.hashIds.bat, 'is.a'); - equal(options.hashIds.baz, 'slave.driver'); - - return ( - 'HELP ME MY BOSS ' + - options.hashIds.bat + - ':' + - options.hash.bat + - ' ' + - options.hashIds.baz + - ':' + - options.hash.baz - ); - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + it('should include hash ids', function() { + expectTemplate('{{wycats bat=is.a baz=slave.driver}}') + .withCompileOptions({ trackIds: true }) + .withHelpers({ + wycats: function(options) { + equal(options.hashIds.bat, 'is.a'); + equal(options.hashIds.baz, 'slave.driver'); + + return ( + 'HELP ME MY BOSS ' + + options.hashIds.bat + + ':' + + options.hash.bat + + ' ' + + options.hashIds.baz + + ':' + + options.hash.baz + ); + } + }) .withInput(context) .toCompileTo('HELP ME MY BOSS is.a:foo slave.driver:bar'); }); - it('should note ../ and ./ references', function() { - var string = '{{wycats ./is.a ../slave.driver this.is.a this}}'; - var compileOptions = { trackIds: true }; - - var helpers = { - wycats: function(passiveVoice, noun, thiz, thiz2, options) { - equal(options.ids[0], 'is.a'); - equal(options.ids[1], '../slave.driver'); - equal(options.ids[2], 'is.a'); - equal(options.ids[3], ''); - - return ( - 'HELP ME MY BOSS ' + - options.ids[0] + - ':' + - passiveVoice + - ' ' + - options.ids[1] + - ':' + - noun - ); - } - }; - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + it('should note ../ and ./ references', function() { + expectTemplate('{{wycats ./is.a ../slave.driver this.is.a this}}') + .withCompileOptions({ trackIds: true }) + .withHelpers({ + wycats: function(passiveVoice, noun, thiz, thiz2, options) { + equal(options.ids[0], 'is.a'); + equal(options.ids[1], '../slave.driver'); + equal(options.ids[2], 'is.a'); + equal(options.ids[3], ''); + + return ( + 'HELP ME MY BOSS ' + + options.ids[0] + + ':' + + passiveVoice + + ' ' + + options.ids[1] + + ':' + + noun + ); + } + }) .withInput(context) .toCompileTo('HELP ME MY BOSS is.a:foo ../slave.driver:undefined'); }); - it('should note @data references', function() { - var string = '{{wycats @is.a @slave.driver}}'; - var compileOptions = { trackIds: true }; - var helpers = { - wycats: function(passiveVoice, noun, options) { - equal(options.ids[0], '@is.a'); - equal(options.ids[1], '@slave.driver'); - - return ( - 'HELP ME MY BOSS ' + - options.ids[0] + - ':' + - passiveVoice + - ' ' + - options.ids[1] + - ':' + - noun - ); - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + it('should note @data references', function() { + expectTemplate('{{wycats @is.a @slave.driver}}') + .withCompileOptions({ trackIds: true }) + .withHelpers({ + wycats: function(passiveVoice, noun, options) { + equal(options.ids[0], '@is.a'); + equal(options.ids[1], '@slave.driver'); + + return ( + 'HELP ME MY BOSS ' + + options.ids[0] + + ':' + + passiveVoice + + ' ' + + options.ids[1] + + ':' + + noun + ); + } + }) .withRuntimeOptions({ data: context }) .toCompileTo('HELP ME MY BOSS @is.a:foo @slave.driver:bar'); }); it('should return null for constants', function() { - var string = '{{wycats 1 "foo" key=false}}'; - var compileOptions = { trackIds: true }; - - var helpers = { - wycats: function(passiveVoice, noun, options) { - equal(options.ids[0], null); - equal(options.ids[1], null); - equal(options.hashIds.key, null); - - return ( - 'HELP ME MY BOSS ' + - passiveVoice + - ' ' + - noun + - ' ' + - options.hash.key - ); - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate('{{wycats 1 "foo" key=false}}') + .withCompileOptions({ trackIds: true }) + .withHelpers({ + wycats: function(passiveVoice, noun, options) { + equal(options.ids[0], null); + equal(options.ids[1], null); + equal(options.hashIds.key, null); + + return ( + 'HELP ME MY BOSS ' + + passiveVoice + + ' ' + + noun + + ' ' + + options.hash.key + ); + } + }) .withInput(context) .toCompileTo('HELP ME MY BOSS 1 foo false'); }); - it('should return true for subexpressions', function() { - var string = '{{wycats (sub)}}'; - var compileOptions = { trackIds: true }; - var helpers = { - sub: function() { - return 1; - }, - wycats: function(passiveVoice, options) { - equal(options.ids[0], true); - - return 'HELP ME MY BOSS ' + passiveVoice; - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + it('should return true for subexpressions', function() { + expectTemplate('{{wycats (sub)}}') + .withCompileOptions({ trackIds: true }) + .withHelpers({ + sub: function() { + return 1; + }, + wycats: function(passiveVoice, options) { + equal(options.ids[0], true); + + return 'HELP ME MY BOSS ' + passiveVoice; + } + }) .withInput(context) .toCompileTo('HELP ME MY BOSS 1'); }); it('should use block param paths', function() { - var string = '{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}'; - var compileOptions = { trackIds: true }; - - var helpers = { - doIt: function(options) { - var blockParams = [this.is]; - blockParams.path = ['zomg']; - return options.fn(this, { blockParams: blockParams }); - }, - wycats: function(passiveVoice, noun, blah, options) { - equal(options.ids[0], 'zomg.a'); - equal(options.ids[1], 'slave.driver'); - equal(options.ids[2], 'zomg'); - - return ( - 'HELP ME MY BOSS ' + - options.ids[0] + - ':' + - passiveVoice + - ' ' + - options.ids[1] + - ':' + - noun - ); - } - }; - - expectTemplate(string) - .withCompileOptions(compileOptions) - .withHelpers(helpers) + expectTemplate('{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}') + .withCompileOptions({ trackIds: true }) + .withHelpers({ + doIt: function(options) { + var blockParams = [this.is]; + blockParams.path = ['zomg']; + return options.fn(this, { blockParams: blockParams }); + }, + wycats: function(passiveVoice, noun, blah, options) { + equal(options.ids[0], 'zomg.a'); + equal(options.ids[1], 'slave.driver'); + equal(options.ids[2], 'zomg'); + + return ( + 'HELP ME MY BOSS ' + + options.ids[0] + + ':' + + passiveVoice + + ' ' + + options.ids[1] + + ':' + + noun + ); + } + }) .withInput(context) .toCompileTo('HELP ME MY BOSS zomg.a:foo slave.driver:bar'); }); @@ -231,108 +197,84 @@ describe('track ids', function() { describe('#each', function() { it('should track contextPath for arrays', function() { - var string = '{{#each array}}{{wycats name}}{{/each}}'; - var compileOptions = { trackIds: true }; - - var input = { array: [{ name: 'foo' }, { name: 'bar' }] }; - expectTemplate(string) - .withCompileOptions(compileOptions) + expectTemplate('{{#each array}}{{wycats name}}{{/each}}') + .withCompileOptions({ trackIds: true }) .withHelpers(helpers) - .withInput(input) + .withInput({ array: [{ name: 'foo' }, { name: 'bar' }] }) .toCompileTo('foo:array.0\nbar:array.1\n'); }); - it('should track contextPath for keys', function() { - var string = '{{#each object}}{{wycats name}}{{/each}}'; - var compileOptions = { trackIds: true }; - var input = { object: { foo: { name: 'foo' }, bar: { name: 'bar' } } }; - expectTemplate(string) - .withCompileOptions(compileOptions) + it('should track contextPath for keys', function() { + expectTemplate('{{#each object}}{{wycats name}}{{/each}}') + .withCompileOptions({ trackIds: true }) .withHelpers(helpers) - .withInput(input) + .withInput({ object: { foo: { name: 'foo' }, bar: { name: 'bar' } } }) .toCompileTo('foo:object.foo\nbar:object.bar\n'); }); - it('should handle nesting', function() { - var string = '{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}'; - var compileOptions = { trackIds: true }; - var input = { array: [{ name: 'foo' }, { name: 'bar' }] }; - expectTemplate(string) - .withCompileOptions(compileOptions) + it('should handle nesting', function() { + expectTemplate( + '{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}' + ) + .withCompileOptions({ trackIds: true }) .withHelpers(helpers) - .withInput(input) + .withInput({ array: [{ name: 'foo' }, { name: 'bar' }] }) .toCompileTo('foo:.array..0\nbar:.array..1\n'); }); - it('should handle block params', function() { - var string = - '{{#each array as |value|}}{{blockParams value.name}}{{/each}}'; - var compileOptions = { trackIds: true }; - var input = { array: [{ name: 'foo' }, { name: 'bar' }] }; - expectTemplate(string) - .withCompileOptions(compileOptions) + it('should handle block params', function() { + expectTemplate( + '{{#each array as |value|}}{{blockParams value.name}}{{/each}}' + ) + .withCompileOptions({ trackIds: true }) .withHelpers(helpers) - .withInput(input) + .withInput({ array: [{ name: 'foo' }, { name: 'bar' }] }) .toCompileTo('foo:array.0.name\nbar:array.1.name\n'); }); }); + describe('#with', function() { it('should track contextPath', function() { - var string = '{{#with field}}{{wycats name}}{{/with}}'; - var compileOptions = { trackIds: true }; - - var input = { field: { name: 'foo' } }; - expectTemplate(string) - .withCompileOptions(compileOptions) + expectTemplate('{{#with field}}{{wycats name}}{{/with}}') + .withCompileOptions({ trackIds: true }) .withHelpers(helpers) - .withInput(input) + .withInput({ field: { name: 'foo' } }) .toCompileTo('foo:field\n'); }); - it('should handle nesting', function() { - var string = - '{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}'; - var compileOptions = { trackIds: true }; - var input = { bat: { field: { name: 'foo' } } }; - expectTemplate(string) - .withCompileOptions(compileOptions) + it('should handle nesting', function() { + expectTemplate( + '{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}' + ) + .withCompileOptions({ trackIds: true }) .withHelpers(helpers) - .withInput(input) + .withInput({ bat: { field: { name: 'foo' } } }) .toCompileTo('foo:bat.field\n'); }); }); + describe('#blockHelperMissing', function() { it('should track contextPath for arrays', function() { - var string = '{{#field}}{{wycats name}}{{/field}}'; - var compileOptions = { trackIds: true }; - - var input = { field: [{ name: 'foo' }] }; - expectTemplate(string) - .withCompileOptions(compileOptions) + expectTemplate('{{#field}}{{wycats name}}{{/field}}') + .withCompileOptions({ trackIds: true }) .withHelpers(helpers) - .withInput(input) + .withInput({ field: [{ name: 'foo' }] }) .toCompileTo('foo:field.0\n'); }); - it('should track contextPath for keys', function() { - var string = '{{#field}}{{wycats name}}{{/field}}'; - var compileOptions = { trackIds: true }; - var input = { field: { name: 'foo' } }; - expectTemplate(string) - .withCompileOptions(compileOptions) + it('should track contextPath for keys', function() { + expectTemplate('{{#field}}{{wycats name}}{{/field}}') + .withCompileOptions({ trackIds: true }) .withHelpers(helpers) - .withInput(input) + .withInput({ field: { name: 'foo' } }) .toCompileTo('foo:field\n'); }); - it('should handle nesting', function() { - var string = '{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}'; - var compileOptions = { trackIds: true }; - var input = { bat: { field: { name: 'foo' } } }; - expectTemplate(string) - .withCompileOptions(compileOptions) + it('should handle nesting', function() { + expectTemplate('{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}') + .withCompileOptions({ trackIds: true }) .withHelpers(helpers) - .withInput(input) + .withInput({ bat: { field: { name: 'foo' } } }) .toCompileTo('foo:bat.field\n'); }); }); diff --git a/spec/whitespace-control.js b/spec/whitespace-control.js index 0e7f74221..f826d95a1 100644 --- a/spec/whitespace-control.js +++ b/spec/whitespace-control.js @@ -5,9 +5,11 @@ describe('whitespace control', function() { expectTemplate(' {{~foo~}} ') .withInput(hash) .toCompileTo('bar<'); + expectTemplate(' {{~foo}} ') .withInput(hash) .toCompileTo('bar< '); + expectTemplate(' {{foo~}} ') .withInput(hash) .toCompileTo(' bar<'); @@ -15,6 +17,7 @@ describe('whitespace control', function() { expectTemplate(' {{~&foo~}} ') .withInput(hash) .toCompileTo('bar<'); + expectTemplate(' {{~{foo}~}} ') .withInput(hash) .toCompileTo('bar<'); @@ -29,12 +32,15 @@ describe('whitespace control', function() { expectTemplate(' {{~#if foo~}} bar {{~/if~}} ') .withInput(hash) .toCompileTo('bar'); + expectTemplate(' {{#if foo~}} bar {{/if~}} ') .withInput(hash) .toCompileTo(' bar '); + expectTemplate(' {{~#if foo}} bar {{~/if}} ') .withInput(hash) .toCompileTo(' bar '); + expectTemplate(' {{#if foo}} bar {{/if}} ') .withInput(hash) .toCompileTo(' bar '); @@ -42,42 +48,41 @@ describe('whitespace control', function() { expectTemplate(' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ') .withInput(hash) .toCompileTo('bar'); + expectTemplate(' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ') .withInput(hash) .toCompileTo(' abara '); }); + it('should strip whitespace around inverse block calls', function() { - var hash = {}; + expectTemplate(' {{~^if foo~}} bar {{~/if~}} ').toCompileTo('bar'); - expectTemplate(' {{~^if foo~}} bar {{~/if~}} ') - .withInput(hash) - .toCompileTo('bar'); - expectTemplate(' {{^if foo~}} bar {{/if~}} ') - .withInput(hash) - .toCompileTo(' bar '); - expectTemplate(' {{~^if foo}} bar {{~/if}} ') - .withInput(hash) - .toCompileTo(' bar '); - expectTemplate(' {{^if foo}} bar {{/if}} ') - .withInput(hash) - .toCompileTo(' bar '); + expectTemplate(' {{^if foo~}} bar {{/if~}} ').toCompileTo(' bar '); - expectTemplate(' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ') - .withInput(hash) - .toCompileTo('bar'); + expectTemplate(' {{~^if foo}} bar {{~/if}} ').toCompileTo(' bar '); + + expectTemplate(' {{^if foo}} bar {{/if}} ').toCompileTo(' bar '); + + expectTemplate( + ' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ' + ).toCompileTo('bar'); }); + it('should strip whitespace around complex block calls', function() { var hash = { foo: 'bar<' }; expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}') .withInput(hash) .toCompileTo('bar'); + expectTemplate('{{#if foo~}} bar {{^~}} baz {{/if}}') .withInput(hash) .toCompileTo('bar '); + expectTemplate('{{#if foo}} bar {{~^~}} baz {{~/if}}') .withInput(hash) .toCompileTo(' bar'); + expectTemplate('{{#if foo}} bar {{^~}} baz {{/if}}') .withInput(hash) .toCompileTo(' bar '); @@ -91,36 +96,34 @@ describe('whitespace control', function() { ) .withInput(hash) .toCompileTo('bar'); + expectTemplate( '\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n' ) .withInput(hash) .toCompileTo('bar<'); - hash = {}; + expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}').toCompileTo( + 'baz' + ); - expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}') - .withInput(hash) - .toCompileTo('baz'); - expectTemplate('{{#if foo}} bar {{~^~}} baz {{/if}}') - .withInput(hash) - .toCompileTo('baz '); - expectTemplate('{{#if foo~}} bar {{~^}} baz {{~/if}}') - .withInput(hash) - .toCompileTo(' baz'); - expectTemplate('{{#if foo~}} bar {{~^}} baz {{/if}}') - .withInput(hash) - .toCompileTo(' baz '); + expectTemplate('{{#if foo}} bar {{~^~}} baz {{/if}}').toCompileTo('baz '); - expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}') - .withInput(hash) - .toCompileTo('baz'); + expectTemplate('{{#if foo~}} bar {{~^}} baz {{~/if}}').toCompileTo( + ' baz' + ); + + expectTemplate('{{#if foo~}} bar {{~^}} baz {{/if}}').toCompileTo( + ' baz ' + ); + + expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}').toCompileTo( + 'baz' + ); expectTemplate( '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n' - ) - .withInput(hash) - .toCompileTo('baz'); + ).toCompileTo('baz'); }); }); @@ -128,9 +131,11 @@ describe('whitespace control', function() { expectTemplate('foo {{~> dude~}} ') .withPartials({ dude: 'bar' }) .toCompileTo('foobar'); + expectTemplate('foo {{> dude~}} ') .withPartials({ dude: 'bar' }) .toCompileTo('foo bar'); + expectTemplate('foo {{> dude}} ') .withPartials({ dude: 'bar' }) .toCompileTo('foo bar '); @@ -138,16 +143,15 @@ describe('whitespace control', function() { expectTemplate('foo\n {{~> dude}} ') .withPartials({ dude: 'bar' }) .toCompileTo('foobar'); + expectTemplate('foo\n {{> dude}} ') .withPartials({ dude: 'bar' }) .toCompileTo('foo\n bar'); }); it('should only strip whitespace once', function() { - var hash = { foo: 'bar' }; - expectTemplate(' {{~foo~}} {{foo}} {{foo}} ') - .withInput(hash) + .withInput({ foo: 'bar' }) .toCompileTo('barbar bar '); }); }); From eed8bf9819adad314863f6090b861eee671b8855 Mon Sep 17 00:00:00 2001 From: John Boehr Date: Wed, 29 Apr 2020 16:13:46 -0700 Subject: [PATCH 09/11] Apply formatting changes requested by @nknapp --- spec/builtins.js | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/spec/builtins.js b/spec/builtins.js index bd44d8b74..a43fb81f5 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -520,16 +520,16 @@ describe('builtin helpers', function() { return new Iterator(this.arr); }; var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; - var goodbyes = new Iterable([ - { text: 'goodbye' }, - { text: 'Goodbye' }, - { text: 'GOODBYE' } - ]); - var goodbyesEmpty = new Iterable([]); - var hash = { goodbyes: goodbyes, world: 'world' }; expectTemplate(string) - .withInput(hash) + .withInput({ + goodbyes: new Iterable([ + { text: 'goodbye' }, + { text: 'Goodbye' }, + { text: 'GOODBYE' } + ]), + world: 'world' + }) .withMessage( 'each with array argument iterates over the contents when not empty' ) @@ -537,7 +537,7 @@ describe('builtin helpers', function() { expectTemplate(string) .withInput({ - goodbyes: goodbyesEmpty, + goodbyes: new Iterable([]), world: 'world' }) .withMessage( @@ -655,8 +655,6 @@ describe('builtin helpers', function() { }); it('should handle string log levels', function() { - var string = '{{log blah}}'; - var hash = { blah: 'whee' }; var called; console.error = function(log) { @@ -664,8 +662,8 @@ describe('builtin helpers', function() { called = true; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah}}') + .withInput({ blah: 'whee' }) .withRuntimeOptions({ data: { level: 'error' } }) .withCompileOptions({ data: true }) .toCompileTo(''); @@ -673,8 +671,8 @@ describe('builtin helpers', function() { called = false; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah}}') + .withInput({ blah: 'whee' }) .withRuntimeOptions({ data: { level: 'ERROR' } }) .withCompileOptions({ data: true }) .toCompileTo(''); @@ -682,8 +680,6 @@ describe('builtin helpers', function() { }); it('should handle hash log levels', function() { - var string = '{{log blah level="error"}}'; - var hash = { blah: 'whee' }; var called; console.error = function(log) { @@ -691,8 +687,8 @@ describe('builtin helpers', function() { called = true; }; - expectTemplate(string) - .withInput(hash) + expectTemplate('{{log blah level="error"}}') + .withInput({ blah: 'whee' }) .toCompileTo(''); equals(true, called); }); From 7471395db7e6798e6ca068bbe8c325bb6c566981 Mon Sep 17 00:00:00 2001 From: John Boehr Date: Thu, 30 Apr 2020 18:20:54 -0700 Subject: [PATCH 10/11] Apply formatting changes requested by @nknapp --- spec/data.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/spec/data.js b/spec/data.js index 8a126d2b2..74c686cb9 100644 --- a/spec/data.js +++ b/spec/data.js @@ -132,13 +132,9 @@ describe('data', function() { }); it('passing in data to a compiled function that expects data - works with helpers in partials', function() { - var partials = { - myPartial: CompilerContext.compile('{{hello}}', { data: true }) - }; - expectTemplate('{{>myPartial}}') .withCompileOptions({ data: true }) - .withPartials(partials) + .withPartial('myPartial', '{{hello}}') .withHelper('hello', function(options) { return options.data.adjective + ' ' + this.noun; }) @@ -242,16 +238,13 @@ describe('data', function() { describe('@root', function() { it('the root context can be looked up via @root', function() { - var string = '{{@root.foo}}'; - var input = { foo: 'hello' }; - - expectTemplate(string) - .withInput(input) + expectTemplate('{{@root.foo}}') + .withInput({ foo: 'hello' }) .withRuntimeOptions({ data: {} }) .toCompileTo('hello'); - expectTemplate(string) - .withInput(input) + expectTemplate('{{@root.foo}}') + .withInput({ foo: 'hello' }) .toCompileTo('hello'); }); From 0cc64fde2ef5d3029f845f4591bdfdd4d186adbd Mon Sep 17 00:00:00 2001 From: John Boehr Date: Thu, 30 Apr 2020 18:24:07 -0700 Subject: [PATCH 11/11] Clarify data "passed root values take priority" test See: https://github.com/handlebars-lang/handlebars.js/pull/1683#discussion_r418307352 --- spec/data.js | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/data.js b/spec/data.js index 74c686cb9..bde617326 100644 --- a/spec/data.js +++ b/spec/data.js @@ -250,6 +250,7 @@ describe('data', function() { it('passed root values take priority', function() { expectTemplate('{{@root.foo}}') + .withInput({ foo: 'should not be used' }) .withRuntimeOptions({ data: { root: { foo: 'hello' } } }) .toCompileTo('hello'); });