452afbf2ff
These allow for a given block to be wrapped in helper methods or metadata and allow for more control over the current container and method before the code is run.
348 lines
13 KiB
JavaScript
348 lines
13 KiB
JavaScript
describe('blocks', function() {
|
|
it('array', function() {
|
|
var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!';
|
|
var hash = {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');
|
|
});
|
|
|
|
it('array without data', function() {
|
|
var string = '{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}';
|
|
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
|
|
shouldCompileTo(string, [hash,,, false], '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'};
|
|
|
|
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('empty block', function() {
|
|
var string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!';
|
|
var hash = {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');
|
|
});
|
|
|
|
it('block with complex lookup', function() {
|
|
var string = '{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}';
|
|
var hash = {name: 'Alan', 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');
|
|
});
|
|
|
|
it('multiple blocks with complex lookup', function() {
|
|
var string = '{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}';
|
|
var hash = {name: 'Alan', goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]};
|
|
|
|
shouldCompileTo(string, hash, 'AlanAlanAlanAlanAlanAlan');
|
|
});
|
|
|
|
it('block with complex lookup using nested context', function() {
|
|
var string = '{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}';
|
|
|
|
shouldThrow(function() {
|
|
CompilerContext.compile(string);
|
|
}, 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' }] }] };
|
|
|
|
shouldCompileTo(string, hash, 'Goodbye cruel sad OMG!');
|
|
});
|
|
|
|
it('works with cached blocks', function() {
|
|
var template = CompilerContext.compile('{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}', {data: false});
|
|
|
|
var result = template({person: [{first: 'Alan', last: 'Johnson'}, {first: 'Alan', last: 'Johnson'}]});
|
|
equals(result, '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 = {};
|
|
shouldCompileTo(string, hash, 'Right On!', "Inverted section rendered when value isn't set.");
|
|
});
|
|
|
|
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.');
|
|
});
|
|
|
|
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.');
|
|
});
|
|
|
|
it('block inverted sections', function() {
|
|
shouldCompileTo('{{#people}}{{name}}{{^}}{{none}}{{/people}}', {none: 'No people'},
|
|
'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');
|
|
});
|
|
it('chained inverted sections with mismatch', function() {
|
|
shouldThrow(function() {
|
|
shouldCompileTo('{{#people}}{{name}}{{else if none}}{{none}}{{/if}}', {none: 'No people'},
|
|
'No people');
|
|
}, Error);
|
|
});
|
|
|
|
it('block inverted sections with empty arrays', function() {
|
|
shouldCompileTo('{{#people}}{{name}}{{^}}{{none}}{{/people}}', {none: 'No people', people: []},
|
|
'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');
|
|
});
|
|
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');
|
|
});
|
|
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');
|
|
});
|
|
it('should handle nesting', function() {
|
|
shouldCompileTo('{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.', {data: [1, 3, 5]}, '1\n3\n5\nOK.');
|
|
});
|
|
});
|
|
|
|
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' }] }] };
|
|
|
|
shouldCompileTo(string, [hash, undefined, undefined, true], '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' }] }] };
|
|
|
|
shouldCompileTo(string, [hash, undefined, undefined, true], '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' }] }] };
|
|
|
|
shouldCompileTo(string, [hash, undefined, undefined, true], 'Goodbye cruel ');
|
|
});
|
|
});
|
|
|
|
describe('decorators', function() {
|
|
it('should apply mustache decorators', function() {
|
|
var helpers = {
|
|
helper: function(options) {
|
|
return options.fn.run;
|
|
}
|
|
};
|
|
var decorators = {
|
|
decorator: function(fn) {
|
|
fn.run = 'success';
|
|
return fn;
|
|
}
|
|
};
|
|
shouldCompileTo(
|
|
'{{#helper}}{{*decorator}}{{/helper}}',
|
|
{hash: {}, helpers: helpers, decorators: decorators},
|
|
'success');
|
|
});
|
|
it('should apply allow undefined return', function() {
|
|
var helpers = {
|
|
helper: function(options) {
|
|
return options.fn() + options.fn.run;
|
|
}
|
|
};
|
|
var decorators = {
|
|
decorator: function(fn) {
|
|
fn.run = 'cess';
|
|
}
|
|
};
|
|
shouldCompileTo(
|
|
'{{#helper}}{{*decorator}}suc{{/helper}}',
|
|
{hash: {}, helpers: helpers, decorators: decorators},
|
|
'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;
|
|
}
|
|
};
|
|
shouldCompileTo(
|
|
'{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}',
|
|
{hash: {}, helpers: helpers, decorators: decorators},
|
|
'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();
|
|
}
|
|
};
|
|
shouldCompileTo(
|
|
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}',
|
|
{hash: {}, helpers: helpers, decorators: decorators},
|
|
'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;
|
|
}
|
|
};
|
|
shouldCompileTo(
|
|
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}',
|
|
{hash: {}, helpers: helpers, decorators: decorators},
|
|
'success');
|
|
});
|
|
|
|
it('should access parent variables', function() {
|
|
var helpers = {
|
|
helper: function(options) {
|
|
return options.fn.run;
|
|
}
|
|
};
|
|
var decorators = {
|
|
decorator: function(fn, props, container, options) {
|
|
fn.run = options.args;
|
|
return fn;
|
|
}
|
|
};
|
|
shouldCompileTo(
|
|
'{{#helper}}{{*decorator foo}}{{/helper}}',
|
|
{hash: {'foo': 'success'}, helpers: helpers, decorators: decorators},
|
|
'success');
|
|
});
|
|
it('should work with root program', function() {
|
|
var run;
|
|
var decorators = {
|
|
decorator: function(fn, props, container, options) {
|
|
equals(options.args[0], 'success');
|
|
run = true;
|
|
return fn;
|
|
}
|
|
};
|
|
shouldCompileTo(
|
|
'{{*decorator "success"}}',
|
|
{hash: {'foo': 'success'}, decorators: decorators},
|
|
'');
|
|
equals(run, true);
|
|
});
|
|
it('should fail when accessing variables from root', function() {
|
|
var run;
|
|
var decorators = {
|
|
decorator: function(fn, props, container, options) {
|
|
equals(options.args[0], undefined);
|
|
run = true;
|
|
return fn;
|
|
}
|
|
};
|
|
shouldCompileTo(
|
|
'{{*decorator foo}}',
|
|
{hash: {'foo': 'fail'}, decorators: decorators},
|
|
'');
|
|
equals(run, true);
|
|
});
|
|
|
|
describe('registration', function() {
|
|
it('unregisters', function() {
|
|
handlebarsEnv.decorators = {};
|
|
|
|
handlebarsEnv.registerDecorator('foo', function() {
|
|
return 'fail';
|
|
});
|
|
|
|
equals(!!handlebarsEnv.decorators.foo, true);
|
|
handlebarsEnv.unregisterDecorator('foo');
|
|
equals(handlebarsEnv.decorators.foo, undefined);
|
|
});
|
|
|
|
it('allows multiple globals', function() {
|
|
handlebarsEnv.decorators = {};
|
|
|
|
handlebarsEnv.registerDecorator({
|
|
foo: function() {},
|
|
bar: function() {}
|
|
});
|
|
|
|
equals(!!handlebarsEnv.decorators.foo, true);
|
|
equals(!!handlebarsEnv.decorators.bar, true);
|
|
handlebarsEnv.unregisterDecorator('foo');
|
|
handlebarsEnv.unregisterDecorator('bar');
|
|
equals(handlebarsEnv.decorators.foo, undefined);
|
|
equals(handlebarsEnv.decorators.bar, undefined);
|
|
});
|
|
it('fails with multiple and args', function() {
|
|
shouldThrow(function() {
|
|
handlebarsEnv.registerDecorator({
|
|
world: function() { return 'world!'; },
|
|
testHelper: function() { return 'found it!'; }
|
|
}, {});
|
|
}, Error, 'Arg not supported with multiple decorators');
|
|
});
|
|
});
|
|
});
|
|
});
|