Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
handlebars.js/spec/builtins.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
768 lines (683 sloc)
22 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe('builtin helpers', function () { | |
describe('#if', function () { | |
it('if', function () { | |
var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; | |
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}}!'; | |
expectTemplate(string) | |
.withInput({ | |
goodbye: function () { | |
return true; | |
}, | |
world: 'world', | |
}) | |
.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', | |
}) | |
.withMessage( | |
'if with function shows the contents when function returns string' | |
) | |
.toCompileTo('GOODBYE cruel world!'); | |
expectTemplate(string) | |
.withInput({ | |
goodbye: function () { | |
return false; | |
}, | |
world: 'world', | |
}) | |
.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', | |
}) | |
.withMessage( | |
'if with function does not show the contents when returns undefined' | |
) | |
.toCompileTo('cruel world!'); | |
}); | |
it('should not change the depth list', function () { | |
expectTemplate( | |
'{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}' | |
) | |
.withInput({ | |
foo: { goodbye: true }, | |
world: 'world', | |
}) | |
.toCompileTo('GOODBYE cruel world!'); | |
}); | |
}); | |
describe('#with', function () { | |
it('with', function () { | |
expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}') | |
.withInput({ | |
person: { | |
first: 'Alan', | |
last: 'Johnson', | |
}, | |
}) | |
.toCompileTo('Alan Johnson'); | |
}); | |
it('with with function argument', function () { | |
expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}') | |
.withInput({ | |
person: function () { | |
return { | |
first: 'Alan', | |
last: 'Johnson', | |
}; | |
}, | |
}) | |
.toCompileTo('Alan Johnson'); | |
}); | |
it('with with else', function () { | |
expectTemplate( | |
'{{#with person}}Person is present{{else}}Person is not present{{/with}}' | |
).toCompileTo('Person is not present'); | |
}); | |
it('with provides block parameter', function () { | |
expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}') | |
.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}}') | |
.withInput({ person: { first: 'Alan', last: 'Johnson' } }) | |
.withCompileOptions({ data: false }) | |
.toCompileTo('Alan Johnson'); | |
}); | |
}); | |
describe('#each', function () { | |
beforeEach(function () { | |
handlebarsEnv.registerHelper('detectDataInsideEach', function (options) { | |
return options.data && options.data.exclaim; | |
}); | |
}); | |
it('each', function () { | |
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; | |
expectTemplate(string) | |
.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: [], | |
world: 'world', | |
}) | |
.withMessage('each with array argument ignores the contents when empty') | |
.toCompileTo('cruel world!'); | |
}); | |
it('each without data', function () { | |
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!'); | |
expectTemplate('{{#each .}}{{.}}{{/each}}') | |
.withInput({ goodbyes: 'cruel', world: 'world' }) | |
.withRuntimeOptions({ data: false }) | |
.withCompileOptions({ data: false }) | |
.toCompileTo('cruelworld'); | |
}); | |
it('each without context', function () { | |
expectTemplate('{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!') | |
.withInput(undefined) | |
.toCompileTo('cruel !'); | |
}); | |
it('each with an object and @key', function () { | |
var string = | |
'{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!'; | |
function Clazz() { | |
this['<b>#1</b>'] = { text: 'goodbye' }; | |
this[2] = { text: 'GOODBYE' }; | |
} | |
Clazz.prototype.foo = 'fail'; | |
var hash = { goodbyes: new Clazz(), world: 'world' }; | |
// Object property iteration order is undefined according to ECMA spec, | |
// so we need to check both possible orders | |
// @see http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop | |
var actual = compileWithPartials(string, hash); | |
var expected1 = | |
'<b>#1</b>. goodbye! 2. GOODBYE! cruel world!'; | |
var expected2 = | |
'2. GOODBYE! <b>#1</b>. goodbye! cruel world!'; | |
equals( | |
actual === expected1 || actual === expected2, | |
true, | |
'each with object argument iterates over the contents when not empty' | |
); | |
expectTemplate(string) | |
.withInput({ | |
goodbyes: {}, | |
world: 'world', | |
}) | |
.toCompileTo('cruel world!'); | |
}); | |
it('each with @index', function () { | |
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 () { | |
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!' | |
); | |
}); | |
it('each with block params', function () { | |
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 with block params and strict compilation', function () { | |
expectTemplate( | |
'{{#each goodbyes as |value index|}}{{index}}. {{value.text}}!{{/each}}' | |
) | |
.withCompileOptions({ strict: true }) | |
.withInput({ goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }] }) | |
.toCompileTo('0. goodbye!1. Goodbye!'); | |
}); | |
it('each object with @index', function () { | |
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 () { | |
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 () { | |
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!' | |
); | |
}); | |
it('each object with @first', function () { | |
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 () { | |
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 () { | |
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 () { | |
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!' | |
); | |
}); | |
it('each with function argument', function () { | |
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; | |
expectTemplate(string) | |
.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: [], | |
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 () { | |
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 () { | |
expectTemplate( | |
'{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}' | |
) | |
.withInput({ letters: ['a', 'b', 'c'] }) | |
.withMessage('should output data') | |
.withRuntimeOptions({ | |
data: { | |
exclaim: '!', | |
}, | |
}) | |
.toCompileTo('a!b!c!'); | |
}); | |
it('each on implicit context', function () { | |
expectTemplate('{{#each}}{{text}}! {{/each}}cruel world!').toThrow( | |
handlebarsEnv.Exception, | |
'Must pass iterator to #each' | |
); | |
}); | |
if (global.Symbol && global.Symbol.iterator) { | |
it('each on iterable', function () { | |
function Iterator(arr) { | |
this.arr = arr; | |
this.index = 0; | |
} | |
Iterator.prototype.next = function () { | |
var value = this.arr[this.index]; | |
var done = this.index === this.arr.length; | |
if (!done) { | |
this.index++; | |
} | |
return { value: value, done: done }; | |
}; | |
function Iterable(arr) { | |
this.arr = arr; | |
} | |
Iterable.prototype[global.Symbol.iterator] = function () { | |
return new Iterator(this.arr); | |
}; | |
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; | |
expectTemplate(string) | |
.withInput({ | |
goodbyes: new Iterable([ | |
{ 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: new Iterable([]), | |
world: 'world', | |
}) | |
.withMessage( | |
'each with array argument ignores the contents when empty' | |
) | |
.toCompileTo('cruel world!'); | |
}); | |
} | |
}); | |
describe('#log', function () { | |
/* eslint-disable no-console */ | |
if (typeof console === 'undefined') { | |
return; | |
} | |
var $log, $info, $error; | |
beforeEach(function () { | |
$log = console.log; | |
$info = console.info; | |
$error = console.error; | |
}); | |
afterEach(function () { | |
console.log = $log; | |
console.info = $info; | |
console.error = $error; | |
}); | |
it('should call logger at default level', function () { | |
var levelArg, logArg; | |
handlebarsEnv.log = function (level, arg) { | |
levelArg = level; | |
logArg = arg; | |
}; | |
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 levelArg, logArg; | |
handlebarsEnv.log = function (level, arg) { | |
levelArg = level; | |
logArg = arg; | |
}; | |
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 called; | |
console.info = function (info) { | |
equals('whee', info); | |
called = true; | |
console.info = $info; | |
console.log = $log; | |
}; | |
console.log = function (log) { | |
equals('whee', log); | |
called = true; | |
console.info = $info; | |
console.log = $log; | |
}; | |
expectTemplate('{{log blah}}') | |
.withInput({ blah: 'whee' }) | |
.toCompileTo(''); | |
equals(true, called); | |
}); | |
it('should log at data level', function () { | |
var called; | |
console.error = function (log) { | |
equals('whee', log); | |
called = true; | |
console.error = $error; | |
}; | |
expectTemplate('{{log blah}}') | |
.withInput({ blah: 'whee' }) | |
.withRuntimeOptions({ data: { level: '03' } }) | |
.withCompileOptions({ data: true }) | |
.toCompileTo(''); | |
equals(true, called); | |
}); | |
it('should handle missing logger', function () { | |
var called = false; | |
console.error = undefined; | |
console.log = function (log) { | |
equals('whee', log); | |
called = true; | |
console.log = $log; | |
}; | |
expectTemplate('{{log blah}}') | |
.withInput({ blah: 'whee' }) | |
.withRuntimeOptions({ data: { level: '03' } }) | |
.withCompileOptions({ data: true }) | |
.toCompileTo(''); | |
equals(true, called); | |
}); | |
it('should handle string log levels', function () { | |
var called; | |
console.error = function (log) { | |
equals('whee', log); | |
called = true; | |
}; | |
expectTemplate('{{log blah}}') | |
.withInput({ blah: 'whee' }) | |
.withRuntimeOptions({ data: { level: 'error' } }) | |
.withCompileOptions({ data: true }) | |
.toCompileTo(''); | |
equals(true, called); | |
called = false; | |
expectTemplate('{{log blah}}') | |
.withInput({ blah: 'whee' }) | |
.withRuntimeOptions({ data: { level: 'ERROR' } }) | |
.withCompileOptions({ data: true }) | |
.toCompileTo(''); | |
equals(true, called); | |
}); | |
it('should handle hash log levels', function () { | |
var called; | |
console.error = function (log) { | |
equals('whee', log); | |
called = true; | |
}; | |
expectTemplate('{{log blah level="error"}}') | |
.withInput({ blah: 'whee' }) | |
.toCompileTo(''); | |
equals(true, called); | |
}); | |
it('should handle hash log levels', function () { | |
var called = false; | |
console.info = | |
console.log = | |
console.error = | |
console.debug = | |
function () { | |
called = true; | |
console.info = console.log = console.error = console.debug = $log; | |
}; | |
expectTemplate('{{log blah level="debug"}}') | |
.withInput({ blah: 'whee' }) | |
.toCompileTo(''); | |
equals(false, called); | |
}); | |
it('should pass multiple log arguments', function () { | |
var called; | |
console.info = console.log = function (log1, log2, log3) { | |
equals('whee', log1); | |
equals('foo', log2); | |
equals(1, log3); | |
called = true; | |
console.log = $log; | |
}; | |
expectTemplate('{{log blah "foo" 1}}') | |
.withInput({ blah: 'whee' }) | |
.toCompileTo(''); | |
equals(true, called); | |
}); | |
it('should pass zero log arguments', function () { | |
var called; | |
console.info = console.log = function () { | |
expect(arguments.length).to.equal(0); | |
called = true; | |
console.log = $log; | |
}; | |
expectTemplate('{{log}}').withInput({ blah: 'whee' }).toCompileTo(''); | |
expect(called).to.be.true(); | |
}); | |
/* eslint-enable no-console */ | |
}); | |
describe('#lookup', function () { | |
it('should lookup arbitrary content', function () { | |
expectTemplate('{{#each goodbyes}}{{lookup ../data .}}{{/each}}') | |
.withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] }) | |
.toCompileTo('foobar'); | |
}); | |
it('should not fail on undefined value', function () { | |
expectTemplate('{{#each goodbyes}}{{lookup ../bar .}}{{/each}}') | |
.withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] }) | |
.toCompileTo(''); | |
}); | |
}); | |
}); |