9a49d35023
Adds multiple variable support and the ability to set statement level logging semantics. This breaks that logger API, cleaning up the manner in which enums are set, but the other behaviors are backwards compatible. Fixes #956
415 lines
16 KiB
JavaScript
415 lines
16 KiB
JavaScript
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');
|
|
});
|
|
|
|
it('if with function argument', function() {
|
|
var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
|
|
shouldCompileTo(string, {goodbye: function() {return true; }, world: 'world'}, 'GOODBYE cruel world!',
|
|
'if with function shows the contents when function returns true');
|
|
shouldCompileTo(string, {goodbye: function() {return this.world; }, world: 'world'}, 'GOODBYE cruel world!',
|
|
'if with function shows the contents when function returns string');
|
|
shouldCompileTo(string, {goodbye: function() {return false; }, world: 'world'}, 'cruel world!',
|
|
'if with function does not show the contents when returns false');
|
|
shouldCompileTo(string, {goodbye: function() {return this.foo; }, world: 'world'}, 'cruel world!',
|
|
'if with function does not show the contents when returns undefined');
|
|
});
|
|
});
|
|
|
|
describe('#with', function() {
|
|
it('with', function() {
|
|
var string = '{{#with person}}{{first}} {{last}}{{/with}}';
|
|
shouldCompileTo(string, {person: {first: 'Alan', last: 'Johnson'}}, 'Alan Johnson');
|
|
});
|
|
it('with with function argument', function() {
|
|
var string = '{{#with person}}{{first}} {{last}}{{/with}}';
|
|
shouldCompileTo(string, {person: function() { return {first: 'Alan', last: 'Johnson'}; }}, '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');
|
|
});
|
|
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');
|
|
});
|
|
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');
|
|
});
|
|
});
|
|
|
|
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}}!';
|
|
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], 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');
|
|
});
|
|
|
|
it('each without data', function() {
|
|
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
|
|
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
|
|
shouldCompileTo(string, [hash,,,, false], 'goodbye! Goodbye! GOODBYE! cruel world!');
|
|
|
|
hash = {goodbyes: 'cruel', world: 'world'};
|
|
shouldCompileTo('{{#each .}}{{.}}{{/each}}', [hash,,,, false], 'cruelworld');
|
|
});
|
|
|
|
it('each without context', function() {
|
|
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
|
|
shouldCompileTo(string, [,,,, ], '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');
|
|
shouldCompileTo(string, {goodbyes: {}, world: 'world'}, 'cruel world!');
|
|
});
|
|
|
|
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'};
|
|
|
|
var template = CompilerContext.compile(string);
|
|
var result = template(hash);
|
|
|
|
equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @index variable is used');
|
|
});
|
|
|
|
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'};
|
|
|
|
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');
|
|
});
|
|
|
|
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'};
|
|
|
|
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!');
|
|
});
|
|
|
|
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'};
|
|
|
|
var template = CompilerContext.compile(string);
|
|
var result = template(hash);
|
|
|
|
equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @index variable is used');
|
|
});
|
|
|
|
|
|
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'};
|
|
|
|
var template = CompilerContext.compile(string);
|
|
var result = template(hash);
|
|
|
|
equal(result, 'goodbye! cruel world!', 'The @first variable is used');
|
|
});
|
|
|
|
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'};
|
|
|
|
var template = CompilerContext.compile(string);
|
|
var result = template(hash);
|
|
|
|
equal(result, '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!', 'The @first variable is used');
|
|
});
|
|
|
|
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'};
|
|
|
|
var template = CompilerContext.compile(string);
|
|
var result = template(hash);
|
|
|
|
equal(result, 'goodbye! cruel world!', 'The @first variable is used');
|
|
});
|
|
|
|
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'};
|
|
|
|
var template = CompilerContext.compile(string);
|
|
var result = template(hash);
|
|
|
|
equal(result, 'GOODBYE! cruel world!', 'The @last variable is used');
|
|
});
|
|
|
|
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'};
|
|
|
|
var template = CompilerContext.compile(string);
|
|
var result = template(hash);
|
|
|
|
equal(result, 'Goodbye! cruel world!', 'The @last variable is used');
|
|
});
|
|
|
|
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'};
|
|
|
|
var template = CompilerContext.compile(string);
|
|
var result = template(hash);
|
|
|
|
equal(result, '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!', 'The @last variable is used');
|
|
});
|
|
|
|
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'};
|
|
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');
|
|
});
|
|
|
|
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');
|
|
});
|
|
|
|
it('each on implicit context', function() {
|
|
shouldThrow(function() {
|
|
var template = CompilerContext.compile('{{#each}}{{text}}! {{/each}}cruel world!');
|
|
template({});
|
|
}, handlebarsEnv.Exception, 'Must pass iterator to #each');
|
|
});
|
|
});
|
|
|
|
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 string = '{{log blah}}';
|
|
var hash = { blah: 'whee' };
|
|
|
|
var levelArg, logArg;
|
|
handlebarsEnv.log = function(level, arg) {
|
|
levelArg = level;
|
|
logArg = arg;
|
|
};
|
|
|
|
shouldCompileTo(string, hash, '', 'log should not display');
|
|
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' };
|
|
|
|
var levelArg, logArg;
|
|
handlebarsEnv.log = function(level, arg) {
|
|
levelArg = level;
|
|
logArg = arg;
|
|
};
|
|
|
|
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
|
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) {
|
|
equals('whee', info);
|
|
called = true;
|
|
};
|
|
console.log = function(log) {
|
|
equals('whee', log);
|
|
called = true;
|
|
};
|
|
|
|
shouldCompileTo(string, hash, '');
|
|
equals(true, called);
|
|
});
|
|
it('should log at data level', function() {
|
|
var string = '{{log blah}}';
|
|
var hash = { blah: 'whee' };
|
|
var called;
|
|
|
|
console.error = function(log) {
|
|
equals('whee', log);
|
|
called = true;
|
|
};
|
|
|
|
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
|
equals(true, called);
|
|
});
|
|
it('should handle missing logger', function() {
|
|
var string = '{{log blah}}';
|
|
var hash = { blah: 'whee' };
|
|
|
|
console.error = undefined;
|
|
|
|
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
|
});
|
|
|
|
it('should handle string log levels', function() {
|
|
var string = '{{log blah}}';
|
|
var hash = { blah: 'whee' };
|
|
var called;
|
|
|
|
console.error = function(log) {
|
|
equals('whee', log);
|
|
called = true;
|
|
};
|
|
|
|
shouldCompileTo(string, [hash,,,, {level: 'error'}], '');
|
|
equals(true, called);
|
|
|
|
called = false;
|
|
|
|
shouldCompileTo(string, [hash,,,, {level: 'ERROR'}], '');
|
|
equals(true, called);
|
|
});
|
|
it('should handle hash log levels', function() {
|
|
var string = '{{log blah level="error"}}';
|
|
var hash = { blah: 'whee' };
|
|
var called;
|
|
|
|
console.error = function(log) {
|
|
equals('whee', log);
|
|
called = true;
|
|
};
|
|
|
|
shouldCompileTo(string, hash, '');
|
|
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(log) {
|
|
equals('whee', log);
|
|
called = true;
|
|
};
|
|
|
|
shouldCompileTo(string, hash, '');
|
|
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) {
|
|
equals('whee', log1);
|
|
equals('foo', log2);
|
|
equals(1, log3);
|
|
called = true;
|
|
};
|
|
|
|
shouldCompileTo(string, hash, '');
|
|
equals(true, called);
|
|
});
|
|
/*eslint-enable no-console */
|
|
});
|
|
|
|
|
|
describe('#lookup', function() {
|
|
it('should lookup arbitrary content', 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');
|
|
});
|
|
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, '');
|
|
});
|
|
});
|
|
});
|