style: reformat all files using prettier
This commit is contained in:
+158
-78
@@ -7,44 +7,102 @@ describe('ast', function() {
|
||||
|
||||
describe('BlockStatement', function() {
|
||||
it('should throw on mustache mismatch', function() {
|
||||
shouldThrow(function() {
|
||||
handlebarsEnv.parse('\n {{#foo}}{{/bar}}');
|
||||
}, Handlebars.Exception, "foo doesn't match bar - 2:5");
|
||||
shouldThrow(
|
||||
function() {
|
||||
handlebarsEnv.parse('\n {{#foo}}{{/bar}}');
|
||||
},
|
||||
Handlebars.Exception,
|
||||
"foo doesn't match bar - 2:5"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('helpers', function() {
|
||||
describe('#helperExpression', function() {
|
||||
it('should handle mustache statements', function() {
|
||||
equals(AST.helpers.helperExpression({type: 'MustacheStatement', params: [], hash: undefined}), false);
|
||||
equals(AST.helpers.helperExpression({type: 'MustacheStatement', params: [1], hash: undefined}), true);
|
||||
equals(AST.helpers.helperExpression({type: 'MustacheStatement', params: [], hash: {}}), true);
|
||||
equals(
|
||||
AST.helpers.helperExpression({
|
||||
type: 'MustacheStatement',
|
||||
params: [],
|
||||
hash: undefined
|
||||
}),
|
||||
false
|
||||
);
|
||||
equals(
|
||||
AST.helpers.helperExpression({
|
||||
type: 'MustacheStatement',
|
||||
params: [1],
|
||||
hash: undefined
|
||||
}),
|
||||
true
|
||||
);
|
||||
equals(
|
||||
AST.helpers.helperExpression({
|
||||
type: 'MustacheStatement',
|
||||
params: [],
|
||||
hash: {}
|
||||
}),
|
||||
true
|
||||
);
|
||||
});
|
||||
it('should handle block statements', function() {
|
||||
equals(AST.helpers.helperExpression({type: 'BlockStatement', params: [], hash: undefined}), false);
|
||||
equals(AST.helpers.helperExpression({type: 'BlockStatement', params: [1], hash: undefined}), true);
|
||||
equals(AST.helpers.helperExpression({type: 'BlockStatement', params: [], hash: {}}), true);
|
||||
equals(
|
||||
AST.helpers.helperExpression({
|
||||
type: 'BlockStatement',
|
||||
params: [],
|
||||
hash: undefined
|
||||
}),
|
||||
false
|
||||
);
|
||||
equals(
|
||||
AST.helpers.helperExpression({
|
||||
type: 'BlockStatement',
|
||||
params: [1],
|
||||
hash: undefined
|
||||
}),
|
||||
true
|
||||
);
|
||||
equals(
|
||||
AST.helpers.helperExpression({
|
||||
type: 'BlockStatement',
|
||||
params: [],
|
||||
hash: {}
|
||||
}),
|
||||
true
|
||||
);
|
||||
});
|
||||
it('should handle subexpressions', function() {
|
||||
equals(AST.helpers.helperExpression({type: 'SubExpression'}), true);
|
||||
equals(AST.helpers.helperExpression({ type: 'SubExpression' }), true);
|
||||
});
|
||||
it('should work with non-helper nodes', function() {
|
||||
equals(AST.helpers.helperExpression({type: 'Program'}), false);
|
||||
equals(AST.helpers.helperExpression({ type: 'Program' }), false);
|
||||
|
||||
equals(AST.helpers.helperExpression({type: 'PartialStatement'}), false);
|
||||
equals(AST.helpers.helperExpression({type: 'ContentStatement'}), false);
|
||||
equals(AST.helpers.helperExpression({type: 'CommentStatement'}), false);
|
||||
equals(
|
||||
AST.helpers.helperExpression({ type: 'PartialStatement' }),
|
||||
false
|
||||
);
|
||||
equals(
|
||||
AST.helpers.helperExpression({ type: 'ContentStatement' }),
|
||||
false
|
||||
);
|
||||
equals(
|
||||
AST.helpers.helperExpression({ type: 'CommentStatement' }),
|
||||
false
|
||||
);
|
||||
|
||||
equals(AST.helpers.helperExpression({type: 'PathExpression'}), false);
|
||||
equals(AST.helpers.helperExpression({ type: 'PathExpression' }), false);
|
||||
|
||||
equals(AST.helpers.helperExpression({type: 'StringLiteral'}), false);
|
||||
equals(AST.helpers.helperExpression({type: 'NumberLiteral'}), false);
|
||||
equals(AST.helpers.helperExpression({type: 'BooleanLiteral'}), false);
|
||||
equals(AST.helpers.helperExpression({type: 'UndefinedLiteral'}), false);
|
||||
equals(AST.helpers.helperExpression({type: 'NullLiteral'}), false);
|
||||
equals(AST.helpers.helperExpression({ type: 'StringLiteral' }), false);
|
||||
equals(AST.helpers.helperExpression({ type: 'NumberLiteral' }), false);
|
||||
equals(AST.helpers.helperExpression({ type: 'BooleanLiteral' }), false);
|
||||
equals(
|
||||
AST.helpers.helperExpression({ type: 'UndefinedLiteral' }),
|
||||
false
|
||||
);
|
||||
equals(AST.helpers.helperExpression({ type: 'NullLiteral' }), false);
|
||||
|
||||
equals(AST.helpers.helperExpression({type: 'Hash'}), false);
|
||||
equals(AST.helpers.helperExpression({type: 'HashPair'}), false);
|
||||
equals(AST.helpers.helperExpression({ type: 'Hash' }), false);
|
||||
equals(AST.helpers.helperExpression({ type: 'HashPair' }), false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -61,17 +119,18 @@ describe('ast', function() {
|
||||
|
||||
/* eslint-disable no-multi-spaces */
|
||||
ast = Handlebars.parse(
|
||||
'line 1 {{line1Token}}\n' // 1
|
||||
+ ' line 2 {{line2token}}\n' // 2
|
||||
+ ' line 3 {{#blockHelperOnLine3}}\n' // 3
|
||||
+ 'line 4{{line4token}}\n' // 4
|
||||
+ 'line5{{else}}\n' // 5
|
||||
+ '{{line6Token}}\n' // 6
|
||||
+ '{{/blockHelperOnLine3}}\n' // 7
|
||||
+ '{{#open}}\n' // 8
|
||||
+ '{{else inverse}}\n' // 9
|
||||
+ '{{else}}\n' // 10
|
||||
+ '{{/open}}'); // 11
|
||||
'line 1 {{line1Token}}\n' + // 1
|
||||
' line 2 {{line2token}}\n' + // 2
|
||||
' line 3 {{#blockHelperOnLine3}}\n' + // 3
|
||||
'line 4{{line4token}}\n' + // 4
|
||||
'line5{{else}}\n' + // 5
|
||||
'{{line6Token}}\n' + // 6
|
||||
'{{/blockHelperOnLine3}}\n' + // 7
|
||||
'{{#open}}\n' + // 8
|
||||
'{{else inverse}}\n' + // 9
|
||||
'{{else}}\n' + // 10
|
||||
'{{/open}}'
|
||||
); // 11
|
||||
/* eslint-enable no-multi-spaces */
|
||||
body = ast.body;
|
||||
|
||||
@@ -92,35 +151,35 @@ describe('ast', function() {
|
||||
it('gets MustacheStatement line numbers correct across newlines', function() {
|
||||
var secondMustacheStatement = body[3];
|
||||
testColumns(secondMustacheStatement, 2, 2, 8, 22);
|
||||
});
|
||||
});
|
||||
|
||||
it('gets the block helper information correct', function() {
|
||||
var blockHelperNode = body[5];
|
||||
testColumns(blockHelperNode, 3, 7, 8, 23);
|
||||
});
|
||||
it('gets the block helper information correct', function() {
|
||||
var blockHelperNode = body[5];
|
||||
testColumns(blockHelperNode, 3, 7, 8, 23);
|
||||
});
|
||||
|
||||
it('correctly records the line numbers the program of a block helper', function() {
|
||||
var blockHelperNode = body[5],
|
||||
program = blockHelperNode.program;
|
||||
it('correctly records the line numbers the program of a block helper', function() {
|
||||
var blockHelperNode = body[5],
|
||||
program = blockHelperNode.program;
|
||||
|
||||
testColumns(program, 3, 5, 31, 5);
|
||||
});
|
||||
testColumns(program, 3, 5, 31, 5);
|
||||
});
|
||||
|
||||
it('correctly records the line numbers of an inverse of a block helper', function() {
|
||||
var blockHelperNode = body[5],
|
||||
inverse = blockHelperNode.inverse;
|
||||
it('correctly records the line numbers of an inverse of a block helper', function() {
|
||||
var blockHelperNode = body[5],
|
||||
inverse = blockHelperNode.inverse;
|
||||
|
||||
testColumns(inverse, 5, 7, 13, 0);
|
||||
});
|
||||
testColumns(inverse, 5, 7, 13, 0);
|
||||
});
|
||||
|
||||
it('correctly records the line number of chained inverses', function() {
|
||||
var chainInverseNode = body[7];
|
||||
it('correctly records the line number of chained inverses', function() {
|
||||
var chainInverseNode = body[7];
|
||||
|
||||
testColumns(chainInverseNode.program, 8, 9, 9, 0);
|
||||
testColumns(chainInverseNode.inverse, 9, 10, 16, 0);
|
||||
testColumns(chainInverseNode.inverse.body[0].program, 9, 10, 16, 0);
|
||||
testColumns(chainInverseNode.inverse.body[0].inverse, 10, 11, 8, 0);
|
||||
});
|
||||
testColumns(chainInverseNode.program, 8, 9, 9, 0);
|
||||
testColumns(chainInverseNode.inverse, 9, 10, 16, 0);
|
||||
testColumns(chainInverseNode.inverse.body[0].program, 9, 10, 16, 0);
|
||||
testColumns(chainInverseNode.inverse.body[0].inverse, 10, 11, 8, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('whitespace control', function() {
|
||||
@@ -149,7 +208,9 @@ describe('ast', function() {
|
||||
});
|
||||
|
||||
it('block statements', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing(' {{# comment~}} \nfoo\n {{~/comment}}');
|
||||
var ast = Handlebars.parseWithoutProcessing(
|
||||
' {{# comment~}} \nfoo\n {{~/comment}}'
|
||||
);
|
||||
|
||||
equals(ast.body[0].value, ' ');
|
||||
equals(ast.body[1].program.body[0].value, ' \nfoo\n ');
|
||||
@@ -167,7 +228,9 @@ describe('ast', function() {
|
||||
});
|
||||
describe('blocks - parseWithoutProcessing', function() {
|
||||
it('block mustaches', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||
var ast = Handlebars.parseWithoutProcessing(
|
||||
' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '
|
||||
),
|
||||
block = ast.body[1];
|
||||
|
||||
equals(ast.body[0].value, ' ');
|
||||
@@ -178,13 +241,17 @@ describe('ast', function() {
|
||||
equals(ast.body[2].value, ' ');
|
||||
});
|
||||
it('initial block mustaches', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('{{# comment}} \nfoo\n {{/comment}}'),
|
||||
var ast = Handlebars.parseWithoutProcessing(
|
||||
'{{# comment}} \nfoo\n {{/comment}}'
|
||||
),
|
||||
block = ast.body[0];
|
||||
|
||||
equals(block.program.body[0].value, ' \nfoo\n ');
|
||||
});
|
||||
it('mustaches with children', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('{{# comment}} \n{{foo}}\n {{/comment}}'),
|
||||
var ast = Handlebars.parseWithoutProcessing(
|
||||
'{{# comment}} \n{{foo}}\n {{/comment}}'
|
||||
),
|
||||
block = ast.body[0];
|
||||
|
||||
equals(block.program.body[0].value, ' \n');
|
||||
@@ -192,7 +259,9 @@ describe('ast', function() {
|
||||
equals(block.program.body[2].value, '\n ');
|
||||
});
|
||||
it('nested block mustaches', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'),
|
||||
var ast = Handlebars.parseWithoutProcessing(
|
||||
'{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'
|
||||
),
|
||||
body = ast.body[0].program.body,
|
||||
block = body[1];
|
||||
|
||||
@@ -202,8 +271,10 @@ describe('ast', function() {
|
||||
equals(block.inverse.body[0].value, ' \n bar \n ');
|
||||
});
|
||||
it('column 0 block mustaches', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||
block = ast.body[1];
|
||||
var ast = Handlebars.parseWithoutProcessing(
|
||||
'test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '
|
||||
),
|
||||
block = ast.body[1];
|
||||
|
||||
equals(ast.body[0].omit, undefined);
|
||||
|
||||
@@ -215,8 +286,10 @@ describe('ast', function() {
|
||||
});
|
||||
describe('blocks', function() {
|
||||
it('marks block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||
block = ast.body[1];
|
||||
var ast = Handlebars.parse(
|
||||
' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '
|
||||
),
|
||||
block = ast.body[1];
|
||||
|
||||
equals(ast.body[0].value, '');
|
||||
|
||||
@@ -227,22 +300,24 @@ describe('ast', function() {
|
||||
});
|
||||
it('marks initial block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('{{# comment}} \nfoo\n {{/comment}}'),
|
||||
block = ast.body[0];
|
||||
block = ast.body[0];
|
||||
|
||||
equals(block.program.body[0].value, 'foo\n');
|
||||
});
|
||||
it('marks mustaches with children as standalone', function() {
|
||||
var ast = Handlebars.parse('{{# comment}} \n{{foo}}\n {{/comment}}'),
|
||||
block = ast.body[0];
|
||||
block = ast.body[0];
|
||||
|
||||
equals(block.program.body[0].value, '');
|
||||
equals(block.program.body[1].path.original, 'foo');
|
||||
equals(block.program.body[2].value, '\n');
|
||||
});
|
||||
it('marks nested block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'),
|
||||
body = ast.body[0].program.body,
|
||||
block = body[1];
|
||||
var ast = Handlebars.parse(
|
||||
'{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'
|
||||
),
|
||||
body = ast.body[0].program.body,
|
||||
block = body[1];
|
||||
|
||||
equals(body[0].value, '');
|
||||
|
||||
@@ -252,9 +327,11 @@ describe('ast', function() {
|
||||
equals(body[0].value, '');
|
||||
});
|
||||
it('does not mark nested block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('{{#foo}} {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} {{/foo}}'),
|
||||
body = ast.body[0].program.body,
|
||||
block = body[1];
|
||||
var ast = Handlebars.parse(
|
||||
'{{#foo}} {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} {{/foo}}'
|
||||
),
|
||||
body = ast.body[0].program.body,
|
||||
block = body[1];
|
||||
|
||||
equals(body[0].omit, undefined);
|
||||
|
||||
@@ -264,9 +341,11 @@ describe('ast', function() {
|
||||
equals(body[0].omit, undefined);
|
||||
});
|
||||
it('does not mark nested initial block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('{{#foo}}{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}}{{/foo}}'),
|
||||
body = ast.body[0].program.body,
|
||||
block = body[0];
|
||||
var ast = Handlebars.parse(
|
||||
'{{#foo}}{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}}{{/foo}}'
|
||||
),
|
||||
body = ast.body[0].program.body,
|
||||
block = body[0];
|
||||
|
||||
equals(block.program.body[0].value, ' \nfoo\n');
|
||||
equals(block.inverse.body[0].value, ' bar \n ');
|
||||
@@ -275,8 +354,10 @@ describe('ast', function() {
|
||||
});
|
||||
|
||||
it('marks column 0 block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||
block = ast.body[1];
|
||||
var ast = Handlebars.parse(
|
||||
'test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '
|
||||
),
|
||||
block = ast.body[1];
|
||||
|
||||
equals(ast.body[0].omit, undefined);
|
||||
|
||||
@@ -348,4 +429,3 @@ describe('ast', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+442
-139
@@ -18,8 +18,12 @@ describe('basic context', function() {
|
||||
});
|
||||
|
||||
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');
|
||||
shouldCompileTo(
|
||||
'Goodbye\n{{cruel}}\n{{world}}!',
|
||||
{ cruel: 'cruel', world: 'world' },
|
||||
'Goodbye\ncruel\nworld!',
|
||||
'It works if all the required keys are provided'
|
||||
);
|
||||
});
|
||||
|
||||
it('compiling with a string context', function() {
|
||||
@@ -27,15 +31,26 @@ describe('basic context', function() {
|
||||
});
|
||||
|
||||
it('compiling with an undefined context', function() {
|
||||
shouldCompileTo('Goodbye\n{{cruel}}\n{{world.bar}}!', undefined, 'Goodbye\n\n!');
|
||||
shouldCompileTo(
|
||||
'Goodbye\n{{cruel}}\n{{world.bar}}!',
|
||||
undefined,
|
||||
'Goodbye\n\n!'
|
||||
);
|
||||
|
||||
shouldCompileTo('{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}', undefined, 'Goodbye');
|
||||
shouldCompileTo(
|
||||
'{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}',
|
||||
undefined,
|
||||
'Goodbye'
|
||||
);
|
||||
});
|
||||
|
||||
it('comments', function() {
|
||||
shouldCompileTo('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!',
|
||||
{cruel: 'cruel', world: 'world'}, 'Goodbye\ncruel\nworld!',
|
||||
'comments are ignored');
|
||||
shouldCompileTo(
|
||||
'{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!',
|
||||
{ cruel: 'cruel', world: 'world' },
|
||||
'Goodbye\ncruel\nworld!',
|
||||
'comments are ignored'
|
||||
);
|
||||
|
||||
shouldCompileTo(' {{~! comment ~}} blah', {}, 'blah');
|
||||
shouldCompileTo(' {{~!-- long-comment --~}} blah', {}, 'blah');
|
||||
@@ -47,237 +62,506 @@ describe('basic context', function() {
|
||||
|
||||
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: 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');
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ goodbye: false, world: 'world' },
|
||||
'cruel world!',
|
||||
'booleans do not show the contents when false'
|
||||
);
|
||||
});
|
||||
|
||||
it('zeros', function() {
|
||||
shouldCompileTo('num1: {{num1}}, num2: {{num2}}', {num1: 42, num2: 0},
|
||||
'num1: 42, num2: 0');
|
||||
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');
|
||||
shouldCompileTo('num: {{num1/num2}}', { num1: { num2: 0 } }, '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(
|
||||
'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(
|
||||
'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');
|
||||
shouldCompileTo(
|
||||
'val1: {{{val1}}}, val2: {{{val2}}}',
|
||||
{ val1: false, val2: new Boolean(false) },
|
||||
'val1: false, val2: false'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'val: {{{val1/val2}}}',
|
||||
{ val1: { val2: false } },
|
||||
'val: false'
|
||||
);
|
||||
/* eslint-enable */
|
||||
});
|
||||
|
||||
it('should handle undefined and null', function() {
|
||||
shouldCompileTo('{{awesome undefined null}}',
|
||||
{
|
||||
awesome: function(_undefined, _null, options) {
|
||||
return (_undefined === undefined) + ' ' + (_null === null) + ' ' + (typeof options);
|
||||
}
|
||||
},
|
||||
'true true object');
|
||||
shouldCompileTo('{{undefined}}',
|
||||
{
|
||||
'undefined': function() {
|
||||
return 'undefined!';
|
||||
}
|
||||
},
|
||||
'undefined!');
|
||||
shouldCompileTo('{{null}}',
|
||||
{
|
||||
'null': function() {
|
||||
return 'null!';
|
||||
}
|
||||
},
|
||||
'null!');
|
||||
shouldCompileTo(
|
||||
'{{awesome undefined null}}',
|
||||
{
|
||||
awesome: function(_undefined, _null, options) {
|
||||
return (
|
||||
(_undefined === undefined) +
|
||||
' ' +
|
||||
(_null === null) +
|
||||
' ' +
|
||||
typeof options
|
||||
);
|
||||
}
|
||||
},
|
||||
'true true object'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{undefined}}',
|
||||
{
|
||||
undefined: function() {
|
||||
return 'undefined!';
|
||||
}
|
||||
},
|
||||
'undefined!'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{null}}',
|
||||
{
|
||||
null: function() {
|
||||
return 'null!';
|
||||
}
|
||||
},
|
||||
'null!'
|
||||
);
|
||||
});
|
||||
|
||||
it('newlines', function() {
|
||||
shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest");
|
||||
shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest");
|
||||
shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest");
|
||||
shouldCompileTo("Alan's\rTest", {}, "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');
|
||||
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'
|
||||
);
|
||||
});
|
||||
|
||||
it('escaping expressions', function() {
|
||||
shouldCompileTo('{{{awesome}}}', {awesome: '&\'\\<>'}, '&\'\\<>',
|
||||
"expressions with 3 handlebars aren't escaped");
|
||||
shouldCompileTo(
|
||||
'{{{awesome}}}',
|
||||
{ awesome: "&'\\<>" },
|
||||
"&'\\<>",
|
||||
"expressions with 3 handlebars aren't escaped"
|
||||
);
|
||||
|
||||
shouldCompileTo('{{&awesome}}', {awesome: '&\'\\<>'}, '&\'\\<>',
|
||||
"expressions with {{& handlebars aren't escaped");
|
||||
shouldCompileTo(
|
||||
'{{&awesome}}',
|
||||
{ awesome: "&'\\<>" },
|
||||
"&'\\<>",
|
||||
"expressions with {{& handlebars aren't escaped"
|
||||
);
|
||||
|
||||
shouldCompileTo('{{awesome}}', {awesome: "&\"'`\\<>"}, '&"'`\\<>',
|
||||
'by default expressions should be escaped');
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
{ awesome: '&"\'`\\<>' },
|
||||
'&"'`\\<>',
|
||||
'by default expressions should be escaped'
|
||||
);
|
||||
|
||||
shouldCompileTo('{{awesome}}', {awesome: 'Escaped, <b> looks like: <b>'}, 'Escaped, <b> looks like: &lt;b&gt;',
|
||||
'escaping should properly handle amperstands');
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
{ awesome: 'Escaped, <b> looks like: <b>' },
|
||||
'Escaped, <b> looks like: &lt;b&gt;',
|
||||
'escaping should properly handle amperstands'
|
||||
);
|
||||
});
|
||||
|
||||
it("functions returning safestrings shouldn't be escaped", function() {
|
||||
var hash = {awesome: function() { return new Handlebars.SafeString('&\'\\<>'); }};
|
||||
shouldCompileTo('{{awesome}}', hash, '&\'\\<>',
|
||||
"functions returning safestrings aren't escaped");
|
||||
var hash = {
|
||||
awesome: function() {
|
||||
return new Handlebars.SafeString("&'\\<>");
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
hash,
|
||||
"&'\\<>",
|
||||
"functions returning safestrings aren't escaped"
|
||||
);
|
||||
});
|
||||
|
||||
it('functions', function() {
|
||||
shouldCompileTo('{{awesome}}', {awesome: function() { return 'Awesome'; }}, 'Awesome',
|
||||
'functions are called and render their output');
|
||||
shouldCompileTo('{{awesome}}', {awesome: function() { return this.more; }, more: 'More awesome'}, 'More awesome',
|
||||
'functions are bound to the context');
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
{
|
||||
awesome: function() {
|
||||
return 'Awesome';
|
||||
}
|
||||
},
|
||||
'Awesome',
|
||||
'functions are called and render their output'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
{
|
||||
awesome: function() {
|
||||
return this.more;
|
||||
},
|
||||
more: 'More awesome'
|
||||
},
|
||||
'More awesome',
|
||||
'functions are bound to the context'
|
||||
);
|
||||
});
|
||||
|
||||
it('functions with context argument', function() {
|
||||
shouldCompileTo('{{awesome frank}}',
|
||||
{awesome: function(context) { return context; },
|
||||
frank: 'Frank'},
|
||||
'Frank', 'functions are called with context arguments');
|
||||
shouldCompileTo(
|
||||
'{{awesome frank}}',
|
||||
{
|
||||
awesome: function(context) {
|
||||
return context;
|
||||
},
|
||||
frank: 'Frank'
|
||||
},
|
||||
'Frank',
|
||||
'functions are called with context arguments'
|
||||
);
|
||||
});
|
||||
it('pathed functions with context argument', function() {
|
||||
shouldCompileTo('{{bar.awesome frank}}',
|
||||
{bar: {awesome: function(context) { return context; }},
|
||||
frank: 'Frank'},
|
||||
'Frank', 'functions are called with context arguments');
|
||||
shouldCompileTo(
|
||||
'{{bar.awesome frank}}',
|
||||
{
|
||||
bar: {
|
||||
awesome: function(context) {
|
||||
return context;
|
||||
}
|
||||
},
|
||||
frank: 'Frank'
|
||||
},
|
||||
'Frank',
|
||||
'functions are called with context arguments'
|
||||
);
|
||||
});
|
||||
it('depthed functions with context argument', function() {
|
||||
shouldCompileTo('{{#with frank}}{{../awesome .}}{{/with}}',
|
||||
{awesome: function(context) { return context; },
|
||||
frank: 'Frank'},
|
||||
'Frank', 'functions are called with context arguments');
|
||||
shouldCompileTo(
|
||||
'{{#with frank}}{{../awesome .}}{{/with}}',
|
||||
{
|
||||
awesome: function(context) {
|
||||
return context;
|
||||
},
|
||||
frank: 'Frank'
|
||||
},
|
||||
'Frank',
|
||||
'functions are called with context arguments'
|
||||
);
|
||||
});
|
||||
|
||||
it('block functions with context argument', function() {
|
||||
shouldCompileTo('{{#awesome 1}}inner {{.}}{{/awesome}}',
|
||||
{awesome: function(context, options) { return options.fn(context); }},
|
||||
'inner 1', 'block functions are called with context and options');
|
||||
shouldCompileTo(
|
||||
'{{#awesome 1}}inner {{.}}{{/awesome}}',
|
||||
{
|
||||
awesome: function(context, options) {
|
||||
return options.fn(context);
|
||||
}
|
||||
},
|
||||
'inner 1',
|
||||
'block functions are called with context and options'
|
||||
);
|
||||
});
|
||||
|
||||
it('depthed block functions with context argument', function() {
|
||||
shouldCompileTo('{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}',
|
||||
{value: true, awesome: function(context, options) { return options.fn(context); }},
|
||||
'inner 1', 'block functions are called with context and options');
|
||||
shouldCompileTo(
|
||||
'{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}',
|
||||
{
|
||||
value: true,
|
||||
awesome: function(context, options) {
|
||||
return options.fn(context);
|
||||
}
|
||||
},
|
||||
'inner 1',
|
||||
'block functions are called with context and options'
|
||||
);
|
||||
});
|
||||
|
||||
it('block functions without context argument', function() {
|
||||
shouldCompileTo('{{#awesome}}inner{{/awesome}}',
|
||||
{awesome: function(options) { return options.fn(this); }},
|
||||
'inner', 'block functions are called with options');
|
||||
shouldCompileTo(
|
||||
'{{#awesome}}inner{{/awesome}}',
|
||||
{
|
||||
awesome: function(options) {
|
||||
return options.fn(this);
|
||||
}
|
||||
},
|
||||
'inner',
|
||||
'block functions are called with options'
|
||||
);
|
||||
});
|
||||
it('pathed block functions without context argument', function() {
|
||||
shouldCompileTo('{{#foo.awesome}}inner{{/foo.awesome}}',
|
||||
{foo: {awesome: function() { return this; }}},
|
||||
'inner', 'block functions are called with options');
|
||||
shouldCompileTo(
|
||||
'{{#foo.awesome}}inner{{/foo.awesome}}',
|
||||
{
|
||||
foo: {
|
||||
awesome: function() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
},
|
||||
'inner',
|
||||
'block functions are called with options'
|
||||
);
|
||||
});
|
||||
it('depthed block functions without context argument', function() {
|
||||
shouldCompileTo('{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}',
|
||||
{value: true, awesome: function() { return this; }},
|
||||
'inner', 'block functions are called with options');
|
||||
shouldCompileTo(
|
||||
'{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}',
|
||||
{
|
||||
value: true,
|
||||
awesome: function() {
|
||||
return this;
|
||||
}
|
||||
},
|
||||
'inner',
|
||||
'block functions are called with options'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
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 (-)');
|
||||
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 (-)'
|
||||
);
|
||||
});
|
||||
|
||||
it('nested paths', function() {
|
||||
shouldCompileTo('Goodbye {{alan/expression}} world!', {alan: {expression: 'beautiful'}},
|
||||
'Goodbye beautiful world!', 'Nested paths access nested objects');
|
||||
shouldCompileTo(
|
||||
'Goodbye {{alan/expression}} world!',
|
||||
{ alan: { expression: 'beautiful' } },
|
||||
'Goodbye beautiful world!',
|
||||
'Nested paths access nested objects'
|
||||
);
|
||||
});
|
||||
|
||||
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');
|
||||
shouldCompileTo(
|
||||
'Goodbye {{alan/expression}} world!',
|
||||
{ alan: { expression: '' } },
|
||||
'Goodbye world!',
|
||||
'Nested paths access nested objects with empty string'
|
||||
);
|
||||
});
|
||||
|
||||
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');
|
||||
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'
|
||||
);
|
||||
});
|
||||
|
||||
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!');
|
||||
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!'
|
||||
);
|
||||
});
|
||||
|
||||
it("that current context path ({{.}}) doesn't hit helpers", function() {
|
||||
shouldCompileTo('test: {{.}}', [null, {helper: 'awesome'}], 'test: ');
|
||||
shouldCompileTo('test: {{.}}', [null, { helper: 'awesome' }], 'test: ');
|
||||
});
|
||||
|
||||
it('complex but empty paths', function() {
|
||||
shouldCompileTo('{{person/name}}', {person: {name: null}}, '');
|
||||
shouldCompileTo('{{person/name}}', {person: {}}, '');
|
||||
shouldCompileTo('{{person/name}}', { person: { name: null } }, '');
|
||||
shouldCompileTo('{{person/name}}', { person: {} }, '');
|
||||
});
|
||||
|
||||
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');
|
||||
var hash = { goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] };
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'goodbyeGoodbyeGOODBYE',
|
||||
'This keyword in paths evaluates to current context'
|
||||
);
|
||||
|
||||
string = '{{#hellos}}{{this/text}}{{/hellos}}';
|
||||
hash = {hellos: [{text: 'hello'}, {text: 'Hello'}, {text: 'HELLO'}]};
|
||||
shouldCompileTo(string, hash, 'helloHelloHELLO', 'This keyword evaluates in more complex paths');
|
||||
hash = {
|
||||
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'helloHelloHELLO',
|
||||
'This keyword evaluates in more complex paths'
|
||||
);
|
||||
});
|
||||
|
||||
it('this keyword nested inside path', function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile('{{#hellos}}{{text/this/foo}}{{/hellos}}');
|
||||
}, Error, 'Invalid path: text/this - 1:13');
|
||||
shouldThrow(
|
||||
function() {
|
||||
CompilerContext.compile('{{#hellos}}{{text/this/foo}}{{/hellos}}');
|
||||
},
|
||||
Error,
|
||||
'Invalid path: text/this - 1:13'
|
||||
);
|
||||
|
||||
shouldCompileTo('{{[this]}}', {'this': 'bar'}, 'bar');
|
||||
shouldCompileTo('{{text/[this]}}', {text: {'this': 'bar'}}, 'bar');
|
||||
shouldCompileTo('{{[this]}}', { this: 'bar' }, 'bar');
|
||||
shouldCompileTo('{{text/[this]}}', { text: { this: 'bar' } }, 'bar');
|
||||
});
|
||||
|
||||
it('this keyword in helpers', function() {
|
||||
var helpers = {foo: function(value) {
|
||||
var helpers = {
|
||||
foo: function(value) {
|
||||
return 'bar ' + value;
|
||||
}};
|
||||
}
|
||||
};
|
||||
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');
|
||||
var hash = { goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] };
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, helpers],
|
||||
'bar goodbyebar Goodbyebar GOODBYE',
|
||||
'This keyword in paths evaluates to current context'
|
||||
);
|
||||
|
||||
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');
|
||||
hash = {
|
||||
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, helpers],
|
||||
'bar hellobar Hellobar HELLO',
|
||||
'This keyword evaluates in more complex paths'
|
||||
);
|
||||
});
|
||||
|
||||
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');
|
||||
shouldThrow(
|
||||
function() {
|
||||
CompilerContext.compile(string);
|
||||
},
|
||||
Error,
|
||||
'Invalid path: text/this - 1:17'
|
||||
);
|
||||
|
||||
shouldCompileTo(
|
||||
'{{foo [this]}}',
|
||||
{foo: function(value) { return value; }, 'this': 'bar'},
|
||||
'bar');
|
||||
'{{foo [this]}}',
|
||||
{
|
||||
foo: function(value) {
|
||||
return value;
|
||||
},
|
||||
this: 'bar'
|
||||
},
|
||||
'bar'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{foo text/[this]}}',
|
||||
{foo: function(value) { return value; }, text: {'this': 'bar'}},
|
||||
'bar');
|
||||
'{{foo text/[this]}}',
|
||||
{
|
||||
foo: function(value) {
|
||||
return value;
|
||||
},
|
||||
text: { this: 'bar' }
|
||||
},
|
||||
'bar'
|
||||
);
|
||||
});
|
||||
|
||||
it('pass string literals', function() {
|
||||
shouldCompileTo('{{"foo"}}', {}, '');
|
||||
shouldCompileTo('{{"foo"}}', { foo: 'bar' }, 'bar');
|
||||
shouldCompileTo('{{#"foo"}}{{.}}{{/"foo"}}', { foo: ['bar', 'baz'] }, 'barbaz');
|
||||
shouldCompileTo(
|
||||
'{{#"foo"}}{{.}}{{/"foo"}}',
|
||||
{ foo: ['bar', 'baz'] },
|
||||
'barbaz'
|
||||
);
|
||||
});
|
||||
|
||||
it('pass number literals', function() {
|
||||
@@ -285,13 +569,21 @@ describe('basic context', function() {
|
||||
shouldCompileTo('{{12}}', { '12': 'bar' }, 'bar');
|
||||
shouldCompileTo('{{12.34}}', {}, '');
|
||||
shouldCompileTo('{{12.34}}', { '12.34': 'bar' }, 'bar');
|
||||
shouldCompileTo('{{12.34 1}}', { '12.34': function(arg) { return 'bar' + arg; } }, 'bar1');
|
||||
shouldCompileTo(
|
||||
'{{12.34 1}}',
|
||||
{
|
||||
'12.34': function(arg) {
|
||||
return 'bar' + arg;
|
||||
}
|
||||
},
|
||||
'bar1'
|
||||
);
|
||||
});
|
||||
|
||||
it('pass boolean literals', function() {
|
||||
shouldCompileTo('{{true}}', {}, '');
|
||||
shouldCompileTo('{{true}}', { '': 'foo' }, '');
|
||||
shouldCompileTo('{{false}}', { 'false': 'foo' }, 'foo');
|
||||
shouldCompileTo('{{false}}', { false: 'foo' }, 'foo');
|
||||
});
|
||||
|
||||
it('should handle literals in subexpression', function() {
|
||||
@@ -300,6 +592,17 @@ describe('basic context', function() {
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
shouldCompileTo('{{foo (false)}}', [{ 'false': function() { return 'bar'; } }, helpers], 'bar');
|
||||
shouldCompileTo(
|
||||
'{{foo (false)}}',
|
||||
[
|
||||
{
|
||||
false: function() {
|
||||
return 'bar';
|
||||
}
|
||||
},
|
||||
helpers
|
||||
],
|
||||
'bar'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+255
-98
@@ -1,51 +1,99 @@
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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 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');
|
||||
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');
|
||||
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');
|
||||
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'}]};
|
||||
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');
|
||||
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'}]};
|
||||
var hash = {
|
||||
name: 'Alan',
|
||||
goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }]
|
||||
};
|
||||
|
||||
shouldCompileTo(string, hash, 'AlanAlanAlanAlanAlanAlan');
|
||||
});
|
||||
@@ -59,111 +107,201 @@ describe('blocks', function() {
|
||||
});
|
||||
|
||||
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' }] }] };
|
||||
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 template = CompilerContext.compile(
|
||||
'{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}',
|
||||
{ data: false }
|
||||
);
|
||||
|
||||
var result = template({person: [{first: 'Alan', last: 'Johnson'}, {first: 'Alan', last: 'Johnson'}]});
|
||||
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 string =
|
||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}';
|
||||
var hash = {};
|
||||
shouldCompileTo(string, hash, 'Right On!', "Inverted section rendered when value isn't set.");
|
||||
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.');
|
||||
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.');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
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');
|
||||
[{ 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');
|
||||
[{ 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');
|
||||
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.');
|
||||
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' }] }] };
|
||||
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!');
|
||||
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' }] }] };
|
||||
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!');
|
||||
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' }] }] };
|
||||
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 ');
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, undefined, undefined, true],
|
||||
'Goodbye cruel '
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -181,9 +319,10 @@ describe('blocks', function() {
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{*decorator}}{{/helper}}',
|
||||
{hash: {}, helpers: helpers, decorators: decorators},
|
||||
'success');
|
||||
'{{#helper}}{{*decorator}}{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should apply allow undefined return', function() {
|
||||
var helpers = {
|
||||
@@ -197,9 +336,10 @@ describe('blocks', function() {
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{*decorator}}suc{{/helper}}',
|
||||
{hash: {}, helpers: helpers, decorators: decorators},
|
||||
'success');
|
||||
'{{#helper}}{{*decorator}}suc{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
});
|
||||
|
||||
it('should apply block decorators', function() {
|
||||
@@ -215,9 +355,10 @@ describe('blocks', function() {
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}',
|
||||
{hash: {}, helpers: helpers, decorators: decorators},
|
||||
'success');
|
||||
'{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should support nested decorators', function() {
|
||||
var helpers = {
|
||||
@@ -235,9 +376,10 @@ describe('blocks', function() {
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}',
|
||||
{hash: {}, helpers: helpers, decorators: decorators},
|
||||
'success');
|
||||
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
});
|
||||
|
||||
it('should apply multiple decorators', function() {
|
||||
@@ -253,9 +395,10 @@ describe('blocks', function() {
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}',
|
||||
{hash: {}, helpers: helpers, decorators: decorators},
|
||||
'success');
|
||||
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
});
|
||||
|
||||
it('should access parent variables', function() {
|
||||
@@ -271,9 +414,10 @@ describe('blocks', function() {
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{*decorator foo}}{{/helper}}',
|
||||
{hash: {'foo': 'success'}, helpers: helpers, decorators: decorators},
|
||||
'success');
|
||||
'{{#helper}}{{*decorator foo}}{{/helper}}',
|
||||
{ hash: { foo: 'success' }, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should work with root program', function() {
|
||||
var run;
|
||||
@@ -285,9 +429,10 @@ describe('blocks', function() {
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{*decorator "success"}}',
|
||||
{hash: {'foo': 'success'}, decorators: decorators},
|
||||
'');
|
||||
'{{*decorator "success"}}',
|
||||
{ hash: { foo: 'success' }, decorators: decorators },
|
||||
''
|
||||
);
|
||||
equals(run, true);
|
||||
});
|
||||
it('should fail when accessing variables from root', function() {
|
||||
@@ -300,9 +445,10 @@ describe('blocks', function() {
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{*decorator foo}}',
|
||||
{hash: {'foo': 'fail'}, decorators: decorators},
|
||||
'');
|
||||
'{{*decorator foo}}',
|
||||
{ hash: { foo: 'fail' }, decorators: decorators },
|
||||
''
|
||||
);
|
||||
equals(run, true);
|
||||
});
|
||||
|
||||
@@ -335,12 +481,23 @@ describe('blocks', function() {
|
||||
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');
|
||||
shouldThrow(
|
||||
function() {
|
||||
handlebarsEnv.registerDecorator(
|
||||
{
|
||||
world: function() {
|
||||
return 'world!';
|
||||
},
|
||||
testHelper: function() {
|
||||
return 'found it!';
|
||||
}
|
||||
},
|
||||
{}
|
||||
);
|
||||
},
|
||||
Error,
|
||||
'Arg not supported with multiple decorators'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+372
-109
@@ -2,64 +2,156 @@ 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');
|
||||
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');
|
||||
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'
|
||||
);
|
||||
});
|
||||
|
||||
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!');
|
||||
var string =
|
||||
'{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}';
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ foo: { goodbye: true }, world: 'world' },
|
||||
'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');
|
||||
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');
|
||||
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}}';
|
||||
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');
|
||||
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 template = CompilerContext.compile(
|
||||
'{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}',
|
||||
{ data: false }
|
||||
);
|
||||
|
||||
var result = template({person: {first: 'Alan', last: 'Johnson'}});
|
||||
var result = template({ person: { first: 'Alan', last: 'Johnson' } });
|
||||
equals(result, 'Alan Johnson');
|
||||
});
|
||||
});
|
||||
@@ -73,92 +165,179 @@ 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'};
|
||||
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');
|
||||
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!');
|
||||
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');
|
||||
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 !');
|
||||
shouldCompileTo(string, [, , , ,], 'cruel !');
|
||||
});
|
||||
|
||||
it('each with an object and @key', function() {
|
||||
var string = '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!';
|
||||
var string =
|
||||
'{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!';
|
||||
|
||||
function Clazz() {
|
||||
this['<b>#1</b>'] = {text: 'goodbye'};
|
||||
this[2] = {text: 'GOODBYE'};
|
||||
this['<b>#1</b>'] = { text: 'goodbye' };
|
||||
this[2] = { text: 'GOODBYE' };
|
||||
}
|
||||
Clazz.prototype.foo = 'fail';
|
||||
var hash = {goodbyes: new Clazz(), world: 'world'};
|
||||
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!';
|
||||
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!');
|
||||
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 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');
|
||||
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 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');
|
||||
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 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!');
|
||||
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 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');
|
||||
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 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);
|
||||
@@ -167,18 +346,34 @@ describe('builtin helpers', function() {
|
||||
});
|
||||
|
||||
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 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');
|
||||
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 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);
|
||||
@@ -187,8 +382,16 @@ describe('builtin helpers', function() {
|
||||
});
|
||||
|
||||
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 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);
|
||||
@@ -197,8 +400,12 @@ describe('builtin helpers', function() {
|
||||
});
|
||||
|
||||
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 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);
|
||||
@@ -207,37 +414,78 @@ describe('builtin helpers', function() {
|
||||
});
|
||||
|
||||
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 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');
|
||||
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');
|
||||
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('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'};
|
||||
var string =
|
||||
'{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!';
|
||||
var hash = {
|
||||
goodbyes: {
|
||||
a: { text: 'goodbye' },
|
||||
b: { 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!', 'Empty string key is not skipped');
|
||||
equal(
|
||||
result,
|
||||
'0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!',
|
||||
'Empty string key is not skipped'
|
||||
);
|
||||
});
|
||||
|
||||
it('data passed to helpers', function() {
|
||||
var string = '{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}';
|
||||
var hash = {letters: ['a', 'b', 'c']};
|
||||
var hash = { letters: ['a', 'b', 'c'] };
|
||||
|
||||
var template = CompilerContext.compile(string);
|
||||
var result = template(hash, {
|
||||
@@ -249,10 +497,16 @@ describe('builtin helpers', function() {
|
||||
});
|
||||
|
||||
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');
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#each}}{{text}}! {{/each}}cruel world!'
|
||||
);
|
||||
template({});
|
||||
},
|
||||
handlebarsEnv.Exception,
|
||||
'Must pass iterator to #each'
|
||||
);
|
||||
});
|
||||
|
||||
if (global.Symbol && global.Symbol.iterator) {
|
||||
@@ -276,13 +530,25 @@ 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 goodbyes = new Iterable([
|
||||
{ text: 'goodbye' },
|
||||
{ text: 'Goodbye' },
|
||||
{ text: 'GOODBYE' }
|
||||
]);
|
||||
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');
|
||||
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'
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -293,9 +559,7 @@ describe('builtin helpers', function() {
|
||||
return;
|
||||
}
|
||||
|
||||
var $log,
|
||||
$info,
|
||||
$error;
|
||||
var $log, $info, $error;
|
||||
beforeEach(function() {
|
||||
$log = console.log;
|
||||
$info = console.info;
|
||||
@@ -331,7 +595,7 @@ describe('builtin helpers', function() {
|
||||
logArg = arg;
|
||||
};
|
||||
|
||||
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
||||
shouldCompileTo(string, [hash, , , , { level: '03' }], '');
|
||||
equals('03', levelArg);
|
||||
equals('whee', logArg);
|
||||
});
|
||||
@@ -367,13 +631,13 @@ describe('builtin helpers', function() {
|
||||
console.error = $error;
|
||||
};
|
||||
|
||||
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
||||
shouldCompileTo(string, [hash, , , , { level: '03' }], '');
|
||||
equals(true, called);
|
||||
});
|
||||
it('should handle missing logger', function() {
|
||||
var string = '{{log blah}}';
|
||||
var hash = { blah: 'whee' },
|
||||
called = false;
|
||||
called = false;
|
||||
|
||||
console.error = undefined;
|
||||
console.log = function(log) {
|
||||
@@ -382,7 +646,7 @@ describe('builtin helpers', function() {
|
||||
console.log = $log;
|
||||
};
|
||||
|
||||
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
|
||||
shouldCompileTo(string, [hash, , , , { level: '03' }], '');
|
||||
equals(true, called);
|
||||
});
|
||||
|
||||
@@ -396,12 +660,12 @@ describe('builtin helpers', function() {
|
||||
called = true;
|
||||
};
|
||||
|
||||
shouldCompileTo(string, [hash,,,, {level: 'error'}], '');
|
||||
shouldCompileTo(string, [hash, , , , { level: 'error' }], '');
|
||||
equals(true, called);
|
||||
|
||||
called = false;
|
||||
|
||||
shouldCompileTo(string, [hash,,,, {level: 'ERROR'}], '');
|
||||
shouldCompileTo(string, [hash, , , , { level: 'ERROR' }], '');
|
||||
equals(true, called);
|
||||
});
|
||||
it('should handle hash log levels', function() {
|
||||
@@ -449,11 +713,10 @@ describe('builtin helpers', function() {
|
||||
/* 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']};
|
||||
hash = { goodbyes: [0, 1], data: ['foo', 'bar'] };
|
||||
|
||||
var template = CompilerContext.compile(string);
|
||||
var result = template(hash);
|
||||
@@ -462,7 +725,7 @@ describe('builtin helpers', function() {
|
||||
});
|
||||
it('should not fail on undefined value', function() {
|
||||
var string = '{{#each goodbyes}}{{lookup ../bar .}}{{/each}}',
|
||||
hash = {goodbyes: [0, 1], data: ['foo', 'bar']};
|
||||
hash = { goodbyes: [0, 1], data: ['foo', 'bar'] };
|
||||
|
||||
var template = CompilerContext.compile(string);
|
||||
var result = template(hash);
|
||||
|
||||
+112
-35
@@ -13,37 +13,84 @@ describe('compiler', function() {
|
||||
equal(compile('foo').equals(compile('foo')), true);
|
||||
equal(compile('{{foo}}').equals(compile('{{foo}}')), true);
|
||||
equal(compile('{{foo.bar}}').equals(compile('{{foo.bar}}')), true);
|
||||
equal(compile('{{foo.bar baz "foo" true false bat=1}}').equals(compile('{{foo.bar baz "foo" true false bat=1}}')), true);
|
||||
equal(compile('{{foo.bar (baz bat=1)}}').equals(compile('{{foo.bar (baz bat=1)}}')), true);
|
||||
equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{/foo}}')), true);
|
||||
equal(
|
||||
compile('{{foo.bar baz "foo" true false bat=1}}').equals(
|
||||
compile('{{foo.bar baz "foo" true false bat=1}}')
|
||||
),
|
||||
true
|
||||
);
|
||||
equal(
|
||||
compile('{{foo.bar (baz bat=1)}}').equals(
|
||||
compile('{{foo.bar (baz bat=1)}}')
|
||||
),
|
||||
true
|
||||
);
|
||||
equal(
|
||||
compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{/foo}}')),
|
||||
true
|
||||
);
|
||||
});
|
||||
it('should treat as not equal', function() {
|
||||
equal(compile('foo').equals(compile('bar')), false);
|
||||
equal(compile('{{foo}}').equals(compile('{{bar}}')), false);
|
||||
equal(compile('{{foo.bar}}').equals(compile('{{bar.bar}}')), false);
|
||||
equal(compile('{{foo.bar baz bat=1}}').equals(compile('{{foo.bar bar bat=1}}')), false);
|
||||
equal(compile('{{foo.bar (baz bat=1)}}').equals(compile('{{foo.bar (bar bat=1)}}')), false);
|
||||
equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#bar}} {{/bar}}')), false);
|
||||
equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{foo}}{{/foo}}')), false);
|
||||
equal(
|
||||
compile('{{foo.bar baz bat=1}}').equals(
|
||||
compile('{{foo.bar bar bat=1}}')
|
||||
),
|
||||
false
|
||||
);
|
||||
equal(
|
||||
compile('{{foo.bar (baz bat=1)}}').equals(
|
||||
compile('{{foo.bar (bar bat=1)}}')
|
||||
),
|
||||
false
|
||||
);
|
||||
equal(
|
||||
compile('{{#foo}} {{/foo}}').equals(compile('{{#bar}} {{/bar}}')),
|
||||
false
|
||||
);
|
||||
equal(
|
||||
compile('{{#foo}} {{/foo}}').equals(
|
||||
compile('{{#foo}} {{foo}}{{/foo}}')
|
||||
),
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#compile', function() {
|
||||
it('should fail with invalid input', function() {
|
||||
shouldThrow(function() {
|
||||
Handlebars.compile(null);
|
||||
}, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null');
|
||||
shouldThrow(function() {
|
||||
Handlebars.compile({});
|
||||
}, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]');
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.compile(null);
|
||||
},
|
||||
Error,
|
||||
'You must pass a string or Handlebars AST to Handlebars.compile. You passed null'
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.compile({});
|
||||
},
|
||||
Error,
|
||||
'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]'
|
||||
);
|
||||
});
|
||||
|
||||
it('should include the location in the error (row and column)', function() {
|
||||
try {
|
||||
Handlebars.compile(' \n {{#if}}\n{{/def}}')();
|
||||
equal(true, false, 'Statement must throw exception. This line should not be executed.');
|
||||
equal(
|
||||
true,
|
||||
false,
|
||||
'Statement must throw exception. This line should not be executed.'
|
||||
);
|
||||
} catch (err) {
|
||||
equal(err.message, 'if doesn\'t match def - 2:5', 'Checking error message');
|
||||
equal(
|
||||
err.message,
|
||||
"if doesn't match def - 2:5",
|
||||
'Checking error message'
|
||||
);
|
||||
if (Object.getOwnPropertyDescriptor(err, 'column').writable) {
|
||||
// In Safari 8, the column-property is read-only. This means that even if it is set with defineProperty,
|
||||
// its value won't change (https://github.com/jquery/esprima/issues/1290#issuecomment-132455482)
|
||||
@@ -57,17 +104,28 @@ describe('compiler', function() {
|
||||
it('should include the location as enumerable property', function() {
|
||||
try {
|
||||
Handlebars.compile(' \n {{#if}}\n{{/def}}')();
|
||||
equal(true, false, 'Statement must throw exception. This line should not be executed.');
|
||||
equal(
|
||||
true,
|
||||
false,
|
||||
'Statement must throw exception. This line should not be executed.'
|
||||
);
|
||||
} catch (err) {
|
||||
equal(Object.prototype.propertyIsEnumerable.call(err, 'column'), true, 'Checking error column');
|
||||
equal(
|
||||
Object.prototype.propertyIsEnumerable.call(err, 'column'),
|
||||
true,
|
||||
'Checking error column'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('can utilize AST instance', function() {
|
||||
equal(Handlebars.compile({
|
||||
type: 'Program',
|
||||
body: [ {type: 'ContentStatement', value: 'Hello'}]
|
||||
})(), 'Hello');
|
||||
equal(
|
||||
Handlebars.compile({
|
||||
type: 'Program',
|
||||
body: [{ type: 'ContentStatement', value: 'Hello' }]
|
||||
})(),
|
||||
'Hello'
|
||||
);
|
||||
});
|
||||
|
||||
it('can pass through an empty string', function() {
|
||||
@@ -75,33 +133,52 @@ describe('compiler', function() {
|
||||
});
|
||||
|
||||
it('should not modify the options.data property(GH-1327)', function() {
|
||||
var options = {data: [{a: 'foo'}, {a: 'bar'}]};
|
||||
var options = { data: [{ a: 'foo' }, { a: 'bar' }] };
|
||||
Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
|
||||
equal(JSON.stringify(options, 0, 2), JSON.stringify({data: [{a: 'foo'}, {a: 'bar'}]}, 0, 2));
|
||||
equal(
|
||||
JSON.stringify(options, 0, 2),
|
||||
JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, 0, 2)
|
||||
);
|
||||
});
|
||||
|
||||
it('should not modify the options.knownHelpers property(GH-1327)', function() {
|
||||
var options = {knownHelpers: {}};
|
||||
var options = { knownHelpers: {} };
|
||||
Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
|
||||
equal(JSON.stringify(options, 0, 2), JSON.stringify({knownHelpers: {}}, 0, 2));
|
||||
equal(
|
||||
JSON.stringify(options, 0, 2),
|
||||
JSON.stringify({ knownHelpers: {} }, 0, 2)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#precompile', function() {
|
||||
it('should fail with invalid input', function() {
|
||||
shouldThrow(function() {
|
||||
Handlebars.precompile(null);
|
||||
}, Error, 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null');
|
||||
shouldThrow(function() {
|
||||
Handlebars.precompile({});
|
||||
}, Error, 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed [object Object]');
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.precompile(null);
|
||||
},
|
||||
Error,
|
||||
'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null'
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.precompile({});
|
||||
},
|
||||
Error,
|
||||
'You must pass a string or Handlebars AST to Handlebars.precompile. You passed [object Object]'
|
||||
);
|
||||
});
|
||||
|
||||
it('can utilize AST instance', function() {
|
||||
equal(/return "Hello"/.test(Handlebars.precompile({
|
||||
type: 'Program',
|
||||
body: [ {type: 'ContentStatement', value: 'Hello'}]
|
||||
})), true);
|
||||
equal(
|
||||
/return "Hello"/.test(
|
||||
Handlebars.precompile({
|
||||
type: 'Program',
|
||||
body: [{ type: 'ContentStatement', value: 'Hello' }]
|
||||
})
|
||||
),
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('can pass through an empty string', function() {
|
||||
|
||||
+141
-48
@@ -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 template = CompilerContext.compile('{{hello}}', { data: true });
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
@@ -8,7 +8,10 @@ describe('data', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({noun: 'cat'}, {helpers: helpers, data: {adjective: 'happy'}});
|
||||
var result = template(
|
||||
{ noun: 'cat' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy cat', result, 'Data output by helper');
|
||||
});
|
||||
|
||||
@@ -19,7 +22,9 @@ describe('data', function() {
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile(
|
||||
'{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}'
|
||||
);
|
||||
|
||||
var helpers = Handlebars.createFrame(handlebarsEnv.helpers);
|
||||
|
||||
@@ -47,7 +52,11 @@ describe('data', function() {
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers, data: { world: 'world' } });
|
||||
equals('Hello world', result, '@foo as a parameter retrieves template data');
|
||||
equals(
|
||||
'Hello world',
|
||||
result,
|
||||
'@foo as a parameter retrieves template data'
|
||||
);
|
||||
});
|
||||
|
||||
it('hash values can be looked up via @foo', function() {
|
||||
@@ -59,7 +68,11 @@ describe('data', function() {
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers, data: { world: 'world' } });
|
||||
equals('Hello world', result, '@foo as a parameter retrieves template data');
|
||||
equals(
|
||||
'Hello world',
|
||||
result,
|
||||
'@foo as a parameter retrieves template data'
|
||||
);
|
||||
});
|
||||
|
||||
it('nested parameter data can be looked up via @foo.bar', function() {
|
||||
@@ -70,8 +83,15 @@ describe('data', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers, data: { world: {bar: 'world' } } });
|
||||
equals('Hello world', result, '@foo as a parameter retrieves template data');
|
||||
var result = template(
|
||||
{},
|
||||
{ helpers: helpers, data: { world: { bar: 'world' } } }
|
||||
);
|
||||
equals(
|
||||
'Hello world',
|
||||
result,
|
||||
'@foo as a parameter retrieves template data'
|
||||
);
|
||||
});
|
||||
|
||||
it('nested parameter data does not fail with @world.bar', function() {
|
||||
@@ -82,8 +102,15 @@ describe('data', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers, data: { foo: {bar: 'world' } } });
|
||||
equals('Hello undefined', result, '@foo as a parameter retrieves template data');
|
||||
var result = template(
|
||||
{},
|
||||
{ helpers: helpers, data: { foo: { bar: 'world' } } }
|
||||
);
|
||||
equals(
|
||||
'Hello undefined',
|
||||
result,
|
||||
'@foo as a parameter retrieves template data'
|
||||
);
|
||||
});
|
||||
|
||||
it('parameter data throws when using complex scope references', function() {
|
||||
@@ -96,17 +123,38 @@ describe('data', function() {
|
||||
|
||||
it('data can be functions', function() {
|
||||
var template = CompilerContext.compile('{{@hello}}');
|
||||
var result = template({}, { data: { hello: function() { return 'hello'; } } });
|
||||
var result = template(
|
||||
{},
|
||||
{
|
||||
data: {
|
||||
hello: function() {
|
||||
return 'hello';
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
equals('hello', result);
|
||||
});
|
||||
it('data can be functions with params', function() {
|
||||
var template = CompilerContext.compile('{{@hello "hello"}}');
|
||||
var result = template({}, { data: { hello: function(arg) { return arg; } } });
|
||||
var result = template(
|
||||
{},
|
||||
{
|
||||
data: {
|
||||
hello: function(arg) {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
equals('hello', result);
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile(
|
||||
'{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}',
|
||||
{ data: true }
|
||||
);
|
||||
var helpers = {
|
||||
let: function(options) {
|
||||
var frame = Handlebars.createFrame(options.data);
|
||||
@@ -115,19 +163,22 @@ describe('data', function() {
|
||||
frame[prop] = options.hash[prop];
|
||||
}
|
||||
}
|
||||
return options.fn(this, {data: frame});
|
||||
return options.fn(this, { data: frame });
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({ bar: { baz: 'hello world' } }, { helpers: helpers, data: {} });
|
||||
var result = template(
|
||||
{ bar: { baz: 'hello world' } },
|
||||
{ helpers: helpers, data: {} }
|
||||
);
|
||||
equals('2hello world1', result, 'data variables are inherited downstream');
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile('{{>myPartial}}', { data: true });
|
||||
|
||||
var partials = {
|
||||
myPartial: CompilerContext.compile('{{hello}}', {data: true})
|
||||
myPartial: CompilerContext.compile('{{hello}}', { data: true })
|
||||
};
|
||||
|
||||
var helpers = {
|
||||
@@ -136,12 +187,15 @@ describe('data', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({noun: 'cat'}, {helpers: helpers, partials: partials, data: {adjective: 'happy'}});
|
||||
var result = template(
|
||||
{ noun: 'cat' },
|
||||
{ helpers: helpers, partials: partials, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy cat', result, 'Data output by helper inside partial');
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile('{{hello world}}', { data: true });
|
||||
|
||||
var helpers = {
|
||||
hello: function(noun, options) {
|
||||
@@ -149,12 +203,17 @@ describe('data', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({exclaim: true, world: 'world'}, {helpers: helpers, data: {adjective: 'happy'}});
|
||||
var result = template(
|
||||
{ exclaim: true, world: 'world' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy world!', result, 'Data output by helper');
|
||||
});
|
||||
|
||||
it('passing in data to a compiled function that expects data - works with block helpers', function() {
|
||||
var template = CompilerContext.compile('{{#hello}}{{world}}{{/hello}}', {data: true});
|
||||
var template = CompilerContext.compile('{{#hello}}{{world}}{{/hello}}', {
|
||||
data: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
@@ -165,106 +224,140 @@ describe('data', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({exclaim: true}, {helpers: helpers, data: {adjective: 'happy'}});
|
||||
var result = template(
|
||||
{ exclaim: true },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy world!', result, 'Data output by helper');
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile(
|
||||
'{{#hello}}{{world ../zomg}}{{/hello}}',
|
||||
{ data: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
return options.fn({exclaim: '?'});
|
||||
return options.fn({ exclaim: '?' });
|
||||
},
|
||||
world: function(thing, options) {
|
||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({exclaim: true, zomg: 'world'}, {helpers: helpers, data: {adjective: 'happy'}});
|
||||
var result = template(
|
||||
{ exclaim: true, zomg: 'world' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy world?', result, 'Data output by helper');
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile(
|
||||
'{{#hello}}{{world ../zomg}}{{/hello}}',
|
||||
{ data: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
return options.data.accessData + ' ' + options.fn({exclaim: '?'});
|
||||
return options.data.accessData + ' ' + options.fn({ exclaim: '?' });
|
||||
},
|
||||
world: function(thing, options) {
|
||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({exclaim: true, zomg: 'world'}, {helpers: helpers, data: {adjective: 'happy', accessData: '#win'}});
|
||||
var result = template(
|
||||
{ exclaim: true, zomg: 'world' },
|
||||
{ helpers: helpers, data: { adjective: 'happy', accessData: '#win' } }
|
||||
);
|
||||
equals('#win happy world?', result, 'Data output by helper');
|
||||
});
|
||||
|
||||
it('you can override inherited data when invoking a helper', function() {
|
||||
var template = CompilerContext.compile('{{#hello}}{{world zomg}}{{/hello}}', {data: true});
|
||||
var template = CompilerContext.compile(
|
||||
'{{#hello}}{{world zomg}}{{/hello}}',
|
||||
{ data: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
return options.fn({exclaim: '?', zomg: 'world'}, { data: {adjective: 'sad'} });
|
||||
return options.fn(
|
||||
{ exclaim: '?', zomg: 'world' },
|
||||
{ data: { adjective: 'sad' } }
|
||||
);
|
||||
},
|
||||
world: function(thing, options) {
|
||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({exclaim: true, zomg: 'planet'}, {helpers: helpers, data: {adjective: 'happy'}});
|
||||
var result = template(
|
||||
{ exclaim: true, zomg: 'planet' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('sad world?', result, 'Overriden data output by helper');
|
||||
});
|
||||
|
||||
|
||||
it('you can override inherited data when invoking a helper with depth', function() {
|
||||
var template = CompilerContext.compile('{{#hello}}{{world ../zomg}}{{/hello}}', {data: true});
|
||||
var template = CompilerContext.compile(
|
||||
'{{#hello}}{{world ../zomg}}{{/hello}}',
|
||||
{ data: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
return options.fn({exclaim: '?'}, { data: {adjective: 'sad'} });
|
||||
return options.fn({ exclaim: '?' }, { data: { adjective: 'sad' } });
|
||||
},
|
||||
world: function(thing, options) {
|
||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({exclaim: true, zomg: 'world'}, {helpers: helpers, data: {adjective: 'happy'}});
|
||||
var result = template(
|
||||
{ exclaim: true, zomg: 'world' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('sad world?', result, 'Overriden data output by helper');
|
||||
});
|
||||
|
||||
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: {} });
|
||||
var result = template({ foo: 'hello' }, { data: {} });
|
||||
equals('hello', result);
|
||||
|
||||
result = template({foo: 'hello'}, {});
|
||||
result = template({ foo: 'hello' }, {});
|
||||
equals('hello', result);
|
||||
});
|
||||
it('passed root values take priority', function() {
|
||||
var template = CompilerContext.compile('{{@root.foo}}');
|
||||
var result = template({}, { data: {root: {foo: 'hello'} } });
|
||||
var result = template({}, { data: { root: { foo: 'hello' } } });
|
||||
equals('hello', result);
|
||||
});
|
||||
});
|
||||
|
||||
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});
|
||||
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
|
||||
}
|
||||
},
|
||||
data: {
|
||||
depth: 0
|
||||
}
|
||||
});
|
||||
);
|
||||
equals('2 1 0', result);
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
+5
-3
@@ -1,12 +1,11 @@
|
||||
require('./common');
|
||||
|
||||
var fs = require('fs'),
|
||||
vm = require('vm');
|
||||
vm = require('vm');
|
||||
|
||||
var chai = require('chai');
|
||||
var dirtyChai = require('dirty-chai');
|
||||
|
||||
|
||||
chai.use(dirtyChai);
|
||||
global.expect = chai.expect;
|
||||
|
||||
@@ -18,7 +17,10 @@ var filename = 'dist/handlebars.js';
|
||||
if (global.minimizedTest) {
|
||||
filename = 'dist/handlebars.min.js';
|
||||
}
|
||||
var distHandlebars = fs.readFileSync(require.resolve('../../' + filename), 'utf-8');
|
||||
var distHandlebars = fs.readFileSync(
|
||||
require.resolve('../../' + filename),
|
||||
'utf-8'
|
||||
);
|
||||
vm.runInThisContext(distHandlebars, filename);
|
||||
|
||||
global.CompilerContext = {
|
||||
|
||||
Vendored
+48
-18
@@ -1,6 +1,6 @@
|
||||
var global = (function() {
|
||||
return this;
|
||||
}());
|
||||
})();
|
||||
|
||||
var AssertError;
|
||||
if (Error.captureStackTrace) {
|
||||
@@ -28,10 +28,19 @@ global.shouldCompileTo = function(string, hashOrArray, expected, message) {
|
||||
/**
|
||||
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
||||
*/
|
||||
global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string, hashOrArray, partials, expected, message) {
|
||||
global.shouldCompileToWithPartials = function shouldCompileToWithPartials(
|
||||
string,
|
||||
hashOrArray,
|
||||
partials,
|
||||
expected,
|
||||
message
|
||||
) {
|
||||
var result = compileWithPartials(string, hashOrArray, partials);
|
||||
if (result !== expected) {
|
||||
throw new AssertError("'" + result + "' should === '" + expected + "': " + message, shouldCompileToWithPartials);
|
||||
throw new AssertError(
|
||||
"'" + result + "' should === '" + expected + "': " + message,
|
||||
shouldCompileToWithPartials
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -39,17 +48,18 @@ global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string
|
||||
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
||||
*/
|
||||
global.compileWithPartials = function(string, hashOrArray, partials) {
|
||||
var template,
|
||||
ary,
|
||||
options;
|
||||
var template, ary, options;
|
||||
if (hashOrArray && hashOrArray.hash) {
|
||||
ary = [hashOrArray.hash, hashOrArray];
|
||||
delete hashOrArray.hash;
|
||||
} else if (Object.prototype.toString.call(hashOrArray) === '[object Array]') {
|
||||
ary = [];
|
||||
ary.push(hashOrArray[0]); // input
|
||||
ary.push({helpers: hashOrArray[1], partials: hashOrArray[2]});
|
||||
options = typeof hashOrArray[3] === 'object' ? hashOrArray[3] : {compat: hashOrArray[3]};
|
||||
ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] });
|
||||
options =
|
||||
typeof hashOrArray[3] === 'object'
|
||||
? hashOrArray[3]
|
||||
: { compat: hashOrArray[3] };
|
||||
if (hashOrArray[4] != null) {
|
||||
options.data = !!hashOrArray[4];
|
||||
ary[1].data = hashOrArray[4];
|
||||
@@ -58,18 +68,23 @@ global.compileWithPartials = function(string, hashOrArray, partials) {
|
||||
ary = [hashOrArray];
|
||||
}
|
||||
|
||||
template = CompilerContext[partials ? 'compileWithPartial' : 'compile'](string, options);
|
||||
template = CompilerContext[partials ? 'compileWithPartial' : 'compile'](
|
||||
string,
|
||||
options
|
||||
);
|
||||
return template.apply(this, ary);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Use chai's expect-style API instead (`expect(actualValue).to.equal(expectedValue)`)
|
||||
* @see https://www.chaijs.com/api/bdd/
|
||||
*/
|
||||
global.equals = global.equal = function equals(a, b, msg) {
|
||||
if (a !== b) {
|
||||
throw new AssertError("'" + a + "' should === '" + b + "'" + (msg ? ': ' + msg : ''), equals);
|
||||
throw new AssertError(
|
||||
"'" + a + "' should === '" + b + "'" + (msg ? ': ' + msg : ''),
|
||||
equals
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -86,8 +101,19 @@ global.shouldThrow = function(callback, type, msg) {
|
||||
if (type && !(caught instanceof type)) {
|
||||
throw new AssertError('Type failure: ' + caught);
|
||||
}
|
||||
if (msg && !(msg.test ? msg.test(caught.message) : msg === caught.message)) {
|
||||
throw new AssertError('Throw mismatch: Expected ' + caught.message + ' to match ' + msg + '\n\n' + caught.stack, shouldThrow);
|
||||
if (
|
||||
msg &&
|
||||
!(msg.test ? msg.test(caught.message) : msg === caught.message)
|
||||
) {
|
||||
throw new AssertError(
|
||||
'Throw mismatch: Expected ' +
|
||||
caught.message +
|
||||
' to match ' +
|
||||
msg +
|
||||
'\n\n' +
|
||||
caught.stack,
|
||||
shouldThrow
|
||||
);
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
@@ -95,18 +121,17 @@ global.shouldThrow = function(callback, type, msg) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
global.expectTemplate = function(templateAsString) {
|
||||
return new HandlebarsTestBench(templateAsString);
|
||||
};
|
||||
|
||||
|
||||
function HandlebarsTestBench(templateAsString) {
|
||||
this.templateAsString = templateAsString;
|
||||
this.helpers = {};
|
||||
this.partials = {};
|
||||
this.input = {};
|
||||
this.message = 'Template' + templateAsString + ' does not evaluate to expected output';
|
||||
this.message =
|
||||
'Template' + templateAsString + ' does not evaluate to expected output';
|
||||
this.compileOptions = {};
|
||||
this.runtimeOptions = {};
|
||||
}
|
||||
@@ -146,7 +171,11 @@ HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) {
|
||||
};
|
||||
|
||||
// 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,
|
||||
msg
|
||||
) {
|
||||
var self = this;
|
||||
expect(function() {
|
||||
self._compileAndExeute();
|
||||
@@ -154,7 +183,8 @@ HandlebarsTestBench.prototype.toThrow = function(errorLike, errMsgMatcher, msg)
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype._compileAndExeute = function() {
|
||||
var compile = Object.keys(this.partials).length > 0
|
||||
var compile =
|
||||
Object.keys(this.partials).length > 0
|
||||
? CompilerContext.compileWithPartial
|
||||
: CompilerContext.compile;
|
||||
|
||||
|
||||
Vendored
-1
@@ -3,7 +3,6 @@ require('./common');
|
||||
var chai = require('chai');
|
||||
var dirtyChai = require('dirty-chai');
|
||||
|
||||
|
||||
chai.use(dirtyChai);
|
||||
global.expect = chai.expect;
|
||||
|
||||
|
||||
Vendored
+12
-8
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable no-console */
|
||||
var fs = require('fs'),
|
||||
Mocha = require('mocha'),
|
||||
path = require('path');
|
||||
Mocha = require('mocha'),
|
||||
path = require('path');
|
||||
|
||||
var errors = 0,
|
||||
testDir = path.dirname(__dirname),
|
||||
grep = process.argv[2];
|
||||
testDir = path.dirname(__dirname),
|
||||
grep = process.argv[2];
|
||||
|
||||
// Lazy hack, but whatever
|
||||
if (grep === '--min') {
|
||||
@@ -13,9 +13,14 @@ if (grep === '--min') {
|
||||
grep = undefined;
|
||||
}
|
||||
|
||||
var files = fs.readdirSync(testDir)
|
||||
.filter(function(name) { return (/.*\.js$/).test(name); })
|
||||
.map(function(name) { return testDir + path.sep + name; });
|
||||
var files = fs
|
||||
.readdirSync(testDir)
|
||||
.filter(function(name) {
|
||||
return /.*\.js$/.test(name);
|
||||
})
|
||||
.map(function(name) {
|
||||
return testDir + path.sep + name;
|
||||
});
|
||||
|
||||
if (global.minimizedTest) {
|
||||
run('./runtime', function() {
|
||||
@@ -37,7 +42,6 @@ if (global.minimizedTest) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function run(env, callback) {
|
||||
var mocha = new Mocha();
|
||||
mocha.ui('bdd');
|
||||
|
||||
Vendored
+10
-4
@@ -1,12 +1,11 @@
|
||||
require('./common');
|
||||
|
||||
var fs = require('fs'),
|
||||
vm = require('vm');
|
||||
vm = require('vm');
|
||||
|
||||
var chai = require('chai');
|
||||
var dirtyChai = require('dirty-chai');
|
||||
|
||||
|
||||
chai.use(dirtyChai);
|
||||
global.expect = chai.expect;
|
||||
|
||||
@@ -18,7 +17,10 @@ var filename = 'dist/handlebars.runtime.js';
|
||||
if (global.minimizedTest) {
|
||||
filename = 'dist/handlebars.runtime.min.js';
|
||||
}
|
||||
vm.runInThisContext(fs.readFileSync(__dirname + '/../../' + filename), filename);
|
||||
vm.runInThisContext(
|
||||
fs.readFileSync(__dirname + '/../../' + filename),
|
||||
filename
|
||||
);
|
||||
|
||||
var parse = require('../../dist/cjs/handlebars/compiler/base').parse;
|
||||
var compiler = require('../../dist/cjs/handlebars/compiler/compiler');
|
||||
@@ -30,7 +32,11 @@ global.CompilerContext = {
|
||||
compile: function(template, options) {
|
||||
// Hack the compiler on to the environment for these specific tests
|
||||
handlebarsEnv.precompile = function(precompileTemplate, precompileOptions) {
|
||||
return compiler.precompile(precompileTemplate, precompileOptions, handlebarsEnv);
|
||||
return compiler.precompile(
|
||||
precompileTemplate,
|
||||
precompileOptions,
|
||||
handlebarsEnv
|
||||
);
|
||||
};
|
||||
handlebarsEnv.parse = parse;
|
||||
handlebarsEnv.Compiler = compiler.Compiler;
|
||||
|
||||
+735
-239
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,10 @@ describe('javascript-compiler api', function() {
|
||||
});
|
||||
|
||||
it('should allow override', function() {
|
||||
handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = function(parent, name) {
|
||||
handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = function(
|
||||
parent,
|
||||
name
|
||||
) {
|
||||
return parent + '.bar_' + name;
|
||||
};
|
||||
/* eslint-disable camelcase */
|
||||
@@ -43,7 +46,7 @@ describe('javascript-compiler api', function() {
|
||||
};
|
||||
handlebarsEnv.VM.checkRevision = function(compilerInfo) {
|
||||
if (compilerInfo !== 'crazy') {
|
||||
throw new Error('It didn\'t work');
|
||||
throw new Error("It didn't work");
|
||||
}
|
||||
};
|
||||
shouldCompileTo('{{foo}} ', { foo: 'food' }, 'food ');
|
||||
@@ -54,7 +57,8 @@ describe('javascript-compiler api', function() {
|
||||
beforeEach(function() {
|
||||
handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = true;
|
||||
$superAppend = handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer;
|
||||
$superCreate = handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer;
|
||||
$superCreate =
|
||||
handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer;
|
||||
});
|
||||
afterEach(function() {
|
||||
handlebarsEnv.JavaScriptCompiler.prototype.forceBuffer = false;
|
||||
@@ -69,7 +73,9 @@ describe('javascript-compiler api', function() {
|
||||
shouldCompileTo('{{foo}} ', { foo: 'food' }, 'foo_food ');
|
||||
});
|
||||
it('should allow append buffer override', function() {
|
||||
handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function(string) {
|
||||
handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function(
|
||||
string
|
||||
) {
|
||||
return $superAppend.call(this, [string, ' + "_foo"']);
|
||||
};
|
||||
shouldCompileTo('{{foo}}', { foo: 'food' }, 'food_foo');
|
||||
|
||||
+257
-95
@@ -68,7 +68,10 @@ describe('parser', function() {
|
||||
equals(astFor('{{null}}'), '{{ NULL [] }}\n');
|
||||
});
|
||||
it('parses mustaches with undefined and null parameters', function() {
|
||||
equals(astFor('{{foo undefined null}}'), '{{ PATH:foo [UNDEFINED, NULL] }}\n');
|
||||
equals(
|
||||
astFor('{{foo undefined null}}'),
|
||||
'{{ PATH:foo [UNDEFINED, NULL] }}\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('parses mustaches with DATA parameters', function() {
|
||||
@@ -78,23 +81,53 @@ describe('parser', function() {
|
||||
it('parses mustaches with hash arguments', function() {
|
||||
equals(astFor('{{foo bar=baz}}'), '{{ PATH:foo [] HASH{bar=PATH:baz} }}\n');
|
||||
equals(astFor('{{foo bar=1}}'), '{{ PATH:foo [] HASH{bar=NUMBER{1}} }}\n');
|
||||
equals(astFor('{{foo bar=true}}'), '{{ PATH:foo [] HASH{bar=BOOLEAN{true}} }}\n');
|
||||
equals(astFor('{{foo bar=false}}'), '{{ PATH:foo [] HASH{bar=BOOLEAN{false}} }}\n');
|
||||
equals(astFor('{{foo bar=@baz}}'), '{{ PATH:foo [] HASH{bar=@PATH:baz} }}\n');
|
||||
equals(
|
||||
astFor('{{foo bar=true}}'),
|
||||
'{{ PATH:foo [] HASH{bar=BOOLEAN{true}} }}\n'
|
||||
);
|
||||
equals(
|
||||
astFor('{{foo bar=false}}'),
|
||||
'{{ PATH:foo [] HASH{bar=BOOLEAN{false}} }}\n'
|
||||
);
|
||||
equals(
|
||||
astFor('{{foo bar=@baz}}'),
|
||||
'{{ PATH:foo [] HASH{bar=@PATH:baz} }}\n'
|
||||
);
|
||||
|
||||
equals(astFor('{{foo bar=baz bat=bam}}'), '{{ PATH:foo [] HASH{bar=PATH:baz, bat=PATH:bam} }}\n');
|
||||
equals(astFor('{{foo bar=baz bat="bam"}}'), '{{ PATH:foo [] HASH{bar=PATH:baz, bat="bam"} }}\n');
|
||||
equals(
|
||||
astFor('{{foo bar=baz bat=bam}}'),
|
||||
'{{ PATH:foo [] HASH{bar=PATH:baz, bat=PATH:bam} }}\n'
|
||||
);
|
||||
equals(
|
||||
astFor('{{foo bar=baz bat="bam"}}'),
|
||||
'{{ PATH:foo [] HASH{bar=PATH:baz, bat="bam"} }}\n'
|
||||
);
|
||||
|
||||
equals(astFor("{{foo bat='bam'}}"), '{{ PATH:foo [] HASH{bat="bam"} }}\n');
|
||||
|
||||
equals(astFor('{{foo omg bar=baz bat="bam"}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam"} }}\n');
|
||||
equals(astFor('{{foo omg bar=baz bat="bam" baz=1}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=NUMBER{1}} }}\n');
|
||||
equals(astFor('{{foo omg bar=baz bat="bam" baz=true}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{true}} }}\n');
|
||||
equals(astFor('{{foo omg bar=baz bat="bam" baz=false}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{false}} }}\n');
|
||||
equals(
|
||||
astFor('{{foo omg bar=baz bat="bam"}}'),
|
||||
'{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam"} }}\n'
|
||||
);
|
||||
equals(
|
||||
astFor('{{foo omg bar=baz bat="bam" baz=1}}'),
|
||||
'{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=NUMBER{1}} }}\n'
|
||||
);
|
||||
equals(
|
||||
astFor('{{foo omg bar=baz bat="bam" baz=true}}'),
|
||||
'{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{true}} }}\n'
|
||||
);
|
||||
equals(
|
||||
astFor('{{foo omg bar=baz bat="bam" baz=false}}'),
|
||||
'{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{false}} }}\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('parses contents followed by a mustache', function() {
|
||||
equals(astFor('foo bar {{baz}}'), 'CONTENT[ \'foo bar \' ]\n{{ PATH:baz [] }}\n');
|
||||
equals(
|
||||
astFor('foo bar {{baz}}'),
|
||||
"CONTENT[ 'foo bar ' ]\n{{ PATH:baz [] }}\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses a partial', function() {
|
||||
@@ -108,47 +141,81 @@ describe('parser', function() {
|
||||
});
|
||||
|
||||
it('parses a partial with hash', function() {
|
||||
equals(astFor('{{> foo bar=bat}}'), '{{> PARTIAL:foo HASH{bar=PATH:bat} }}\n');
|
||||
equals(
|
||||
astFor('{{> foo bar=bat}}'),
|
||||
'{{> PARTIAL:foo HASH{bar=PATH:bat} }}\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('parses a partial with context and hash', function() {
|
||||
equals(astFor('{{> foo bar bat=baz}}'), '{{> PARTIAL:foo PATH:bar HASH{bat=PATH:baz} }}\n');
|
||||
equals(
|
||||
astFor('{{> foo bar bat=baz}}'),
|
||||
'{{> PARTIAL:foo PATH:bar HASH{bat=PATH:baz} }}\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('parses a partial with a complex name', function() {
|
||||
equals(astFor('{{> shared/partial?.bar}}'), '{{> PARTIAL:shared/partial?.bar }}\n');
|
||||
equals(
|
||||
astFor('{{> shared/partial?.bar}}'),
|
||||
'{{> PARTIAL:shared/partial?.bar }}\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('parsers partial blocks', function() {
|
||||
equals(astFor('{{#> foo}}bar{{/foo}}'), '{{> PARTIAL BLOCK:foo PROGRAM:\n CONTENT[ \'bar\' ]\n }}\n');
|
||||
equals(
|
||||
astFor('{{#> foo}}bar{{/foo}}'),
|
||||
"{{> PARTIAL BLOCK:foo PROGRAM:\n CONTENT[ 'bar' ]\n }}\n"
|
||||
);
|
||||
});
|
||||
it('should handle parser block mismatch', function() {
|
||||
shouldThrow(function() {
|
||||
astFor('{{#> goodbyes}}{{/hellos}}');
|
||||
}, Error, (/goodbyes doesn't match hellos/));
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('{{#> goodbyes}}{{/hellos}}');
|
||||
},
|
||||
Error,
|
||||
/goodbyes doesn't match hellos/
|
||||
);
|
||||
});
|
||||
it('parsers partial blocks with arguments', function() {
|
||||
equals(astFor('{{#> foo context hash=value}}bar{{/foo}}'), '{{> PARTIAL BLOCK:foo PATH:context HASH{hash=PATH:value} PROGRAM:\n CONTENT[ \'bar\' ]\n }}\n');
|
||||
equals(
|
||||
astFor('{{#> foo context hash=value}}bar{{/foo}}'),
|
||||
"{{> PARTIAL BLOCK:foo PATH:context HASH{hash=PATH:value} PROGRAM:\n CONTENT[ 'bar' ]\n }}\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses a comment', function() {
|
||||
equals(astFor('{{! this is a comment }}'), "{{! ' this is a comment ' }}\n");
|
||||
equals(
|
||||
astFor('{{! this is a comment }}'),
|
||||
"{{! ' this is a comment ' }}\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses a multi-line comment', function() {
|
||||
equals(astFor('{{!\nthis is a multi-line comment\n}}'), "{{! '\nthis is a multi-line comment\n' }}\n");
|
||||
equals(
|
||||
astFor('{{!\nthis is a multi-line comment\n}}'),
|
||||
"{{! '\nthis is a multi-line comment\n' }}\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses an inverse section', function() {
|
||||
equals(astFor('{{#foo}} bar {{^}} baz {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
|
||||
equals(
|
||||
astFor('{{#foo}} bar {{^}} baz {{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses an inverse (else-style) section', function() {
|
||||
equals(astFor('{{#foo}} bar {{else}} baz {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
|
||||
equals(
|
||||
astFor('{{#foo}} bar {{else}} baz {{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses multiple inverse sections', function() {
|
||||
equals(astFor('{{#foo}} bar {{else if bar}}{{else}} baz {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n BLOCK:\n PATH:if [PATH:bar]\n PROGRAM:\n {{^}}\n CONTENT[ ' baz ' ]\n");
|
||||
equals(
|
||||
astFor('{{#foo}} bar {{else if bar}}{{else}} baz {{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n BLOCK:\n PATH:if [PATH:bar]\n PROGRAM:\n {{^}}\n CONTENT[ ' baz ' ]\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses empty blocks', function() {
|
||||
@@ -156,31 +223,52 @@ describe('parser', function() {
|
||||
});
|
||||
|
||||
it('parses empty blocks with empty inverse section', function() {
|
||||
equals(astFor('{{#foo}}{{^}}{{/foo}}'), 'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n');
|
||||
equals(
|
||||
astFor('{{#foo}}{{^}}{{/foo}}'),
|
||||
'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('parses empty blocks with empty inverse (else-style) section', function() {
|
||||
equals(astFor('{{#foo}}{{else}}{{/foo}}'), 'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n');
|
||||
equals(
|
||||
astFor('{{#foo}}{{else}}{{/foo}}'),
|
||||
'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('parses non-empty blocks with empty inverse section', function() {
|
||||
equals(astFor('{{#foo}} bar {{^}}{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
|
||||
equals(
|
||||
astFor('{{#foo}} bar {{^}}{{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses non-empty blocks with empty inverse (else-style) section', function() {
|
||||
equals(astFor('{{#foo}} bar {{else}}{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
|
||||
equals(
|
||||
astFor('{{#foo}} bar {{else}}{{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses empty blocks with non-empty inverse section', function() {
|
||||
equals(astFor('{{#foo}}{{^}} bar {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
|
||||
equals(
|
||||
astFor('{{#foo}}{{^}} bar {{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses empty blocks with non-empty inverse (else-style) section', function() {
|
||||
equals(astFor('{{#foo}}{{else}} bar {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
|
||||
equals(
|
||||
astFor('{{#foo}}{{else}} bar {{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses a standalone inverse section', function() {
|
||||
equals(astFor('{{^foo}}bar{{/foo}}'), "BLOCK:\n PATH:foo []\n {{^}}\n CONTENT[ 'bar' ]\n");
|
||||
equals(
|
||||
astFor('{{^foo}}bar{{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n {{^}}\n CONTENT[ 'bar' ]\n"
|
||||
);
|
||||
});
|
||||
it('throws on old inverse section', function() {
|
||||
shouldThrow(function() {
|
||||
@@ -189,105 +277,179 @@ describe('parser', function() {
|
||||
});
|
||||
|
||||
it('parses block with block params', function() {
|
||||
equals(astFor('{{#foo as |bar baz|}}content{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n");
|
||||
equals(
|
||||
astFor('{{#foo as |bar baz|}}content{{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"
|
||||
);
|
||||
});
|
||||
|
||||
it('parses inverse block with block params', function() {
|
||||
equals(astFor('{{^foo as |bar baz|}}content{{/foo}}'), "BLOCK:\n PATH:foo []\n {{^}}\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n");
|
||||
equals(
|
||||
astFor('{{^foo as |bar baz|}}content{{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n {{^}}\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"
|
||||
);
|
||||
});
|
||||
it('parses chained inverse block with block params', function() {
|
||||
equals(astFor('{{#foo}}{{else foo as |bar baz|}}content{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n");
|
||||
equals(
|
||||
astFor('{{#foo}}{{else foo as |bar baz|}}content{{/foo}}'),
|
||||
"BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"
|
||||
);
|
||||
});
|
||||
it('raises if there\'s a Parse error', function() {
|
||||
shouldThrow(function() {
|
||||
astFor('foo{{^}}bar');
|
||||
}, Error, /Parse error on line 1/);
|
||||
shouldThrow(function() {
|
||||
astFor('{{foo}');
|
||||
}, Error, /Parse error on line 1/);
|
||||
shouldThrow(function() {
|
||||
astFor('{{foo &}}');
|
||||
}, Error, /Parse error on line 1/);
|
||||
shouldThrow(function() {
|
||||
astFor('{{#goodbyes}}{{/hellos}}');
|
||||
}, Error, /goodbyes doesn't match hellos/);
|
||||
it("raises if there's a Parse error", function() {
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('foo{{^}}bar');
|
||||
},
|
||||
Error,
|
||||
/Parse error on line 1/
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('{{foo}');
|
||||
},
|
||||
Error,
|
||||
/Parse error on line 1/
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('{{foo &}}');
|
||||
},
|
||||
Error,
|
||||
/Parse error on line 1/
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('{{#goodbyes}}{{/hellos}}');
|
||||
},
|
||||
Error,
|
||||
/goodbyes doesn't match hellos/
|
||||
);
|
||||
|
||||
shouldThrow(function() {
|
||||
astFor('{{{{goodbyes}}}} {{{{/hellos}}}}');
|
||||
}, Error, /goodbyes doesn't match hellos/);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('{{{{goodbyes}}}} {{{{/hellos}}}}');
|
||||
},
|
||||
Error,
|
||||
/goodbyes doesn't match hellos/
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle invalid paths', function() {
|
||||
shouldThrow(function() {
|
||||
astFor('{{foo/../bar}}');
|
||||
}, Error, /Invalid path: foo\/\.\. - 1:2/);
|
||||
shouldThrow(function() {
|
||||
astFor('{{foo/./bar}}');
|
||||
}, Error, /Invalid path: foo\/\. - 1:2/);
|
||||
shouldThrow(function() {
|
||||
astFor('{{foo/this/bar}}');
|
||||
}, Error, /Invalid path: foo\/this - 1:2/);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('{{foo/../bar}}');
|
||||
},
|
||||
Error,
|
||||
/Invalid path: foo\/\.\. - 1:2/
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('{{foo/./bar}}');
|
||||
},
|
||||
Error,
|
||||
/Invalid path: foo\/\. - 1:2/
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('{{foo/this/bar}}');
|
||||
},
|
||||
Error,
|
||||
/Invalid path: foo\/this - 1:2/
|
||||
);
|
||||
});
|
||||
|
||||
it('knows how to report the correct line number in errors', function() {
|
||||
shouldThrow(function() {
|
||||
astFor('hello\nmy\n{{foo}');
|
||||
}, Error, /Parse error on line 3/);
|
||||
shouldThrow(function() {
|
||||
astFor('hello\n\nmy\n\n{{foo}');
|
||||
}, Error, /Parse error on line 5/);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('hello\nmy\n{{foo}');
|
||||
},
|
||||
Error,
|
||||
/Parse error on line 3/
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('hello\n\nmy\n\n{{foo}');
|
||||
},
|
||||
Error,
|
||||
/Parse error on line 5/
|
||||
);
|
||||
});
|
||||
|
||||
it('knows how to report the correct line number in errors when the first character is a newline', function() {
|
||||
shouldThrow(function() {
|
||||
astFor('\n\nhello\n\nmy\n\n{{foo}');
|
||||
}, Error, /Parse error on line 7/);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('\n\nhello\n\nmy\n\n{{foo}');
|
||||
},
|
||||
Error,
|
||||
/Parse error on line 7/
|
||||
);
|
||||
});
|
||||
|
||||
describe('externally compiled AST', function() {
|
||||
it('can pass through an already-compiled AST', function() {
|
||||
equals(astFor({
|
||||
type: 'Program',
|
||||
body: [ {type: 'ContentStatement', value: 'Hello'}]
|
||||
}), 'CONTENT[ \'Hello\' ]\n');
|
||||
equals(
|
||||
astFor({
|
||||
type: 'Program',
|
||||
body: [{ type: 'ContentStatement', value: 'Hello' }]
|
||||
}),
|
||||
"CONTENT[ 'Hello' ]\n"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('directives', function() {
|
||||
it('should parse block directives', function() {
|
||||
equals(astFor('{{#* foo}}{{/foo}}'), 'DIRECTIVE BLOCK:\n PATH:foo []\n PROGRAM:\n');
|
||||
equals(
|
||||
astFor('{{#* foo}}{{/foo}}'),
|
||||
'DIRECTIVE BLOCK:\n PATH:foo []\n PROGRAM:\n'
|
||||
);
|
||||
});
|
||||
it('should parse directives', function() {
|
||||
equals(astFor('{{* foo}}'), '{{ DIRECTIVE PATH:foo [] }}\n');
|
||||
});
|
||||
it('should fail if directives have inverse', function() {
|
||||
shouldThrow(function() {
|
||||
astFor('{{#* foo}}{{^}}{{/foo}}');
|
||||
}, Error, /Unexpected inverse/);
|
||||
shouldThrow(
|
||||
function() {
|
||||
astFor('{{#* foo}}{{^}}{{/foo}}');
|
||||
},
|
||||
Error,
|
||||
/Unexpected inverse/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('GH1024 - should track program location properly', function() {
|
||||
var p = Handlebars.parse('\n'
|
||||
+ ' {{#if foo}}\n'
|
||||
+ ' {{bar}}\n'
|
||||
+ ' {{else}} {{baz}}\n'
|
||||
+ '\n'
|
||||
+ ' {{/if}}\n'
|
||||
+ ' ');
|
||||
var p = Handlebars.parse(
|
||||
'\n' +
|
||||
' {{#if foo}}\n' +
|
||||
' {{bar}}\n' +
|
||||
' {{else}} {{baz}}\n' +
|
||||
'\n' +
|
||||
' {{/if}}\n' +
|
||||
' '
|
||||
);
|
||||
|
||||
// We really need a deep equals but for now this should be stable...
|
||||
equals(JSON.stringify(p.loc), JSON.stringify({
|
||||
start: { line: 1, column: 0 },
|
||||
end: { line: 7, column: 4 }
|
||||
}));
|
||||
equals(JSON.stringify(p.body[1].program.loc), JSON.stringify({
|
||||
start: { line: 2, column: 13 },
|
||||
end: { line: 4, column: 7 }
|
||||
}));
|
||||
equals(JSON.stringify(p.body[1].inverse.loc), JSON.stringify({
|
||||
start: { line: 4, column: 15 },
|
||||
end: { line: 6, column: 5 }
|
||||
}));
|
||||
equals(
|
||||
JSON.stringify(p.loc),
|
||||
JSON.stringify({
|
||||
start: { line: 1, column: 0 },
|
||||
end: { line: 7, column: 4 }
|
||||
})
|
||||
);
|
||||
equals(
|
||||
JSON.stringify(p.body[1].program.loc),
|
||||
JSON.stringify({
|
||||
start: { line: 2, column: 13 },
|
||||
end: { line: 4, column: 7 }
|
||||
})
|
||||
);
|
||||
equals(
|
||||
JSON.stringify(p.body[1].inverse.loc),
|
||||
JSON.stringify({
|
||||
start: { line: 4, column: 15 },
|
||||
end: { line: 6, column: 5 }
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+504
-162
@@ -2,115 +2,224 @@ describe('partials', function() {
|
||||
it('basic partials', function() {
|
||||
var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}';
|
||||
var partial = '{{name}} ({{url}}) ';
|
||||
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {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) ');
|
||||
var hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ 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) '
|
||||
);
|
||||
});
|
||||
|
||||
it('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 hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
var helpers = {
|
||||
partial: 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) ');
|
||||
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) '
|
||||
);
|
||||
});
|
||||
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 hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
var helpers = {
|
||||
partial: 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');
|
||||
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'
|
||||
);
|
||||
});
|
||||
|
||||
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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ',
|
||||
'Partials can be passed a context');
|
||||
var hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: partial }],
|
||||
true,
|
||||
'Dudes: Yehuda (http://yehuda) Alan (http://alan) ',
|
||||
'Partials can be passed a context'
|
||||
);
|
||||
});
|
||||
|
||||
it('partials with no context', function() {
|
||||
var partial = '{{name}} ({{url}}) ';
|
||||
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
|
||||
var hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
'Dudes: {{#dudes}}{{>dude}}{{/dudes}}',
|
||||
[hash, {}, {dude: partial}, {explicitPartialContext: true}],
|
||||
true,
|
||||
'Dudes: () () ');
|
||||
'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 () ');
|
||||
'Dudes: {{#dudes}}{{>dude name="foo"}}{{/dudes}}',
|
||||
[hash, {}, { dude: partial }, { explicitPartialContext: true }],
|
||||
true,
|
||||
'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');
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: partial }],
|
||||
true,
|
||||
'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');
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: partial }],
|
||||
true,
|
||||
'Dudes: Empty'
|
||||
);
|
||||
});
|
||||
|
||||
it('partials with duplicate parameters', function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile('Dudes: {{>dude dudes foo bar=baz}}');
|
||||
}, Error, 'Unsupported number of partial arguments: 2 - 1:7');
|
||||
shouldThrow(
|
||||
function() {
|
||||
CompilerContext.compile('Dudes: {{>dude dudes foo bar=baz}}');
|
||||
},
|
||||
Error,
|
||||
'Unsupported number of partial arguments: 2 - 1:7'
|
||||
);
|
||||
});
|
||||
|
||||
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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: barYehuda (http://yehuda) barAlan (http://alan) ',
|
||||
'Basic partials output based on current context.');
|
||||
var hash = {
|
||||
foo: 'bar',
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ 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.'
|
||||
);
|
||||
});
|
||||
|
||||
it('partial in a partial', function() {
|
||||
var string = 'Dudes: {{#dudes}}{{>dude}}{{/dudes}}';
|
||||
var dude = '{{name}} {{> url}} ';
|
||||
var url = '<a href="{{url}}">{{url}}</a>';
|
||||
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true, 'Dudes: Yehuda <a href="http://yehuda">http://yehuda</a> Alan <a href="http://alan">http://alan</a> ', 'Partials are rendered inside of other partials');
|
||||
var hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: dude, url: url }],
|
||||
true,
|
||||
'Dudes: Yehuda <a href="http://yehuda">http://yehuda</a> Alan <a href="http://alan">http://alan</a> ',
|
||||
'Partials are rendered inside of other partials'
|
||||
);
|
||||
});
|
||||
|
||||
it('rendering undefined partial throws an exception', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{> whatever}}');
|
||||
template();
|
||||
}, Handlebars.Exception, 'The partial whatever could not be found');
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{> whatever}}');
|
||||
template();
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'The partial whatever could not be found'
|
||||
);
|
||||
});
|
||||
|
||||
it('registering undefined partial throws an exception', function() {
|
||||
shouldThrow(function() {
|
||||
var undef;
|
||||
handlebarsEnv.registerPartial('undefined_test', undef);
|
||||
}, Handlebars.Exception, 'Attempting to register a partial called "undefined_test" as undefined');
|
||||
shouldThrow(
|
||||
function() {
|
||||
var undef;
|
||||
handlebarsEnv.registerPartial('undefined_test', undef);
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'Attempting to register a partial called "undefined_test" as undefined'
|
||||
);
|
||||
});
|
||||
|
||||
it('rendering template partial in vm mode throws an exception', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{> whatever}}');
|
||||
template();
|
||||
}, Handlebars.Exception, 'The partial whatever could not be found');
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{> whatever}}');
|
||||
template();
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'The partial whatever could not be found'
|
||||
);
|
||||
});
|
||||
|
||||
it('rendering function partial in vm mode', function() {
|
||||
@@ -118,30 +227,57 @@ describe('partials', function() {
|
||||
function partial(context) {
|
||||
return context.name + ' (' + context.url + ') ';
|
||||
}
|
||||
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
|
||||
shouldCompileTo(string, [hash, {}, {dude: partial}], 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ',
|
||||
'Function partials output based in VM.');
|
||||
var hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, {}, { dude: partial }],
|
||||
'Dudes: Yehuda (http://yehuda) Alan (http://alan) ',
|
||||
'Function partials output based in VM.'
|
||||
);
|
||||
});
|
||||
|
||||
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');
|
||||
var hash = { name: 'Jeepers', anotherDude: 'Creepers' };
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: dude }],
|
||||
true,
|
||||
'Dudes: Jeepers Creepers',
|
||||
'Regular selectors can follow a partial'
|
||||
);
|
||||
});
|
||||
|
||||
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');
|
||||
var hash = { name: 'Jeepers', anotherDude: 'Creepers' };
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { 'shared/dude': dude }],
|
||||
true,
|
||||
'Dudes: Jeepers',
|
||||
'Partials can use literal paths'
|
||||
);
|
||||
});
|
||||
|
||||
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');
|
||||
var hash = { name: 'Jeepers', anotherDude: 'Creepers' };
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { 'shared/dude.thing': dude }],
|
||||
true,
|
||||
'Dudes: Jeepers',
|
||||
'Partials can use literal with points in paths'
|
||||
);
|
||||
});
|
||||
|
||||
it('Global Partials', function() {
|
||||
@@ -149,8 +285,14 @@ 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');
|
||||
var hash = { name: 'Jeepers', anotherDude: 'Creepers' };
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { 'shared/dude': dude }],
|
||||
true,
|
||||
'Dudes: Jeepers Creepers',
|
||||
'Partials can use globals or passed'
|
||||
);
|
||||
|
||||
handlebarsEnv.unregisterPartial('globalTest');
|
||||
equals(handlebarsEnv.partials.globalTest, undefined);
|
||||
@@ -163,51 +305,95 @@ 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');
|
||||
var hash = { name: 'Jeepers', anotherDude: 'Creepers' };
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash],
|
||||
true,
|
||||
'Dudes: Jeepers Creepers',
|
||||
'Partials can use globals or passed'
|
||||
);
|
||||
});
|
||||
|
||||
it('Partials with integer path', 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');
|
||||
var hash = { name: 'Jeepers', anotherDude: 'Creepers' };
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { 404: dude }],
|
||||
true,
|
||||
'Dudes: Jeepers',
|
||||
'Partials can use literal paths'
|
||||
);
|
||||
});
|
||||
|
||||
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');
|
||||
var hash = { name: 'Jeepers', anotherDude: 'Creepers' };
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { '404/asdf?.bar': dude }],
|
||||
true,
|
||||
'Dudes: Jeepers',
|
||||
'Partials can use literal paths'
|
||||
);
|
||||
});
|
||||
|
||||
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');
|
||||
var hash = { name: 'Jeepers', anotherDude: 'Creepers' };
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { '+404/asdf?.bar': dude }],
|
||||
true,
|
||||
'Dudes: Jeepers',
|
||||
'Partials can use literal paths'
|
||||
);
|
||||
});
|
||||
|
||||
it('Partials with string', function() {
|
||||
var string = 'Dudes: {{> \'+404/asdf?.bar\'}}';
|
||||
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');
|
||||
var hash = { name: 'Jeepers', anotherDude: 'Creepers' };
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { '+404/asdf?.bar': dude }],
|
||||
true,
|
||||
'Dudes: Jeepers',
|
||||
'Partials can use literal paths'
|
||||
);
|
||||
});
|
||||
|
||||
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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: ');
|
||||
var hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: partial }],
|
||||
true,
|
||||
'Dudes: '
|
||||
);
|
||||
});
|
||||
|
||||
it('throw on missing partial', function() {
|
||||
var compile = handlebarsEnv.compile;
|
||||
handlebarsEnv.compile = undefined;
|
||||
shouldThrow(function() {
|
||||
shouldCompileTo('{{> dude}}', [{}, {}, {dude: 'fail'}], '');
|
||||
}, Error, /The partial dude could not be compiled/);
|
||||
shouldThrow(
|
||||
function() {
|
||||
shouldCompileTo('{{> dude}}', [{}, {}, { dude: 'fail' }], '');
|
||||
},
|
||||
Error,
|
||||
/The partial dude could not be compiled/
|
||||
);
|
||||
handlebarsEnv.compile = compile;
|
||||
});
|
||||
|
||||
@@ -217,224 +403,309 @@ describe('partials', function() {
|
||||
'{{#> dude}}success{{/dude}}',
|
||||
[{}, {}, {}],
|
||||
true,
|
||||
'success');
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should execute default block with proper context', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#> dude context}}{{value}}{{/dude}}',
|
||||
[{context: {value: 'success'}}, {}, {}],
|
||||
[{ context: { value: 'success' } }, {}, {}],
|
||||
true,
|
||||
'success');
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should propagate block parameters to default block', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}',
|
||||
[{context: {value: 'success'}}, {}, {}],
|
||||
[{ context: { value: 'success' } }, {}, {}],
|
||||
true,
|
||||
'success');
|
||||
'success'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not use partial block if partial exists', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#> dude}}fail{{/dude}}',
|
||||
[{}, {}, {dude: 'success'}],
|
||||
[{}, {}, { dude: 'success' }],
|
||||
true,
|
||||
'success');
|
||||
'success'
|
||||
);
|
||||
});
|
||||
|
||||
it('should render block from partial', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#> dude}}success{{/dude}}',
|
||||
[{}, {}, {dude: '{{> @partial-block }}'}],
|
||||
[{}, {}, { dude: '{{> @partial-block }}' }],
|
||||
true,
|
||||
'success');
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should be able to render the partial-block twice', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#> dude}}success{{/dude}}',
|
||||
[{}, {}, {dude: '{{> @partial-block }} {{> @partial-block }}'}],
|
||||
true,
|
||||
'success success');
|
||||
'{{#> dude}}success{{/dude}}',
|
||||
[{}, {}, { dude: '{{> @partial-block }} {{> @partial-block }}' }],
|
||||
true,
|
||||
'success success'
|
||||
);
|
||||
});
|
||||
it('should render block from partial with context', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#> dude}}{{value}}{{/dude}}',
|
||||
[{context: {value: 'success'}}, {}, {dude: '{{#with context}}{{> @partial-block }}{{/with}}'}],
|
||||
[
|
||||
{ context: { value: 'success' } },
|
||||
{},
|
||||
{ dude: '{{#with context}}{{> @partial-block }}{{/with}}' }
|
||||
],
|
||||
true,
|
||||
'success');
|
||||
'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: '<code>before-block: {{@root/value}} {{> @partial-block }}</code>'}],
|
||||
[
|
||||
{ value: 'success' },
|
||||
{},
|
||||
{
|
||||
dude:
|
||||
'<code>before-block: {{@root/value}} {{> @partial-block }}</code>'
|
||||
}
|
||||
],
|
||||
true,
|
||||
'<code>before-block: success in-block: success</code>');
|
||||
'<code>before-block: success in-block: success</code>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow the #each-helper to be used along with partial-blocks', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'<template>{{#> list value}}value = {{.}}{{/list}}</template>',
|
||||
[
|
||||
{value: ['a', 'b', 'c']},
|
||||
{ value: ['a', 'b', 'c'] },
|
||||
{},
|
||||
{
|
||||
list: '<list>{{#each .}}<item>{{> @partial-block}}</item>{{/each}}</list>'
|
||||
list:
|
||||
'<list>{{#each .}}<item>{{> @partial-block}}</item>{{/each}}</list>'
|
||||
}
|
||||
],
|
||||
true,
|
||||
'<template><list><item>value = a</item><item>value = b</item><item>value = c</item></list></template>');
|
||||
'<template><list><item>value = a</item><item>value = b</item><item>value = c</item></list></template>'
|
||||
);
|
||||
});
|
||||
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');
|
||||
'{{#> dude}}{{value}}{{/dude}}',
|
||||
[
|
||||
{ context: { value: 'success' } },
|
||||
{},
|
||||
{
|
||||
dude:
|
||||
'{{#with context}}{{> @partial-block }} {{> @partial-block }}{{/with}}'
|
||||
}
|
||||
],
|
||||
true,
|
||||
'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}}'}],
|
||||
[
|
||||
{ context: { value: 'success' } },
|
||||
{},
|
||||
{ dude: '{{#with context}}{{> @partial-block }}{{/with}}' }
|
||||
],
|
||||
true,
|
||||
'success');
|
||||
'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 }}'}],
|
||||
[
|
||||
{ context: { value: 'success' } },
|
||||
{},
|
||||
{ dude: '{{> @partial-block }}' }
|
||||
],
|
||||
true,
|
||||
'success');
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should render nested partial blocks', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'<template>{{#> outer}}{{value}}{{/outer}}</template>',
|
||||
[
|
||||
{value: 'success'},
|
||||
{ value: 'success' },
|
||||
{},
|
||||
{
|
||||
outer: '<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}</outer>',
|
||||
outer:
|
||||
'<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}</outer>',
|
||||
nested: '<nested>{{> @partial-block}}</nested>'
|
||||
}
|
||||
],
|
||||
true,
|
||||
'<template><outer><nested><outer-block>success</outer-block></nested></outer></template>');
|
||||
'<template><outer><nested><outer-block>success</outer-block></nested></outer></template>'
|
||||
);
|
||||
});
|
||||
it('should render nested partial blocks at different nesting levels', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'<template>{{#> outer}}{{value}}{{/outer}}</template>',
|
||||
[
|
||||
{value: 'success'},
|
||||
{ value: 'success' },
|
||||
{},
|
||||
{
|
||||
outer: '<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}</outer>',
|
||||
outer:
|
||||
'<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}</outer>',
|
||||
nested: '<nested>{{> @partial-block}}</nested>'
|
||||
}
|
||||
],
|
||||
true,
|
||||
'<template><outer><nested><outer-block>success</outer-block></nested>success</outer></template>');
|
||||
'<template><outer><nested><outer-block>success</outer-block></nested>success</outer></template>'
|
||||
);
|
||||
});
|
||||
it('should render nested partial blocks at different nesting levels (twice)', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'<template>{{#> outer}}{{value}}{{/outer}}</template>',
|
||||
[
|
||||
{value: 'success'},
|
||||
{ value: 'success' },
|
||||
{},
|
||||
{
|
||||
outer: '<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}+{{> @partial-block}}</outer>',
|
||||
outer:
|
||||
'<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}+{{> @partial-block}}</outer>',
|
||||
nested: '<nested>{{> @partial-block}}</nested>'
|
||||
}
|
||||
],
|
||||
true,
|
||||
'<template><outer><nested><outer-block>success success</outer-block></nested>success+success</outer></template>');
|
||||
'<template><outer><nested><outer-block>success success</outer-block></nested>success+success</outer></template>'
|
||||
);
|
||||
});
|
||||
it('should render nested partial blocks (twice at each level)', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'<template>{{#> outer}}{{value}}{{/outer}}</template>',
|
||||
[
|
||||
{value: 'success'},
|
||||
{ value: 'success' },
|
||||
{},
|
||||
{
|
||||
outer: '<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}</outer>',
|
||||
outer:
|
||||
'<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}</outer>',
|
||||
nested: '<nested>{{> @partial-block}}{{> @partial-block}}</nested>'
|
||||
}
|
||||
],
|
||||
true,
|
||||
'<template><outer>' +
|
||||
'<nested><outer-block>success success</outer-block><outer-block>success success</outer-block></nested>' +
|
||||
'</outer></template>');
|
||||
'<nested><outer-block>success success</outer-block><outer-block>success success</outer-block></nested>' +
|
||||
'</outer></template>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inline partials', function() {
|
||||
it('should define inline partials for template', function() {
|
||||
shouldCompileTo('{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', {}, 'success');
|
||||
shouldCompileTo(
|
||||
'{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}',
|
||||
{},
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should overwrite multiple partials in the same template', function() {
|
||||
shouldCompileTo('{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', {}, 'success');
|
||||
shouldCompileTo(
|
||||
'{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}',
|
||||
{},
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should define inline partials for block', function() {
|
||||
shouldCompileTo('{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}', {}, 'success');
|
||||
shouldThrow(function() {
|
||||
shouldCompileTo('{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}', {}, 'success');
|
||||
}, Error, /myPartial could not/);
|
||||
shouldCompileTo(
|
||||
'{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}',
|
||||
{},
|
||||
'success'
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
shouldCompileTo(
|
||||
'{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}',
|
||||
{},
|
||||
'success'
|
||||
);
|
||||
},
|
||||
Error,
|
||||
/myPartial could not/
|
||||
);
|
||||
});
|
||||
it('should override global partials', function() {
|
||||
shouldCompileTo('{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}', {hash: {}, partials: {myPartial: function() { return 'fail'; }}}, 'success');
|
||||
shouldCompileTo(
|
||||
'{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}',
|
||||
{
|
||||
hash: {},
|
||||
partials: {
|
||||
myPartial: function() {
|
||||
return 'fail';
|
||||
}
|
||||
}
|
||||
},
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should override template partials', function() {
|
||||
shouldCompileTo('{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}', {}, 'success');
|
||||
shouldCompileTo(
|
||||
'{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}',
|
||||
{},
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should override partials down the entire stack', function() {
|
||||
shouldCompileTo('{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}', {}, 'success');
|
||||
shouldCompileTo(
|
||||
'{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}',
|
||||
{},
|
||||
'success'
|
||||
);
|
||||
});
|
||||
|
||||
it('should define inline partials for partial call', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#*inline "myPartial"}}success{{/inline}}{{> dude}}',
|
||||
[{}, {}, {dude: '{{> myPartial }}'}],
|
||||
[{}, {}, { dude: '{{> myPartial }}' }],
|
||||
true,
|
||||
'success');
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should define inline partials in partial block call', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#> dude}}{{#*inline "myPartial"}}success{{/inline}}{{/dude}}',
|
||||
[{}, {}, {dude: '{{> myPartial }}'}],
|
||||
[{}, {}, { dude: '{{> myPartial }}' }],
|
||||
true,
|
||||
'success');
|
||||
'success'
|
||||
);
|
||||
});
|
||||
it('should render nested inline partials', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}}</outer-block>{{/inner}}{{/inline}}' +
|
||||
'{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
|
||||
'{{#>outer}}{{value}}{{/outer}}',
|
||||
[{value: 'success'}, {}, {}],
|
||||
'{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
|
||||
'{{#>outer}}{{value}}{{/outer}}',
|
||||
[{ value: 'success' }, {}, {}],
|
||||
true,
|
||||
'<inner><outer-block>success</outer-block></inner>');
|
||||
'<inner><outer-block>success</outer-block></inner>'
|
||||
);
|
||||
});
|
||||
it('should render nested inline partials with partial-blocks on different nesting levels', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}}</outer-block>{{/inner}}{{>@partial-block}}{{/inline}}' +
|
||||
'{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
|
||||
'{{#>outer}}{{value}}{{/outer}}',
|
||||
[{value: 'success'}, {}, {}],
|
||||
'{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
|
||||
'{{#>outer}}{{value}}{{/outer}}',
|
||||
[{ value: 'success' }, {}, {}],
|
||||
true,
|
||||
'<inner><outer-block>success</outer-block></inner>success');
|
||||
'<inner><outer-block>success</outer-block></inner>success'
|
||||
);
|
||||
});
|
||||
it('should render nested inline partials (twice at each level)', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}} {{>@partial-block}}</outer-block>{{/inner}}{{/inline}}' +
|
||||
'{{#*inline "inner"}}<inner>{{>@partial-block}}{{>@partial-block}}</inner>{{/inline}}' +
|
||||
'{{#>outer}}{{value}}{{/outer}}',
|
||||
[{value: 'success'}, {}, {}],
|
||||
'{{#*inline "inner"}}<inner>{{>@partial-block}}{{>@partial-block}}</inner>{{/inline}}' +
|
||||
'{{#>outer}}{{value}}{{/outer}}',
|
||||
[{ value: 'success' }, {}, {}],
|
||||
true,
|
||||
'<inner><outer-block>success success</outer-block><outer-block>success success</outer-block></inner>');
|
||||
'<inner><outer-block>success success</outer-block><outer-block>success success</outer-block></inner>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -442,8 +713,8 @@ describe('partials', function() {
|
||||
if (Handlebars.compile) {
|
||||
var env = Handlebars.create();
|
||||
env.registerPartial('partial', '{{foo}}');
|
||||
var template = env.compile('{{foo}} {{> partial}}', {noEscape: true});
|
||||
equal(template({foo: '<'}), '< <');
|
||||
var template = env.compile('{{foo}} {{> partial}}', { noEscape: true });
|
||||
equal(template({ foo: '<' }), '< <');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -451,25 +722,52 @@ describe('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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: dude}], true,
|
||||
'Dudes:\n Yehuda\n Alan\n');
|
||||
var hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: dude }],
|
||||
true,
|
||||
'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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true,
|
||||
'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n');
|
||||
var hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ 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'
|
||||
);
|
||||
});
|
||||
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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}, {preventIndent: true}], true,
|
||||
'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n');
|
||||
var hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ 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'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -477,26 +775,70 @@ describe('partials', 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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}, true], true, 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ');
|
||||
var hash = {
|
||||
root: 'yes',
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: partial }, true],
|
||||
true,
|
||||
'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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}, true], true, 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ');
|
||||
var hash = {
|
||||
root: 'yes',
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: partial }, true],
|
||||
true,
|
||||
'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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}, true, false], true, 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ');
|
||||
var hash = {
|
||||
root: 'yes',
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: partial }, true, false],
|
||||
true,
|
||||
'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'}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}, true], true, 'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes ');
|
||||
var hash = {
|
||||
root: 'yes',
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[hash, {}, { dude: partial }, true],
|
||||
true,
|
||||
'Dudes: Yehuda (http://yehuda) yes Alan (http://alan) yes '
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+185
-92
@@ -6,27 +6,24 @@ describe('precompiler', function() {
|
||||
}
|
||||
|
||||
var Handlebars = require('../lib'),
|
||||
Precompiler = require('../dist/cjs/precompiler'),
|
||||
fs = require('fs'),
|
||||
uglify = require('uglify-js');
|
||||
Precompiler = require('../dist/cjs/precompiler'),
|
||||
fs = require('fs'),
|
||||
uglify = require('uglify-js');
|
||||
|
||||
var log,
|
||||
logFunction,
|
||||
errorLog,
|
||||
errorLogFunction,
|
||||
|
||||
precompile,
|
||||
minify,
|
||||
|
||||
emptyTemplate = {
|
||||
path: __dirname + '/artifacts/empty.handlebars',
|
||||
name: 'empty',
|
||||
source: ''
|
||||
},
|
||||
|
||||
file,
|
||||
content,
|
||||
writeFileSync;
|
||||
logFunction,
|
||||
errorLog,
|
||||
errorLogFunction,
|
||||
precompile,
|
||||
minify,
|
||||
emptyTemplate = {
|
||||
path: __dirname + '/artifacts/empty.handlebars',
|
||||
name: 'empty',
|
||||
source: ''
|
||||
},
|
||||
file,
|
||||
content,
|
||||
writeFileSync;
|
||||
|
||||
/**
|
||||
* Mock the Module.prototype.require-function such that an error is thrown, when "uglify-js" is loaded.
|
||||
@@ -87,102 +84,166 @@ describe('precompiler', function() {
|
||||
});
|
||||
|
||||
it('should output version', function() {
|
||||
Precompiler.cli({templates: [], version: true});
|
||||
Precompiler.cli({ templates: [], version: true });
|
||||
equals(log, Handlebars.VERSION);
|
||||
});
|
||||
it('should throw if lacking templates', function() {
|
||||
shouldThrow(function() {
|
||||
Precompiler.cli({templates: []});
|
||||
}, Handlebars.Exception, 'Must define at least one template or directory.');
|
||||
shouldThrow(
|
||||
function() {
|
||||
Precompiler.cli({ templates: [] });
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'Must define at least one template or directory.'
|
||||
);
|
||||
});
|
||||
it('should handle empty/filtered directories', function() {
|
||||
Precompiler.cli({hasDirectory: true, templates: []});
|
||||
Precompiler.cli({ hasDirectory: true, templates: [] });
|
||||
// Success is not throwing
|
||||
});
|
||||
it('should throw when combining simple and minimized', function() {
|
||||
shouldThrow(function() {
|
||||
Precompiler.cli({templates: [__dirname], simple: true, min: true});
|
||||
}, Handlebars.Exception, 'Unable to minimize simple output');
|
||||
shouldThrow(
|
||||
function() {
|
||||
Precompiler.cli({ templates: [__dirname], simple: true, min: true });
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'Unable to minimize simple output'
|
||||
);
|
||||
});
|
||||
it('should throw when combining simple and multiple templates', function() {
|
||||
shouldThrow(function() {
|
||||
Precompiler.cli({templates: [__dirname + '/artifacts/empty.handlebars', __dirname + '/artifacts/empty.handlebars'], simple: true});
|
||||
}, Handlebars.Exception, 'Unable to output multiple templates in simple mode');
|
||||
shouldThrow(
|
||||
function() {
|
||||
Precompiler.cli({
|
||||
templates: [
|
||||
__dirname + '/artifacts/empty.handlebars',
|
||||
__dirname + '/artifacts/empty.handlebars'
|
||||
],
|
||||
simple: true
|
||||
});
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'Unable to output multiple templates in simple mode'
|
||||
);
|
||||
});
|
||||
it('should throw when missing name', function() {
|
||||
shouldThrow(function() {
|
||||
Precompiler.cli({templates: [{source: ''}], amd: true});
|
||||
}, Handlebars.Exception, 'Name missing for template');
|
||||
shouldThrow(
|
||||
function() {
|
||||
Precompiler.cli({ templates: [{ source: '' }], amd: true });
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'Name missing for template'
|
||||
);
|
||||
});
|
||||
it('should throw when combining simple and directories', function() {
|
||||
shouldThrow(function() {
|
||||
Precompiler.cli({hasDirectory: true, templates: [1], simple: true});
|
||||
}, Handlebars.Exception, 'Unable to output multiple templates in simple mode');
|
||||
shouldThrow(
|
||||
function() {
|
||||
Precompiler.cli({ hasDirectory: true, templates: [1], simple: true });
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'Unable to output multiple templates in simple mode'
|
||||
);
|
||||
});
|
||||
|
||||
it('should output simple templates', function() {
|
||||
Handlebars.precompile = function() { return 'simple'; };
|
||||
Precompiler.cli({templates: [emptyTemplate], simple: true});
|
||||
Handlebars.precompile = function() {
|
||||
return 'simple';
|
||||
};
|
||||
Precompiler.cli({ templates: [emptyTemplate], simple: true });
|
||||
equal(log, 'simple\n');
|
||||
});
|
||||
it('should default to simple templates', function() {
|
||||
Handlebars.precompile = function() { return 'simple'; };
|
||||
Precompiler.cli({templates: [{source: ''}]});
|
||||
Handlebars.precompile = function() {
|
||||
return 'simple';
|
||||
};
|
||||
Precompiler.cli({ templates: [{ source: '' }] });
|
||||
equal(log, 'simple\n');
|
||||
});
|
||||
it('should output amd templates', function() {
|
||||
Handlebars.precompile = function() { return 'amd'; };
|
||||
Precompiler.cli({templates: [emptyTemplate], amd: true});
|
||||
Handlebars.precompile = function() {
|
||||
return 'amd';
|
||||
};
|
||||
Precompiler.cli({ templates: [emptyTemplate], amd: true });
|
||||
equal(/template\(amd\)/.test(log), true);
|
||||
});
|
||||
it('should output multiple amd', function() {
|
||||
Handlebars.precompile = function() { return 'amd'; };
|
||||
Precompiler.cli({templates: [emptyTemplate, emptyTemplate], amd: true, namespace: 'foo'});
|
||||
Handlebars.precompile = function() {
|
||||
return 'amd';
|
||||
};
|
||||
Precompiler.cli({
|
||||
templates: [emptyTemplate, emptyTemplate],
|
||||
amd: true,
|
||||
namespace: 'foo'
|
||||
});
|
||||
equal(/templates = foo = foo \|\|/.test(log), true);
|
||||
equal(/return templates/.test(log), true);
|
||||
equal(/template\(amd\)/.test(log), true);
|
||||
});
|
||||
it('should output amd partials', function() {
|
||||
Handlebars.precompile = function() { return 'amd'; };
|
||||
Precompiler.cli({templates: [emptyTemplate], amd: true, partial: true});
|
||||
Handlebars.precompile = function() {
|
||||
return 'amd';
|
||||
};
|
||||
Precompiler.cli({ templates: [emptyTemplate], amd: true, partial: true });
|
||||
equal(/return Handlebars\.partials\['empty'\]/.test(log), true);
|
||||
equal(/template\(amd\)/.test(log), true);
|
||||
});
|
||||
it('should output multiple amd partials', function() {
|
||||
Handlebars.precompile = function() { return 'amd'; };
|
||||
Precompiler.cli({templates: [emptyTemplate, emptyTemplate], amd: true, partial: true});
|
||||
Handlebars.precompile = function() {
|
||||
return 'amd';
|
||||
};
|
||||
Precompiler.cli({
|
||||
templates: [emptyTemplate, emptyTemplate],
|
||||
amd: true,
|
||||
partial: true
|
||||
});
|
||||
equal(/return Handlebars\.partials\[/.test(log), false);
|
||||
equal(/template\(amd\)/.test(log), true);
|
||||
});
|
||||
it('should output commonjs templates', function() {
|
||||
Handlebars.precompile = function() { return 'commonjs'; };
|
||||
Precompiler.cli({templates: [emptyTemplate], commonjs: true});
|
||||
Handlebars.precompile = function() {
|
||||
return 'commonjs';
|
||||
};
|
||||
Precompiler.cli({ templates: [emptyTemplate], commonjs: true });
|
||||
equal(/template\(commonjs\)/.test(log), true);
|
||||
});
|
||||
|
||||
it('should set data flag', function() {
|
||||
Handlebars.precompile = function(data, options) { equal(options.data, true); return 'simple'; };
|
||||
Precompiler.cli({templates: [emptyTemplate], simple: true, data: true});
|
||||
Handlebars.precompile = function(data, options) {
|
||||
equal(options.data, true);
|
||||
return 'simple';
|
||||
};
|
||||
Precompiler.cli({ templates: [emptyTemplate], simple: true, data: true });
|
||||
equal(log, 'simple\n');
|
||||
});
|
||||
|
||||
it('should set known helpers', function() {
|
||||
Handlebars.precompile = function(data, options) { equal(options.knownHelpers.foo, true); return 'simple'; };
|
||||
Precompiler.cli({templates: [emptyTemplate], simple: true, known: 'foo'});
|
||||
Handlebars.precompile = function(data, options) {
|
||||
equal(options.knownHelpers.foo, true);
|
||||
return 'simple';
|
||||
};
|
||||
Precompiler.cli({ templates: [emptyTemplate], simple: true, known: 'foo' });
|
||||
equal(log, 'simple\n');
|
||||
});
|
||||
it('should output to file system', function() {
|
||||
Handlebars.precompile = function() { return 'simple'; };
|
||||
Precompiler.cli({templates: [emptyTemplate], simple: true, output: 'file!'});
|
||||
Handlebars.precompile = function() {
|
||||
return 'simple';
|
||||
};
|
||||
Precompiler.cli({
|
||||
templates: [emptyTemplate],
|
||||
simple: true,
|
||||
output: 'file!'
|
||||
});
|
||||
equal(file, 'file!');
|
||||
equal(content, 'simple\n');
|
||||
equal(log, '');
|
||||
});
|
||||
|
||||
it('should output minimized templates', function() {
|
||||
Handlebars.precompile = function() { return 'amd'; };
|
||||
uglify.minify = function() { return {code: 'min'}; };
|
||||
Precompiler.cli({templates: [emptyTemplate], min: true});
|
||||
Handlebars.precompile = function() {
|
||||
return 'amd';
|
||||
};
|
||||
uglify.minify = function() {
|
||||
return { code: 'min' };
|
||||
};
|
||||
Precompiler.cli({ templates: [emptyTemplate], min: true });
|
||||
equal(log, 'min');
|
||||
});
|
||||
|
||||
@@ -191,8 +252,10 @@ describe('precompiler', function() {
|
||||
error.code = 'MODULE_NOT_FOUND';
|
||||
mockRequireUglify(error, function() {
|
||||
var Precompiler = require('../dist/cjs/precompiler');
|
||||
Handlebars.precompile = function() { return 'amd'; };
|
||||
Precompiler.cli({templates: [emptyTemplate], min: true});
|
||||
Handlebars.precompile = function() {
|
||||
return 'amd';
|
||||
};
|
||||
Precompiler.cli({ templates: [emptyTemplate], min: true });
|
||||
equal(/template\(amd\)/.test(log), true);
|
||||
equal(/\n/.test(log), true);
|
||||
equal(/Code minimization is disabled/.test(errorLog), true);
|
||||
@@ -201,23 +264,33 @@ describe('precompiler', function() {
|
||||
|
||||
it('should fail on errors (other than missing module) while loading uglify-js', function() {
|
||||
mockRequireUglify(new Error('Mock Error'), function() {
|
||||
shouldThrow(function() {
|
||||
var Precompiler = require('../dist/cjs/precompiler');
|
||||
Handlebars.precompile = function() { return 'amd'; };
|
||||
Precompiler.cli({templates: [emptyTemplate], min: true});
|
||||
}, Error, 'Mock Error');
|
||||
shouldThrow(
|
||||
function() {
|
||||
var Precompiler = require('../dist/cjs/precompiler');
|
||||
Handlebars.precompile = function() {
|
||||
return 'amd';
|
||||
};
|
||||
Precompiler.cli({ templates: [emptyTemplate], min: true });
|
||||
},
|
||||
Error,
|
||||
'Mock Error'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should output map', function() {
|
||||
Precompiler.cli({templates: [emptyTemplate], map: 'foo.js.map'});
|
||||
Precompiler.cli({ templates: [emptyTemplate], map: 'foo.js.map' });
|
||||
|
||||
equal(file, 'foo.js.map');
|
||||
equal(log.match(/sourceMappingURL=/g).length, 1);
|
||||
});
|
||||
|
||||
it('should output map', function() {
|
||||
Precompiler.cli({templates: [emptyTemplate], min: true, map: 'foo.js.map'});
|
||||
Precompiler.cli({
|
||||
templates: [emptyTemplate],
|
||||
min: true,
|
||||
map: 'foo.js.map'
|
||||
});
|
||||
|
||||
equal(file, 'foo.js.map');
|
||||
equal(log.match(/sourceMappingURL=/g).length, 1);
|
||||
@@ -225,35 +298,48 @@ describe('precompiler', function() {
|
||||
|
||||
describe('#loadTemplates', function() {
|
||||
it('should throw on missing template', function(done) {
|
||||
Precompiler.loadTemplates({files: ['foo']}, function(err) {
|
||||
Precompiler.loadTemplates({ files: ['foo'] }, function(err) {
|
||||
equal(err.message, 'Unable to open template file "foo"');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should enumerate directories by extension', function(done) {
|
||||
Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'hbs'}, function(err, opts) {
|
||||
equal(opts.templates.length, 1);
|
||||
equal(opts.templates[0].name, 'example_2');
|
||||
done(err);
|
||||
});
|
||||
Precompiler.loadTemplates(
|
||||
{ files: [__dirname + '/artifacts'], extension: 'hbs' },
|
||||
function(err, opts) {
|
||||
equal(opts.templates.length, 1);
|
||||
equal(opts.templates[0].name, 'example_2');
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
it('should enumerate all templates by extension', function(done) {
|
||||
Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'handlebars'}, function(err, opts) {
|
||||
equal(opts.templates.length, 3);
|
||||
equal(opts.templates[0].name, 'bom');
|
||||
equal(opts.templates[1].name, 'empty');
|
||||
equal(opts.templates[2].name, 'example_1');
|
||||
done(err);
|
||||
});
|
||||
Precompiler.loadTemplates(
|
||||
{ files: [__dirname + '/artifacts'], extension: 'handlebars' },
|
||||
function(err, opts) {
|
||||
equal(opts.templates.length, 3);
|
||||
equal(opts.templates[0].name, 'bom');
|
||||
equal(opts.templates[1].name, 'empty');
|
||||
equal(opts.templates[2].name, 'example_1');
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
it('should handle regular expression characters in extensions', function(done) {
|
||||
Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'hb(s'}, function(err) {
|
||||
// Success is not throwing
|
||||
done(err);
|
||||
});
|
||||
Precompiler.loadTemplates(
|
||||
{ files: [__dirname + '/artifacts'], extension: 'hb(s' },
|
||||
function(err) {
|
||||
// Success is not throwing
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
it('should handle BOM', function(done) {
|
||||
var opts = {files: [__dirname + '/artifacts/bom.handlebars'], extension: 'handlebars', bom: true};
|
||||
var opts = {
|
||||
files: [__dirname + '/artifacts/bom.handlebars'],
|
||||
extension: 'handlebars',
|
||||
bom: true
|
||||
};
|
||||
Precompiler.loadTemplates(opts, function(err, opts) {
|
||||
equal(opts.templates[0].source, 'a');
|
||||
done(err);
|
||||
@@ -261,7 +347,11 @@ describe('precompiler', function() {
|
||||
});
|
||||
|
||||
it('should handle different root', function(done) {
|
||||
var opts = {files: [__dirname + '/artifacts/empty.handlebars'], simple: true, root: 'foo/'};
|
||||
var opts = {
|
||||
files: [__dirname + '/artifacts/empty.handlebars'],
|
||||
simple: true,
|
||||
root: 'foo/'
|
||||
};
|
||||
Precompiler.loadTemplates(opts, function(err, opts) {
|
||||
equal(opts.templates[0].name, __dirname + '/artifacts/empty');
|
||||
done(err);
|
||||
@@ -269,7 +359,7 @@ describe('precompiler', function() {
|
||||
});
|
||||
|
||||
it('should accept string inputs', function(done) {
|
||||
var opts = {string: ''};
|
||||
var opts = { string: '' };
|
||||
Precompiler.loadTemplates(opts, function(err, opts) {
|
||||
equal(opts.templates[0].name, undefined);
|
||||
equal(opts.templates[0].source, '');
|
||||
@@ -277,7 +367,7 @@ describe('precompiler', function() {
|
||||
});
|
||||
});
|
||||
it('should accept string array inputs', function(done) {
|
||||
var opts = {string: ['', 'bar'], name: ['beep', 'boop']};
|
||||
var opts = { string: ['', 'bar'], name: ['beep', 'boop'] };
|
||||
Precompiler.loadTemplates(opts, function(err, opts) {
|
||||
equal(opts.templates[0].name, 'beep');
|
||||
equal(opts.templates[0].source, '');
|
||||
@@ -288,7 +378,7 @@ describe('precompiler', function() {
|
||||
});
|
||||
it('should accept stdin input', function(done) {
|
||||
var stdin = require('mock-stdin').stdin();
|
||||
Precompiler.loadTemplates({string: '-'}, function(err, opts) {
|
||||
Precompiler.loadTemplates({ string: '-' }, function(err, opts) {
|
||||
equal(opts.templates[0].source, 'foo');
|
||||
done(err);
|
||||
});
|
||||
@@ -297,9 +387,12 @@ describe('precompiler', function() {
|
||||
stdin.end();
|
||||
});
|
||||
it('error on name missing', function(done) {
|
||||
var opts = {string: ['', 'bar']};
|
||||
var opts = { string: ['', 'bar'] };
|
||||
Precompiler.loadTemplates(opts, function(err) {
|
||||
equal(err.message, 'Number of names did not match the number of string inputs');
|
||||
equal(
|
||||
err.message,
|
||||
'Number of names did not match the number of string inputs'
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
+222
-107
@@ -1,93 +1,134 @@
|
||||
describe('Regressions', function() {
|
||||
it('GH-94: Cannot read property of undefined', function() {
|
||||
var data = {
|
||||
'books': [{
|
||||
'title': 'The origin of species',
|
||||
'author': {
|
||||
'name': 'Charles Darwin'
|
||||
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}}';
|
||||
shouldCompileTo(string, data, 'The origin of speciesCharles DarwinLazarillo de Tormes',
|
||||
'Renders without an undefined property error');
|
||||
shouldCompileTo(
|
||||
string,
|
||||
data,
|
||||
'The origin of speciesCharles DarwinLazarillo de Tormes',
|
||||
'Renders without an undefined property error'
|
||||
);
|
||||
});
|
||||
|
||||
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");
|
||||
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"
|
||||
);
|
||||
});
|
||||
|
||||
it('GH-158: Using array index twice, breaks the template', function() {
|
||||
var string = '{{arr.[0]}}, {{arr.[1]}}';
|
||||
var data = { 'arr': [1, 2] };
|
||||
var data = { arr: [1, 2] };
|
||||
|
||||
shouldCompileTo(string, data, '1, 2', 'it works as expected');
|
||||
});
|
||||
|
||||
it("bug reported by @fat where lambdas weren't being properly resolved", function() {
|
||||
var string = '<strong>This is a slightly more complicated {{thing}}.</strong>.\n'
|
||||
+ '{{! Just ignore this business. }}\n'
|
||||
+ 'Check this out:\n'
|
||||
+ '{{#hasThings}}\n'
|
||||
+ '<ul>\n'
|
||||
+ '{{#things}}\n'
|
||||
+ '<li class={{className}}>{{word}}</li>\n'
|
||||
+ '{{/things}}</ul>.\n'
|
||||
+ '{{/hasThings}}\n'
|
||||
+ '{{^hasThings}}\n'
|
||||
+ '\n'
|
||||
+ '<small>Nothing to check out...</small>\n'
|
||||
+ '{{/hasThings}}';
|
||||
var string =
|
||||
'<strong>This is a slightly more complicated {{thing}}.</strong>.\n' +
|
||||
'{{! Just ignore this business. }}\n' +
|
||||
'Check this out:\n' +
|
||||
'{{#hasThings}}\n' +
|
||||
'<ul>\n' +
|
||||
'{{#things}}\n' +
|
||||
'<li class={{className}}>{{word}}</li>\n' +
|
||||
'{{/things}}</ul>.\n' +
|
||||
'{{/hasThings}}\n' +
|
||||
'{{^hasThings}}\n' +
|
||||
'\n' +
|
||||
'<small>Nothing to check out...</small>\n' +
|
||||
'{{/hasThings}}';
|
||||
var data = {
|
||||
thing: function() {
|
||||
return 'blah';
|
||||
},
|
||||
things: [
|
||||
{className: 'one', word: '@fat'},
|
||||
{className: 'two', word: '@dhg'},
|
||||
{className: 'three', word: '@sayrer'}
|
||||
{ className: 'one', word: '@fat' },
|
||||
{ className: 'two', word: '@dhg' },
|
||||
{ className: 'three', word: '@sayrer' }
|
||||
],
|
||||
hasThings: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var output = '<strong>This is a slightly more complicated blah.</strong>.\n'
|
||||
+ 'Check this out:\n'
|
||||
+ '<ul>\n'
|
||||
+ '<li class=one>@fat</li>\n'
|
||||
+ '<li class=two>@dhg</li>\n'
|
||||
+ '<li class=three>@sayrer</li>\n'
|
||||
+ '</ul>.\n';
|
||||
var output =
|
||||
'<strong>This is a slightly more complicated blah.</strong>.\n' +
|
||||
'Check this out:\n' +
|
||||
'<ul>\n' +
|
||||
'<li class=one>@fat</li>\n' +
|
||||
'<li class=two>@dhg</li>\n' +
|
||||
'<li class=three>@sayrer</li>\n' +
|
||||
'</ul>.\n';
|
||||
shouldCompileTo(string, data, output);
|
||||
});
|
||||
|
||||
it('GH-408: Multiple loops fail', function() {
|
||||
var context = [
|
||||
{ name: 'John Doe', location: { city: 'Chicago' } },
|
||||
{ name: 'Jane Doe', location: { city: 'New York'} }
|
||||
{ name: 'Jane Doe', location: { city: 'New York' } }
|
||||
];
|
||||
|
||||
var template = CompilerContext.compile('{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}');
|
||||
var template = CompilerContext.compile(
|
||||
'{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}'
|
||||
);
|
||||
|
||||
var result = template(context);
|
||||
equals(result, 'John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe', 'It should output multiple times');
|
||||
equals(
|
||||
result,
|
||||
'John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe',
|
||||
'It should output multiple times'
|
||||
);
|
||||
});
|
||||
|
||||
it('GS-428: Nested if else rendering', function() {
|
||||
var succeedingTemplate = '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
||||
var failingTemplate = '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
||||
var succeedingTemplate =
|
||||
'{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
||||
var failingTemplate =
|
||||
'{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
||||
|
||||
var helpers = {
|
||||
blk: function(block) { return block.fn(''); },
|
||||
inverse: function(block) { return block.inverse(''); }
|
||||
blk: function(block) {
|
||||
return block.fn('');
|
||||
},
|
||||
inverse: function(block) {
|
||||
return block.inverse('');
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo(succeedingTemplate, [{}, helpers], ' Expected ');
|
||||
@@ -95,7 +136,7 @@ describe('Regressions', function() {
|
||||
});
|
||||
|
||||
it('GH-458: Scoped this identifier', function() {
|
||||
shouldCompileTo('{{./foo}}', {foo: 'bar'}, 'bar');
|
||||
shouldCompileTo('{{./foo}}', { foo: 'bar' }, 'bar');
|
||||
});
|
||||
|
||||
it('GH-375: Unicode line terminators', function() {
|
||||
@@ -104,11 +145,11 @@ describe('Regressions', function() {
|
||||
|
||||
it('GH-534: Object prototype aliases', function() {
|
||||
/* eslint-disable no-extend-native */
|
||||
Object.prototype[0xD834] = true;
|
||||
Object.prototype[0xd834] = true;
|
||||
|
||||
shouldCompileTo('{{foo}}', { foo: 'bar' }, 'bar');
|
||||
|
||||
delete Object.prototype[0xD834];
|
||||
delete Object.prototype[0xd834];
|
||||
/* eslint-enable no-extend-native */
|
||||
});
|
||||
|
||||
@@ -123,34 +164,46 @@ describe('Regressions', function() {
|
||||
|
||||
it('GH-676: Using array in escaping mustache fails', function() {
|
||||
var string = '{{arr}}';
|
||||
var data = { 'arr': [1, 2] };
|
||||
var data = { arr: [1, 2] };
|
||||
|
||||
shouldCompileTo(string, data, data.arr.toString(), 'it works as expected');
|
||||
});
|
||||
|
||||
it('Mustache man page', function() {
|
||||
var string = 'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}';
|
||||
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
|
||||
name: 'Chris',
|
||||
value: 10000,
|
||||
taxed_value: 10000 - 10000 * 0.4,
|
||||
in_ca: true
|
||||
};
|
||||
|
||||
shouldCompileTo(string, data, 'Hello Chris. You have just won $10000! Well, $6000, after taxes.', 'the hello world mustache example works');
|
||||
shouldCompileTo(
|
||||
string,
|
||||
data,
|
||||
'Hello Chris. You have just won $10000! Well, $6000, after taxes.',
|
||||
'the hello world mustache example works'
|
||||
);
|
||||
});
|
||||
|
||||
it('GH-731: zero context rendering', function() {
|
||||
shouldCompileTo('{{#foo}} This is {{bar}} ~ {{/foo}}', {foo: 0, bar: 'OK'}, ' This is ~ ');
|
||||
shouldCompileTo(
|
||||
'{{#foo}} This is {{bar}} ~ {{/foo}}',
|
||||
{ foo: 0, bar: 'OK' },
|
||||
' This is ~ '
|
||||
);
|
||||
});
|
||||
|
||||
it('GH-820: zero pathed rendering', function() {
|
||||
shouldCompileTo('{{foo.bar}}', {foo: 0}, '');
|
||||
shouldCompileTo('{{foo.bar}}', { foo: 0 }, '');
|
||||
});
|
||||
|
||||
it('GH-837: undefined values for helpers', function() {
|
||||
var helpers = {
|
||||
str: function(value) { return value + ''; }
|
||||
str: function(value) {
|
||||
return value + '';
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo('{{str bar.baz}}', [{}, helpers], 'undefined');
|
||||
@@ -159,15 +212,13 @@ describe('Regressions', function() {
|
||||
it('GH-926: Depths and de-dupe', function() {
|
||||
var context = {
|
||||
name: 'foo',
|
||||
data: [
|
||||
1
|
||||
],
|
||||
notData: [
|
||||
1
|
||||
]
|
||||
data: [1],
|
||||
notData: [1]
|
||||
};
|
||||
|
||||
var template = CompilerContext.compile('{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}');
|
||||
var template = CompilerContext.compile(
|
||||
'{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}'
|
||||
);
|
||||
|
||||
var result = template(context);
|
||||
equals(result, 'foo');
|
||||
@@ -176,11 +227,15 @@ describe('Regressions', function() {
|
||||
it('GH-1021: Each empty string key', function() {
|
||||
var data = {
|
||||
'': 'foo',
|
||||
'name': 'Chris',
|
||||
'value': 10000
|
||||
name: 'Chris',
|
||||
value: 10000
|
||||
};
|
||||
|
||||
shouldCompileTo('{{#each data}}Key: {{@key}}\n{{/each}}', {data: data}, 'Key: \nKey: name\nKey: value\n');
|
||||
shouldCompileTo(
|
||||
'{{#each data}}Key: {{@key}}\n{{/each}}',
|
||||
{ data: data },
|
||||
'Key: \nKey: name\nKey: value\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('GH-1054: Should handle simple safe string responses', function() {
|
||||
@@ -194,18 +249,27 @@ describe('Regressions', function() {
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileToWithPartials(root, [{}, helpers, partials], true, '<partial>');
|
||||
shouldCompileToWithPartials(
|
||||
root,
|
||||
[{}, helpers, partials],
|
||||
true,
|
||||
'<partial>'
|
||||
);
|
||||
});
|
||||
|
||||
it('GH-1065: Sparse arrays', function() {
|
||||
var array = [];
|
||||
array[1] = 'foo';
|
||||
array[3] = 'bar';
|
||||
shouldCompileTo('{{#each array}}{{@index}}{{.}}{{/each}}', {array: array}, '1foo3bar');
|
||||
shouldCompileTo(
|
||||
'{{#each array}}{{@index}}{{.}}{{/each}}',
|
||||
{ array: array },
|
||||
'1foo3bar'
|
||||
);
|
||||
});
|
||||
|
||||
it('GH-1093: Undefined helper context', function() {
|
||||
var obj = {foo: undefined, bar: 'bat'};
|
||||
var obj = { foo: undefined, bar: 'bat' };
|
||||
var helpers = {
|
||||
helper: function() {
|
||||
// It's valid to execute a block against an undefined context, but
|
||||
@@ -216,28 +280,45 @@ describe('Regressions', function() {
|
||||
}
|
||||
}
|
||||
// And to make IE happy, check for the known string as length is not enumerated.
|
||||
return (this === 'bat' ? 'found' : 'not');
|
||||
return this === 'bat' ? 'found' : 'not';
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo('{{#each obj}}{{{helper}}}{{.}}{{/each}}', [{obj: obj}, helpers], 'notfoundbat');
|
||||
shouldCompileTo(
|
||||
'{{#each obj}}{{{helper}}}{{.}}{{/each}}',
|
||||
[{ obj: obj }, helpers],
|
||||
'notfoundbat'
|
||||
);
|
||||
});
|
||||
|
||||
it('should support multiple levels of inline partials', function() {
|
||||
var string = '{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}';
|
||||
var string =
|
||||
'{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}';
|
||||
var partials = {
|
||||
doctype: 'doctype{{> content}}',
|
||||
layout: '{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}'
|
||||
layout:
|
||||
'{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}'
|
||||
};
|
||||
shouldCompileToWithPartials(string, [{}, {}, partials], true, 'doctypelayoutsubcontent');
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[{}, {}, partials],
|
||||
true,
|
||||
'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}}'
|
||||
layout:
|
||||
'{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}'
|
||||
};
|
||||
shouldCompileToWithPartials(string, [{}, {}, partials], true, 'doctypelayoutsubcontent');
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[{}, {}, partials],
|
||||
true,
|
||||
'doctypelayoutsubcontent'
|
||||
);
|
||||
});
|
||||
it('GH-1099: should support greater than 3 nested levels of inline partials', function() {
|
||||
var string = '{{#> layout}}Outer{{/layout}}';
|
||||
@@ -249,51 +330,63 @@ describe('Regressions', function() {
|
||||
});
|
||||
|
||||
it('GH-1135 : Context handling within each iteration', function() {
|
||||
var obj = {array: [1], name: 'John'};
|
||||
var obj = { array: [1], name: 'John' };
|
||||
var helpers = {
|
||||
myif: function(conditional, options) {
|
||||
if (conditional) {
|
||||
return options.fn(this);
|
||||
return options.fn(this);
|
||||
} else {
|
||||
return options.inverse(this);
|
||||
return options.inverse(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo(
|
||||
'{{#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 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'
|
||||
);
|
||||
});
|
||||
|
||||
it('GH-1186: Support block params for existing programs', function() {
|
||||
var string =
|
||||
'{{#*inline "test"}}{{> @partial-block }}{{/inline}}'
|
||||
+ '{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}'
|
||||
+ '{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}';
|
||||
'{{#*inline "test"}}{{> @partial-block }}{{/inline}}' +
|
||||
'{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' +
|
||||
'{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}';
|
||||
|
||||
shouldCompileTo(string, { listOne: ['a'], listTwo: ['b']}, 'ab', '');
|
||||
shouldCompileTo(string, { listOne: ['a'], listTwo: ['b'] }, '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 ', '');
|
||||
var string =
|
||||
'{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}';
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ value: 'parent', list: [null, 'a'] },
|
||||
'parent=parent parent=parent ',
|
||||
''
|
||||
);
|
||||
});
|
||||
|
||||
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}}',
|
||||
partialWithBlock:
|
||||
'{{#if @partial-block}} block {{> @partial-block}} block {{/if}}',
|
||||
partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}'
|
||||
};
|
||||
shouldCompileToWithPartials(string, [{}, {}, partials], true, 'template block partial block template');
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[{}, {}, partials],
|
||||
true,
|
||||
'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() {
|
||||
|
||||
it('should compile and execute templates', function() {
|
||||
var newHandlebarsInstance = Handlebars.create();
|
||||
|
||||
@@ -301,31 +394,53 @@ describe('Regressions', function() {
|
||||
newHandlebarsInstance.registerHelper('loud', function(value) {
|
||||
return value.toUpperCase();
|
||||
});
|
||||
var result = newHandlebarsInstance.templates['test.hbs']({name: 'yehuda'});
|
||||
var result = newHandlebarsInstance.templates['test.hbs']({
|
||||
name: 'yehuda'
|
||||
});
|
||||
equals(result.trim(), 'YEHUDA');
|
||||
});
|
||||
|
||||
it('should call "helperMissing" if a helper is missing', function() {
|
||||
var newHandlebarsInstance = Handlebars.create();
|
||||
|
||||
shouldThrow(function() {
|
||||
registerTemplate(newHandlebarsInstance);
|
||||
newHandlebarsInstance.templates['test.hbs']({});
|
||||
}, Handlebars.Exception, 'Missing helper: "loud"');
|
||||
shouldThrow(
|
||||
function() {
|
||||
registerTemplate(newHandlebarsInstance);
|
||||
newHandlebarsInstance.templates['test.hbs']({});
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'Missing helper: "loud"'
|
||||
);
|
||||
});
|
||||
|
||||
// This is a only slightly modified precompiled templated from compiled with 4.2.1
|
||||
function registerTemplate(Handlebars) {
|
||||
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
templates['test.hbs'] = template({'compiler': [7, '>= 4.0.0'], 'main': function(container, depth0, helpers, partials, data) {
|
||||
return container.escapeExpression((helpers.loud || (depth0 && depth0.loud) || helpers.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}), (depth0 != null ? depth0.name : depth0), {'name': 'loud', 'hash': {}, 'data': data}))
|
||||
+ '\n\n';
|
||||
}, 'useData': true});
|
||||
var template = Handlebars.template,
|
||||
templates = (Handlebars.templates = Handlebars.templates || {});
|
||||
templates['test.hbs'] = template({
|
||||
compiler: [7, '>= 4.0.0'],
|
||||
main: function(container, depth0, helpers, partials, data) {
|
||||
return (
|
||||
container.escapeExpression(
|
||||
(
|
||||
helpers.loud ||
|
||||
(depth0 && depth0.loud) ||
|
||||
helpers.helperMissing
|
||||
).call(
|
||||
depth0 != null ? depth0 : container.nullContext || {},
|
||||
depth0 != null ? depth0.name : depth0,
|
||||
{ name: 'loud', hash: {}, data: data }
|
||||
)
|
||||
) + '\n\n'
|
||||
);
|
||||
},
|
||||
useData: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow hash with protected array names', function() {
|
||||
var obj = {array: [1], name: 'John'};
|
||||
var obj = { array: [1], name: 'John' };
|
||||
var helpers = {
|
||||
helpa: function(options) {
|
||||
return options.hash.length;
|
||||
@@ -352,7 +467,7 @@ describe('Regressions', function() {
|
||||
it('should only compile global partials once', function() {
|
||||
var templateSpy = sinon.spy(newHandlebarsInstance, 'template');
|
||||
newHandlebarsInstance.registerPartial({
|
||||
'dude': 'I am a partial'
|
||||
dude: 'I am a partial'
|
||||
});
|
||||
var string = 'Dudes: {{> dude}} {{> dude}}';
|
||||
newHandlebarsInstance.compile(string)(); // This should compile template + partial once
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ if (typeof require !== 'undefined' && require.extensions['.handlebars']) {
|
||||
equal(template, require('./artifacts/example_1.handlebars'));
|
||||
|
||||
var expected = 'foo\n';
|
||||
var result = template({foo: 'foo'});
|
||||
var result = template({ foo: 'foo' });
|
||||
|
||||
equal(result, expected);
|
||||
});
|
||||
@@ -15,7 +15,7 @@ if (typeof require !== 'undefined' && require.extensions['.handlebars']) {
|
||||
equal(template, require('./artifacts/example_2.hbs'));
|
||||
|
||||
var expected = 'Hello, World!\n';
|
||||
var result = template({name: 'World'});
|
||||
var result = template({ name: 'World' });
|
||||
|
||||
equal(result, expected);
|
||||
});
|
||||
|
||||
+75
-43
@@ -1,34 +1,58 @@
|
||||
describe('runtime', function() {
|
||||
describe('#template', function() {
|
||||
it('should throw on invalid templates', function() {
|
||||
shouldThrow(function() {
|
||||
Handlebars.template({});
|
||||
}, Error, 'Unknown template object: object');
|
||||
shouldThrow(function() {
|
||||
Handlebars.template();
|
||||
}, Error, 'Unknown template object: undefined');
|
||||
shouldThrow(function() {
|
||||
Handlebars.template('');
|
||||
}, Error, 'Unknown template object: string');
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.template({});
|
||||
},
|
||||
Error,
|
||||
'Unknown template object: object'
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.template();
|
||||
},
|
||||
Error,
|
||||
'Unknown template object: undefined'
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.template('');
|
||||
},
|
||||
Error,
|
||||
'Unknown template object: string'
|
||||
);
|
||||
});
|
||||
it('should throw on version mismatch', function() {
|
||||
shouldThrow(function() {
|
||||
Handlebars.template({
|
||||
main: {},
|
||||
compiler: [Handlebars.COMPILER_REVISION + 1]
|
||||
});
|
||||
}, Error, /Template was precompiled with a newer version of Handlebars than the current runtime/);
|
||||
shouldThrow(function() {
|
||||
Handlebars.template({
|
||||
main: {},
|
||||
compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1]
|
||||
});
|
||||
}, Error, /Template was precompiled with an older version of Handlebars than the current runtime/);
|
||||
shouldThrow(function() {
|
||||
Handlebars.template({
|
||||
main: {}
|
||||
});
|
||||
}, Error, /Template was precompiled with an older version of Handlebars than the current runtime/);
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.template({
|
||||
main: {},
|
||||
compiler: [Handlebars.COMPILER_REVISION + 1]
|
||||
});
|
||||
},
|
||||
Error,
|
||||
/Template was precompiled with a newer version of Handlebars than the current runtime/
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.template({
|
||||
main: {},
|
||||
compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1]
|
||||
});
|
||||
},
|
||||
Error,
|
||||
/Template was precompiled with an older version of Handlebars than the current runtime/
|
||||
);
|
||||
shouldThrow(
|
||||
function() {
|
||||
Handlebars.template({
|
||||
main: {}
|
||||
});
|
||||
},
|
||||
Error,
|
||||
/Template was precompiled with an older version of Handlebars than the current runtime/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,34 +62,42 @@ describe('runtime', function() {
|
||||
}
|
||||
|
||||
it('should throw for depthed methods without depths', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{#foo}}{{../bar}}{{/foo}}');
|
||||
// Calling twice to hit the non-compiled case.
|
||||
template._setup({});
|
||||
template._setup({});
|
||||
template._child(1);
|
||||
}, Error, 'must pass parent depths');
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = Handlebars.compile('{{#foo}}{{../bar}}{{/foo}}');
|
||||
// Calling twice to hit the non-compiled case.
|
||||
template._setup({});
|
||||
template._setup({});
|
||||
template._child(1);
|
||||
},
|
||||
Error,
|
||||
'must pass parent depths'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw for block param methods without params', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{#foo as |foo|}}{{foo}}{{/foo}}');
|
||||
// Calling twice to hit the non-compiled case.
|
||||
template._setup({});
|
||||
template._setup({});
|
||||
template._child(1);
|
||||
}, Error, 'must pass block params');
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = Handlebars.compile('{{#foo as |foo|}}{{foo}}{{/foo}}');
|
||||
// Calling twice to hit the non-compiled case.
|
||||
template._setup({});
|
||||
template._setup({});
|
||||
template._child(1);
|
||||
},
|
||||
Error,
|
||||
'must pass block params'
|
||||
);
|
||||
});
|
||||
it('should expose child template', function() {
|
||||
var template = Handlebars.compile('{{#foo}}bar{{/foo}}');
|
||||
// Calling twice to hit the non-compiled case.
|
||||
// Calling twice to hit the non-compiled case.
|
||||
equal(template._child(1)(), 'bar');
|
||||
equal(template._child(1)(), 'bar');
|
||||
});
|
||||
it('should render depthed content', function() {
|
||||
var template = Handlebars.compile('{{#foo}}{{../bar}}{{/foo}}');
|
||||
// Calling twice to hit the non-compiled case.
|
||||
equal(template._child(1, undefined, [], [{bar: 'baz'}])(), 'baz');
|
||||
// Calling twice to hit the non-compiled case.
|
||||
equal(template._child(1, undefined, [], [{ bar: 'baz' }])(), 'baz');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+196
-132
@@ -1,144 +1,208 @@
|
||||
describe('security issues', function() {
|
||||
describe('GH-1495: Prevent Remote Code Execution via constructor', function() {
|
||||
it('should not allow constructors to be accessed', function() {
|
||||
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
describe('GH-1495: Prevent Remote Code Execution via constructor', function() {
|
||||
it('should not allow constructors to be accessed', function() {
|
||||
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
|
||||
expectTemplate('{{constructor.name}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function() {
|
||||
expectTemplate('{{lookup (lookup this (list "constructor")) "name"}}')
|
||||
.withInput({})
|
||||
.withHelper('list', function(element) {
|
||||
return [element];
|
||||
})
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
|
||||
it('should allow the "constructor" property to be accessed if it is enumerable', 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');
|
||||
});
|
||||
|
||||
it('should allow the "constructor" property to be accessed if it is enumerable', function() {
|
||||
shouldCompileTo('{{lookup (lookup this "constructor") "name"}}', {'constructor': {
|
||||
'name': 'here we go'
|
||||
}}, 'here we go');
|
||||
});
|
||||
|
||||
|
||||
it('should allow prototype properties that are not constructors', function() {
|
||||
function TestClass() {
|
||||
}
|
||||
|
||||
Object.defineProperty(TestClass.prototype, 'abc', {
|
||||
get: function() {
|
||||
return 'xyz';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
shouldCompileTo('{{#with this as |obj|}}{{obj.abc}}{{/with}}',
|
||||
new TestClass(), 'xyz');
|
||||
shouldCompileTo('{{#with this as |obj|}}{{lookup obj "abc"}}{{/with}}',
|
||||
new TestClass(), 'xyz');
|
||||
|
||||
});
|
||||
expectTemplate('{{constructor.name}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
describe('GH-1558: Prevent explicit call of helperMissing-helpers', function() {
|
||||
if (!Handlebars.compile) {
|
||||
return;
|
||||
it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function() {
|
||||
expectTemplate('{{lookup (lookup this (list "constructor")) "name"}}')
|
||||
.withInput({})
|
||||
.withHelper('list', function(element) {
|
||||
return [element];
|
||||
})
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('should allow the "constructor" property to be accessed if it is enumerable', 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'
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow the "constructor" property to be accessed if it is enumerable', function() {
|
||||
shouldCompileTo(
|
||||
'{{lookup (lookup this "constructor") "name"}}',
|
||||
{
|
||||
constructor: {
|
||||
name: 'here we go'
|
||||
}
|
||||
},
|
||||
'here we go'
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow prototype properties that are not constructors', function() {
|
||||
function TestClass() {}
|
||||
|
||||
Object.defineProperty(TestClass.prototype, 'abc', {
|
||||
get: function() {
|
||||
return 'xyz';
|
||||
}
|
||||
});
|
||||
|
||||
describe('without the option "allowExplicitCallOfHelperMissing"', function() {
|
||||
it('should throw an exception when calling "{{helperMissing}}" ', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{helperMissing}}');
|
||||
template({});
|
||||
}, Error);
|
||||
});
|
||||
it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{#helperMissing}}{{/helperMissing}}');
|
||||
template({});
|
||||
}, Error);
|
||||
});
|
||||
it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
||||
var functionCalls = [];
|
||||
expect(function() {
|
||||
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
||||
template({ fn: function() { functionCalls.push('called'); }});
|
||||
}).to.throw(Error);
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with the option "allowCallsToHelperMissing" set to true', function() {
|
||||
it('should not throw an exception when calling "{{helperMissing}}" ', 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" .}}');
|
||||
template({ fn: function() { functionCalls.push('called'); }}, {allowCallsToHelperMissing: true});
|
||||
equals(functionCalls.length, 1);
|
||||
});
|
||||
it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
||||
var template = Handlebars.compile('{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}');
|
||||
template({}, {allowCallsToHelperMissing: true});
|
||||
});
|
||||
});
|
||||
shouldCompileTo(
|
||||
'{{#with this as |obj|}}{{obj.abc}}{{/with}}',
|
||||
new TestClass(),
|
||||
'xyz'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{#with this as |obj|}}{{lookup obj "abc"}}{{/with}}',
|
||||
new TestClass(),
|
||||
'xyz'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GH-1563', function() {
|
||||
it('should not allow to access constructor after overriding via __defineGetter__', function() {
|
||||
if (({}).__defineGetter__ == null || ({}).__lookupGetter__ == null) {
|
||||
return this.skip(); // Browser does not support this exploit anyway
|
||||
describe('GH-1558: Prevent explicit call of helperMissing-helpers', function() {
|
||||
if (!Handlebars.compile) {
|
||||
return;
|
||||
}
|
||||
|
||||
describe('without the option "allowExplicitCallOfHelperMissing"', function() {
|
||||
it('should throw an exception when calling "{{helperMissing}}" ', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{helperMissing}}');
|
||||
template({});
|
||||
}, Error);
|
||||
});
|
||||
it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile(
|
||||
'{{#helperMissing}}{{/helperMissing}}'
|
||||
);
|
||||
template({});
|
||||
}, Error);
|
||||
});
|
||||
it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
||||
var functionCalls = [];
|
||||
expect(function() {
|
||||
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
||||
template({
|
||||
fn: function() {
|
||||
functionCalls.push('called');
|
||||
}
|
||||
expectTemplate('{{__defineGetter__ "undefined" valueOf }}' +
|
||||
'{{#with __lookupGetter__ }}' +
|
||||
'{{__defineGetter__ "propertyIsEnumerable" (this.bind (this.bind 1)) }}' +
|
||||
'{{constructor.name}}' +
|
||||
'{{/with}}')
|
||||
.withInput({})
|
||||
.toThrow(/Missing helper: "__defineGetter__"/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GH-1595', function() {
|
||||
it('properties, that are required to be enumerable', function() {
|
||||
expectTemplate('{{constructor}}').withInput({}).toCompileTo('');
|
||||
expectTemplate('{{__defineGetter__}}').withInput({}).toCompileTo('');
|
||||
expectTemplate('{{__defineSetter__}}').withInput({}).toCompileTo('');
|
||||
expectTemplate('{{__lookupGetter__}}').withInput({}).toCompileTo('');
|
||||
expectTemplate('{{__proto__}}').withInput({}).toCompileTo('');
|
||||
|
||||
expectTemplate('{{lookup "constructor"}}').withInput({}).toCompileTo('');
|
||||
expectTemplate('{{lookup "__defineGetter__"}}').withInput({}).toCompileTo('');
|
||||
expectTemplate('{{lookup "__defineSetter__"}}').withInput({}).toCompileTo('');
|
||||
expectTemplate('{{lookup "__lookupGetter__"}}').withInput({}).toCompileTo('');
|
||||
expectTemplate('{{lookup "__proto__"}}').withInput({}).toCompileTo('');
|
||||
});
|
||||
}).to.throw(Error);
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with the option "allowCallsToHelperMissing" set to true', function() {
|
||||
it('should not throw an exception when calling "{{helperMissing}}" ', 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" .}}');
|
||||
template(
|
||||
{
|
||||
fn: function() {
|
||||
functionCalls.push('called');
|
||||
}
|
||||
},
|
||||
{ allowCallsToHelperMissing: true }
|
||||
);
|
||||
equals(functionCalls.length, 1);
|
||||
});
|
||||
it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
||||
var template = Handlebars.compile(
|
||||
'{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}'
|
||||
);
|
||||
template({}, { allowCallsToHelperMissing: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GH-1563', function() {
|
||||
it('should not allow to access constructor after overriding via __defineGetter__', function() {
|
||||
if ({}.__defineGetter__ == null || {}.__lookupGetter__ == null) {
|
||||
return this.skip(); // Browser does not support this exploit anyway
|
||||
}
|
||||
expectTemplate(
|
||||
'{{__defineGetter__ "undefined" valueOf }}' +
|
||||
'{{#with __lookupGetter__ }}' +
|
||||
'{{__defineGetter__ "propertyIsEnumerable" (this.bind (this.bind 1)) }}' +
|
||||
'{{constructor.name}}' +
|
||||
'{{/with}}'
|
||||
)
|
||||
.withInput({})
|
||||
.toThrow(/Missing helper: "__defineGetter__"/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GH-1595', function() {
|
||||
it('properties, that are required to be enumerable', function() {
|
||||
expectTemplate('{{constructor}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
expectTemplate('{{__defineGetter__}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
expectTemplate('{{__defineSetter__}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
expectTemplate('{{__lookupGetter__}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
expectTemplate('{{__proto__}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
|
||||
expectTemplate('{{lookup "constructor"}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
expectTemplate('{{lookup "__defineGetter__"}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
expectTemplate('{{lookup "__defineSetter__"}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
expectTemplate('{{lookup "__lookupGetter__"}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
expectTemplate('{{lookup "__proto__"}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+15
-9
@@ -1,7 +1,7 @@
|
||||
try {
|
||||
if (typeof define !== 'function' || !define.amd) {
|
||||
var SourceMap = require('source-map'),
|
||||
SourceMapConsumer = SourceMap.SourceMapConsumer;
|
||||
SourceMapConsumer = SourceMap.SourceMapConsumer;
|
||||
}
|
||||
} catch (err) {
|
||||
/* NOP for in browser */
|
||||
@@ -13,22 +13,28 @@ describe('source-map', function() {
|
||||
}
|
||||
|
||||
it('should safely include source map info', function() {
|
||||
var template = Handlebars.precompile('{{hello}}', {destName: 'dest.js', srcName: 'src.hbs'});
|
||||
var template = Handlebars.precompile('{{hello}}', {
|
||||
destName: 'dest.js',
|
||||
srcName: 'src.hbs'
|
||||
});
|
||||
|
||||
equal(!!template.code, true);
|
||||
equal(!!template.map, !CompilerContext.browser);
|
||||
});
|
||||
it('should map source properly', function() {
|
||||
var templateSource = ' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}',
|
||||
template = Handlebars.precompile(templateSource, {destName: 'dest.js', srcName: 'src.hbs'});
|
||||
var templateSource =
|
||||
' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}',
|
||||
template = Handlebars.precompile(templateSource, {
|
||||
destName: 'dest.js',
|
||||
srcName: 'src.hbs'
|
||||
});
|
||||
|
||||
if (template.map) {
|
||||
var consumer = new SourceMapConsumer(template.map),
|
||||
lines = template.code.split('\n'),
|
||||
srcLines = templateSource.split('\n'),
|
||||
|
||||
generated = grepLine('" b"', lines),
|
||||
source = grepLine(' b', srcLines);
|
||||
lines = template.code.split('\n'),
|
||||
srcLines = templateSource.split('\n'),
|
||||
generated = grepLine('" b"', lines),
|
||||
source = grepLine(' b', srcLines);
|
||||
|
||||
var mapped = consumer.originalPositionFor(generated);
|
||||
equal(mapped.line, source.line);
|
||||
|
||||
+26
-14
@@ -5,11 +5,11 @@ describe('spec', function() {
|
||||
}
|
||||
|
||||
var _ = require('underscore'),
|
||||
fs = require('fs');
|
||||
fs = require('fs');
|
||||
|
||||
var specDir = __dirname + '/mustache/specs/';
|
||||
var specs = _.filter(fs.readdirSync(specDir), function(name) {
|
||||
return (/.*\.json$/).test(name);
|
||||
return /.*\.json$/.test(name);
|
||||
});
|
||||
|
||||
_.each(specs, function(name) {
|
||||
@@ -17,16 +17,17 @@ describe('spec', function() {
|
||||
_.each(spec.tests, function(test) {
|
||||
// Our lambda implementation knowingly deviates from the optional Mustace lambda spec
|
||||
// We also do not support alternative delimeters
|
||||
if (name === '~lambdas.json'
|
||||
|
||||
// We also choose to throw if paritals are not found
|
||||
|| (name === 'partials.json' && test.name === 'Failed Lookup')
|
||||
|
||||
// We nest the entire response from partials, not just the literals
|
||||
|| (name === 'partials.json' && test.name === 'Standalone Indentation')
|
||||
|
||||
|| (/\{\{=/).test(test.template)
|
||||
|| _.any(test.partials, function(partial) { return (/\{\{=/).test(partial); })) {
|
||||
if (
|
||||
name === '~lambdas.json' ||
|
||||
// We also choose to throw if paritals are not found
|
||||
(name === 'partials.json' && test.name === 'Failed Lookup') ||
|
||||
// We nest the entire response from partials, not just the literals
|
||||
(name === 'partials.json' && test.name === 'Standalone Indentation') ||
|
||||
/\{\{=/.test(test.template) ||
|
||||
_.any(test.partials, function(partial) {
|
||||
return /\{\{=/.test(partial);
|
||||
})
|
||||
) {
|
||||
it.skip(name + ' - ' + test.name);
|
||||
return;
|
||||
}
|
||||
@@ -40,9 +41,20 @@ describe('spec', function() {
|
||||
}
|
||||
it(name + ' - ' + test.name, function() {
|
||||
if (test.partials) {
|
||||
shouldCompileToWithPartials(test.template, [data, {}, test.partials, true], true, test.expected, test.desc + ' "' + test.template + '"');
|
||||
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 + '"');
|
||||
shouldCompileTo(
|
||||
test.template,
|
||||
[data, {}, {}, true],
|
||||
test.expected,
|
||||
test.desc + ' "' + test.template + '"'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+124
-53
@@ -3,43 +3,58 @@ 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});
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{hello}}', { strict: true });
|
||||
|
||||
template({});
|
||||
}, Exception, /"hello" not defined in/);
|
||||
template({});
|
||||
},
|
||||
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');
|
||||
var template = CompilerContext.compile('{{hello.bar}}', { strict: true });
|
||||
equals(template({ hello: { bar: 'foo' } }), 'foo');
|
||||
|
||||
shouldThrow(function() {
|
||||
template({hello: {}});
|
||||
}, Exception, /"bar" not defined in/);
|
||||
shouldThrow(
|
||||
function() {
|
||||
template({ hello: {} });
|
||||
},
|
||||
Exception,
|
||||
/"bar" not defined in/
|
||||
);
|
||||
});
|
||||
it('should handle explicit undefined', function() {
|
||||
var template = CompilerContext.compile('{{hello.bar}}', {strict: true});
|
||||
var template = CompilerContext.compile('{{hello.bar}}', { strict: true });
|
||||
|
||||
equals(template({hello: {bar: undefined}}), '');
|
||||
equals(template({ hello: { bar: undefined } }), '');
|
||||
});
|
||||
it('should error on missing property lookup in known helpers mode', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{hello}}', {strict: true, knownHelpersOnly: true});
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{hello}}', {
|
||||
strict: true,
|
||||
knownHelpersOnly: true
|
||||
});
|
||||
|
||||
template({});
|
||||
}, Exception, /"hello" not defined in/);
|
||||
template({});
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
});
|
||||
it('should error on missing context', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{hello}}', {strict: true});
|
||||
var template = CompilerContext.compile('{{hello}}', { strict: true });
|
||||
|
||||
template();
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it('should error on missing data lookup', function() {
|
||||
var template = CompilerContext.compile('{{@hello}}', {strict: true});
|
||||
equals(template(undefined, {data: {hello: 'foo'}}), 'foo');
|
||||
var template = CompilerContext.compile('{{@hello}}', { strict: true });
|
||||
equals(template(undefined, { data: { hello: 'foo' } }), 'foo');
|
||||
|
||||
shouldThrow(function() {
|
||||
template();
|
||||
@@ -47,45 +62,81 @@ describe('strict', function() {
|
||||
});
|
||||
|
||||
it('should not run helperMissing for helper calls', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{hello foo}}', {strict: true});
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{hello foo}}', {
|
||||
strict: true
|
||||
});
|
||||
|
||||
template({foo: true});
|
||||
}, Exception, /"hello" not defined in/);
|
||||
template({ foo: true });
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{#hello foo}}{{/hello}}', {strict: true});
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{#hello foo}}{{/hello}}', {
|
||||
strict: true
|
||||
});
|
||||
|
||||
template({foo: true});
|
||||
}, Exception, /"hello" not defined in/);
|
||||
template({ foo: true });
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
});
|
||||
it('should throw on ambiguous blocks', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{#hello}}{{/hello}}', {strict: true});
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{#hello}}{{/hello}}', {
|
||||
strict: true
|
||||
});
|
||||
|
||||
template({});
|
||||
}, Exception, /"hello" not defined in/);
|
||||
template({});
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{^hello}}{{/hello}}', {strict: true});
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{^hello}}{{/hello}}', {
|
||||
strict: true
|
||||
});
|
||||
|
||||
template({});
|
||||
}, Exception, /"hello" not defined in/);
|
||||
template({});
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{#hello.bar}}{{/hello.bar}}', {strict: true});
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#hello.bar}}{{/hello.bar}}',
|
||||
{ strict: true }
|
||||
);
|
||||
|
||||
template({hello: {}});
|
||||
}, Exception, /"bar" not defined in/);
|
||||
template({ hello: {} });
|
||||
},
|
||||
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});
|
||||
var template = CompilerContext.compile(
|
||||
'{{#unless foo}}success{{/unless}}',
|
||||
{ strict: true }
|
||||
);
|
||||
equals(template({}), 'success');
|
||||
});
|
||||
|
||||
it('should allow undefined hash when passed to helpers', function() {
|
||||
var template = CompilerContext.compile('{{helper value=@foo}}', {strict: true});
|
||||
var template = CompilerContext.compile('{{helper value=@foo}}', {
|
||||
strict: true
|
||||
});
|
||||
var helpers = {
|
||||
helper: function(options) {
|
||||
equals('value' in options.hash, true);
|
||||
@@ -93,19 +144,27 @@ describe('strict', function() {
|
||||
return 'success';
|
||||
}
|
||||
};
|
||||
equals(template({}, {helpers: helpers}), 'success');
|
||||
equals(template({}, { helpers: helpers }), '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');
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('\n\n\n {{hello}}', {
|
||||
strict: true
|
||||
});
|
||||
template({});
|
||||
},
|
||||
Exception,
|
||||
'"hello" not defined in [object Object] - 4:5'
|
||||
);
|
||||
});
|
||||
|
||||
it('should error contains correct location properties on missing property lookup', function() {
|
||||
try {
|
||||
var template = CompilerContext.compile('\n\n\n {{hello}}', {strict: true});
|
||||
var template = CompilerContext.compile('\n\n\n {{hello}}', {
|
||||
strict: true
|
||||
});
|
||||
template({});
|
||||
} catch (error) {
|
||||
equals(error.lineNumber, 4);
|
||||
@@ -118,25 +177,33 @@ describe('strict', function() {
|
||||
|
||||
describe('assume objects', function() {
|
||||
it('should ignore missing property', function() {
|
||||
var template = CompilerContext.compile('{{hello}}', {assumeObjects: true});
|
||||
var template = CompilerContext.compile('{{hello}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
equal(template({}), '');
|
||||
});
|
||||
it('should ignore missing child', function() {
|
||||
var template = CompilerContext.compile('{{hello.bar}}', {assumeObjects: true});
|
||||
var template = CompilerContext.compile('{{hello.bar}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
equal(template({hello: {}}), '');
|
||||
equal(template({ hello: {} }), '');
|
||||
});
|
||||
it('should error on missing object', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{hello.bar}}', {assumeObjects: true});
|
||||
var template = CompilerContext.compile('{{hello.bar}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
template({});
|
||||
}, Error);
|
||||
});
|
||||
it('should error on missing context', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{hello}}', {assumeObjects: true});
|
||||
var template = CompilerContext.compile('{{hello}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
template();
|
||||
}, Error);
|
||||
@@ -144,14 +211,18 @@ describe('strict', function() {
|
||||
|
||||
it('should error on missing data lookup', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{@hello.bar}}', {assumeObjects: true});
|
||||
var template = CompilerContext.compile('{{@hello.bar}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
template();
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it('should execute blockHelperMissing', function() {
|
||||
var template = CompilerContext.compile('{{^hello}}foo{{/hello}}', {assumeObjects: true});
|
||||
var template = CompilerContext.compile('{{^hello}}foo{{/hello}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
equals(template({}), 'foo');
|
||||
});
|
||||
|
||||
+108
-38
@@ -1,6 +1,8 @@
|
||||
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}}', {stringParams: true});
|
||||
var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {
|
||||
stringParams: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun) {
|
||||
@@ -8,57 +10,99 @@ describe('string params mode', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, {helpers: helpers});
|
||||
var result = template({}, { helpers: helpers });
|
||||
|
||||
equals(result, 'HELP ME MY BOSS is.a slave.driver', 'String parameters output');
|
||||
equals(
|
||||
result,
|
||||
'HELP ME MY BOSS is.a slave.driver',
|
||||
'String parameters output'
|
||||
);
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile(
|
||||
'{{#wycats is.a slave.driver}}help :({{/wycats}}',
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
return 'HELP ME MY BOSS ' + passiveVoice + ' ' +
|
||||
noun + ': ' + options.fn(this);
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
noun +
|
||||
': ' +
|
||||
options.fn(this)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, {helpers: helpers});
|
||||
var result = template({}, { helpers: helpers });
|
||||
|
||||
equals(result, 'HELP ME MY BOSS is.a slave.driver: help :(', 'String parameters output');
|
||||
equals(
|
||||
result,
|
||||
'HELP ME MY BOSS is.a slave.driver: help :(',
|
||||
'String parameters output'
|
||||
);
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile(
|
||||
'{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}',
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
tomdale: function(desire, noun, options) {
|
||||
return 'STOP ME FROM READING HACKER NEWS I ' +
|
||||
options.contexts[0][desire] + ' ' + noun;
|
||||
return (
|
||||
'STOP ME FROM READING HACKER NEWS I ' +
|
||||
options.contexts[0][desire] +
|
||||
' ' +
|
||||
noun
|
||||
);
|
||||
},
|
||||
|
||||
'with': function(context, options) {
|
||||
with: function(context, options) {
|
||||
return options.fn(options.contexts[0][context]);
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({
|
||||
dale: {},
|
||||
var result = template(
|
||||
{
|
||||
dale: {},
|
||||
|
||||
need: 'need-a'
|
||||
}, {helpers: helpers});
|
||||
need: 'need-a'
|
||||
},
|
||||
{ helpers: helpers }
|
||||
);
|
||||
|
||||
equals(result, 'STOP ME FROM READING HACKER NEWS I need-a dad.joke', 'Proper context variable output');
|
||||
equals(
|
||||
result,
|
||||
'STOP ME FROM READING HACKER NEWS I need-a dad.joke',
|
||||
'Proper context variable output'
|
||||
);
|
||||
});
|
||||
|
||||
it('information about the types is passed along', function() {
|
||||
var template = CompilerContext.compile("{{tomdale 'need' dad.joke true false}}", { stringParams: true });
|
||||
var template = CompilerContext.compile(
|
||||
"{{tomdale 'need' dad.joke true false}}",
|
||||
{ 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(
|
||||
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');
|
||||
@@ -72,7 +116,10 @@ describe('string params mode', function() {
|
||||
});
|
||||
|
||||
it('hash parameters get type information', function() {
|
||||
var template = CompilerContext.compile("{{tomdale he.says desire='need' noun=dad.joke bool=true}}", { stringParams: true });
|
||||
var template = CompilerContext.compile(
|
||||
"{{tomdale he.says desire='need' noun=dad.joke bool=true}}",
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
tomdale: function(exclamation, options) {
|
||||
@@ -94,9 +141,12 @@ describe('string params mode', function() {
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile(
|
||||
"{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}",
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var context = {dale: {}};
|
||||
var context = { dale: {} };
|
||||
|
||||
var helpers = {
|
||||
tomdale: function(exclamation, options) {
|
||||
@@ -110,7 +160,7 @@ describe('string params mode', function() {
|
||||
equal(options.hash.bool, true);
|
||||
return 'Helper called';
|
||||
},
|
||||
'with': function(withContext, options) {
|
||||
with: function(withContext, options) {
|
||||
return options.fn(options.contexts[0][withContext]);
|
||||
}
|
||||
};
|
||||
@@ -120,34 +170,52 @@ describe('string params mode', function() {
|
||||
});
|
||||
|
||||
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 template = CompilerContext.compile(
|
||||
'{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}',
|
||||
{ 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);
|
||||
return (
|
||||
'STOP ME FROM READING HACKER NEWS I ' +
|
||||
options.contexts[0][desire] +
|
||||
' ' +
|
||||
noun +
|
||||
' ' +
|
||||
options.fn(this)
|
||||
);
|
||||
},
|
||||
|
||||
'with': function(context, options) {
|
||||
with: function(context, options) {
|
||||
return options.fn(options.contexts[0][context]);
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({
|
||||
dale: {},
|
||||
var result = template(
|
||||
{
|
||||
dale: {},
|
||||
|
||||
need: 'need-a'
|
||||
}, {helpers: helpers});
|
||||
need: 'need-a'
|
||||
},
|
||||
{ helpers: helpers }
|
||||
);
|
||||
|
||||
equals(result, 'STOP ME FROM READING HACKER NEWS I need-a dad.joke wot', 'Proper context variable output');
|
||||
equals(
|
||||
result,
|
||||
'STOP ME FROM READING HACKER NEWS I need-a dad.joke wot',
|
||||
'Proper context variable output'
|
||||
);
|
||||
});
|
||||
|
||||
it('with nested block ambiguous', function() {
|
||||
var template = CompilerContext.compile('{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}', {stringParams: true});
|
||||
var template = CompilerContext.compile(
|
||||
'{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}',
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
'with': function() {
|
||||
with: function() {
|
||||
return 'WITH';
|
||||
},
|
||||
view: function() {
|
||||
@@ -155,12 +223,14 @@ describe('string params mode', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, {helpers: helpers});
|
||||
var result = template({}, { helpers: helpers });
|
||||
equals(result, 'WITH');
|
||||
});
|
||||
|
||||
it('should handle DATA', function() {
|
||||
var template = CompilerContext.compile('{{foo @bar}}', { stringParams: true });
|
||||
var template = CompilerContext.compile('{{foo @bar}}', {
|
||||
stringParams: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
foo: function(bar, options) {
|
||||
|
||||
+75
-21
@@ -31,7 +31,7 @@ describe('subexpressions', function() {
|
||||
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 context = { bar: 'LOL', baz: { bat: 'foo!', bar: 'bar!' } };
|
||||
var helpers = {
|
||||
blog: function(val, that, theOther) {
|
||||
return 'val is ' + val + ', ' + that + ' and ' + theOther;
|
||||
@@ -59,7 +59,7 @@ describe('subexpressions', function() {
|
||||
});
|
||||
|
||||
it('GH-800 : Complex subexpressions', function() {
|
||||
var context = {a: 'a', b: 'b', c: {c: 'c'}, d: 'd', e: {e: 'e'}};
|
||||
var context = { a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' } };
|
||||
var helpers = {
|
||||
dash: function(a, b) {
|
||||
return a + '-' + b;
|
||||
@@ -69,7 +69,11 @@ describe('subexpressions', function() {
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo("{{dash 'abc' (concat a b)}}", [context, helpers], 'abc-ab');
|
||||
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');
|
||||
@@ -122,24 +126,36 @@ describe('subexpressions', function() {
|
||||
});
|
||||
|
||||
it('multiple subexpressions in a hash', function() {
|
||||
var string = '{{input aria-label=(t "Name") placeholder=(t "Example User")}}';
|
||||
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('<input aria-label="' + ariaLabel + '" placeholder="' + placeholder + '" />');
|
||||
return new Handlebars.SafeString(
|
||||
'<input aria-label="' +
|
||||
ariaLabel +
|
||||
'" placeholder="' +
|
||||
placeholder +
|
||||
'" />'
|
||||
);
|
||||
},
|
||||
t: function(defaultString) {
|
||||
return new Handlebars.SafeString(defaultString);
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [{}, helpers], '<input aria-label="Name" placeholder="Example User" />');
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[{}, helpers],
|
||||
'<input aria-label="Name" placeholder="Example User" />'
|
||||
);
|
||||
});
|
||||
|
||||
it('multiple subexpressions in a hash with context', function() {
|
||||
var string = '{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}';
|
||||
var string =
|
||||
'{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}';
|
||||
|
||||
var context = {
|
||||
item: {
|
||||
@@ -153,44 +169,82 @@ describe('subexpressions', function() {
|
||||
var hash = options.hash;
|
||||
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
|
||||
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
|
||||
return new Handlebars.SafeString('<input aria-label="' + ariaLabel + '" placeholder="' + placeholder + '" />');
|
||||
return new Handlebars.SafeString(
|
||||
'<input aria-label="' +
|
||||
ariaLabel +
|
||||
'" placeholder="' +
|
||||
placeholder +
|
||||
'" />'
|
||||
);
|
||||
},
|
||||
t: function(defaultString) {
|
||||
return new Handlebars.SafeString(defaultString);
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [context, helpers], '<input aria-label="Name" placeholder="Example User" />');
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[context, helpers],
|
||||
'<input aria-label="Name" placeholder="Example User" />'
|
||||
);
|
||||
});
|
||||
|
||||
it('in string params mode,', function() {
|
||||
var template = CompilerContext.compile('{{snog (blorg foo x=y) yeah a=b}}', {stringParams: true});
|
||||
var template = CompilerContext.compile(
|
||||
'{{snog (blorg foo x=y) yeah a=b}}',
|
||||
{ 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');
|
||||
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');
|
||||
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 result = template({
|
||||
foo: {},
|
||||
yeah: {}
|
||||
}, {helpers: helpers});
|
||||
var result = template(
|
||||
{
|
||||
foo: {},
|
||||
yeah: {}
|
||||
},
|
||||
{ helpers: helpers }
|
||||
);
|
||||
|
||||
equals(result, 'fooyeah');
|
||||
});
|
||||
|
||||
it('as hashes in string params mode', function() {
|
||||
var template = CompilerContext.compile('{{blog fun=(bork)}}', {stringParams: true});
|
||||
var template = CompilerContext.compile('{{blog fun=(bork)}}', {
|
||||
stringParams: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
blog: function(options) {
|
||||
@@ -202,7 +256,7 @@ describe('subexpressions', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, {helpers: helpers});
|
||||
var result = template({}, { helpers: helpers });
|
||||
equals(result, 'val is BORK');
|
||||
});
|
||||
|
||||
|
||||
+395
-48
@@ -15,18 +15,18 @@ describe('Tokenizer', function() {
|
||||
|
||||
function tokenize(template) {
|
||||
var parser = Handlebars.Parser,
|
||||
lexer = parser.lexer;
|
||||
lexer = parser.lexer;
|
||||
|
||||
lexer.setInput(template);
|
||||
var out = [],
|
||||
token;
|
||||
token;
|
||||
|
||||
while ((token = lexer.lex())) {
|
||||
var result = parser.terminals_[token] || token;
|
||||
if (!result || result === 'EOF' || result === 'INVALID') {
|
||||
break;
|
||||
}
|
||||
out.push({name: result, text: lexer.yytext});
|
||||
out.push({ name: result, text: lexer.yytext });
|
||||
}
|
||||
|
||||
return out;
|
||||
@@ -55,7 +55,16 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('supports escaping delimiters', function() {
|
||||
var result = tokenize('{{foo}} \\{{bar}} {{baz}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
shouldBeToken(result[3], 'CONTENT', ' ');
|
||||
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
||||
@@ -63,7 +72,14 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('supports escaping multiple delimiters', function() {
|
||||
var result = tokenize('{{foo}} \\{{bar}} \\{{baz}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'CONTENT',
|
||||
'CONTENT'
|
||||
]);
|
||||
|
||||
shouldBeToken(result[3], 'CONTENT', ' ');
|
||||
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
||||
@@ -72,14 +88,35 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('supports escaping a triple stash', function() {
|
||||
var result = tokenize('{{foo}} \\{{{bar}}} {{baz}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
shouldBeToken(result[4], 'CONTENT', '{{{bar}}} ');
|
||||
});
|
||||
|
||||
it('supports escaping escape character', function() {
|
||||
var result = tokenize('{{foo}} \\\\{{bar}} {{baz}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||
shouldBeToken(result[5], 'ID', 'bar');
|
||||
@@ -87,7 +124,19 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('supports escaping multiple escape characters', function() {
|
||||
var result = tokenize('{{foo}} \\\\{{bar}} \\\\{{baz}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||
shouldBeToken(result[5], 'ID', 'bar');
|
||||
@@ -97,7 +146,18 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('supports escaped mustaches after escaped escape characters', function() {
|
||||
var result = tokenize('{{foo}} \\\\{{bar}} \\{{baz}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'CONTENT',
|
||||
'CONTENT'
|
||||
]);
|
||||
|
||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||
shouldBeToken(result[4], 'OPEN', '{{');
|
||||
@@ -108,7 +168,17 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('supports escaped escape characters after escaped mustaches', function() {
|
||||
var result = tokenize('{{foo}} \\{{bar}} \\\\{{baz}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'CONTENT',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
||||
shouldBeToken(result[5], 'CONTENT', '\\');
|
||||
@@ -118,7 +188,19 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('supports escaped escape character on a triple stash', function() {
|
||||
var result = tokenize('{{foo}} \\\\{{{bar}}} {{baz}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'OPEN_UNESCAPED',
|
||||
'ID',
|
||||
'CLOSE_UNESCAPED',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
||||
shouldBeToken(result[5], 'ID', 'bar');
|
||||
@@ -133,7 +215,15 @@ describe('Tokenizer', function() {
|
||||
var result = tokenize('{{foo.bar}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
|
||||
shouldMatchTokens(tokenize('{{foo.bar.baz}}'), ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(tokenize('{{foo.bar.baz}}'), [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
});
|
||||
|
||||
it('allows path literals with []', function() {
|
||||
@@ -143,7 +233,18 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('allows multiple path literals on a line with []', function() {
|
||||
var result = tokenize('{{foo.[bar]}}{{foo.[baz]}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE', 'OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
});
|
||||
|
||||
it('allows escaped literals in []', function() {
|
||||
@@ -158,13 +259,29 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('tokenizes a path as "OPEN (ID SEP)* ID CLOSE"', function() {
|
||||
var result = tokenize('{{../foo/bar}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[1], 'ID', '..');
|
||||
});
|
||||
|
||||
it('tokenizes a path with .. as a parent path', function() {
|
||||
var result = tokenize('{{../foo.bar}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[1], 'ID', '..');
|
||||
});
|
||||
|
||||
@@ -216,7 +333,15 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() {
|
||||
var result = tokenize('{{>foo/bar.baz }}');
|
||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN_PARTIAL',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'SEP',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
});
|
||||
|
||||
it('tokenizes partial block declarations', function() {
|
||||
@@ -225,34 +350,69 @@ describe('Tokenizer', function() {
|
||||
});
|
||||
it('tokenizes a comment as "COMMENT"', function() {
|
||||
var result = tokenize('foo {{! this is a comment }} bar {{ baz }}');
|
||||
shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'CONTENT',
|
||||
'COMMENT',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[1], 'COMMENT', '{{! this is a comment }}');
|
||||
});
|
||||
|
||||
it('tokenizes a block comment as "COMMENT"', function() {
|
||||
var result = tokenize('foo {{!-- this is a {{comment}} --}} bar {{ baz }}');
|
||||
shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'CONTENT',
|
||||
'COMMENT',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a {{comment}} --}}');
|
||||
});
|
||||
|
||||
it('tokenizes a block comment with whitespace as "COMMENT"', function() {
|
||||
var result = tokenize('foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}');
|
||||
shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
|
||||
var result = tokenize(
|
||||
'foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}'
|
||||
);
|
||||
shouldMatchTokens(result, [
|
||||
'CONTENT',
|
||||
'COMMENT',
|
||||
'CONTENT',
|
||||
'OPEN',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a\n{{comment}}\n--}}');
|
||||
});
|
||||
|
||||
it('tokenizes open and closing blocks as OPEN_BLOCK, ID, CLOSE ..., OPEN_ENDBLOCK ID CLOSE', function() {
|
||||
var result = tokenize('{{#foo}}content{{/foo}}');
|
||||
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN_BLOCK',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'OPEN_ENDBLOCK',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
});
|
||||
|
||||
it('tokenizes directives', function() {
|
||||
shouldMatchTokens(
|
||||
tokenize('{{#*foo}}content{{/foo}}'),
|
||||
['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(
|
||||
tokenize('{{*foo}}'),
|
||||
['OPEN', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(tokenize('{{#*foo}}content{{/foo}}'), [
|
||||
'OPEN_BLOCK',
|
||||
'ID',
|
||||
'CLOSE',
|
||||
'CONTENT',
|
||||
'OPEN_ENDBLOCK',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldMatchTokens(tokenize('{{*foo}}'), ['OPEN', 'ID', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes inverse sections as "INVERSE"', function() {
|
||||
@@ -351,28 +511,98 @@ describe('Tokenizer', function() {
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
|
||||
result = tokenize('{{ foo bar baz=bat }}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{ foo bar baz=1 }}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'NUMBER', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'NUMBER',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{ foo bar baz=true }}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'BOOLEAN',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{ foo bar baz=false }}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'BOOLEAN',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{ foo bar\n baz=bat }}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{ foo bar baz="bat" }}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'STRING',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{ foo bar baz="bat" bam=wot }}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'STRING',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{foo omg bar=baz bat="bam"}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'STRING',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[2], 'ID', 'omg');
|
||||
});
|
||||
|
||||
@@ -386,7 +616,15 @@ describe('Tokenizer', function() {
|
||||
shouldBeToken(result[3], 'ID', 'bar');
|
||||
|
||||
result = tokenize('{{ foo bar=@baz }}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'DATA', 'ID', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'ID',
|
||||
'EQUALS',
|
||||
'DATA',
|
||||
'ID',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[5], 'ID', 'baz');
|
||||
});
|
||||
|
||||
@@ -400,12 +638,27 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('tokenizes subexpressions', function() {
|
||||
var result = tokenize('{{foo (bar)}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'CLOSE_SEXPR', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'CLOSE_SEXPR',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[1], 'ID', 'foo');
|
||||
shouldBeToken(result[3], 'ID', 'bar');
|
||||
|
||||
result = tokenize('{{foo (a-x b-y)}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'ID', 'CLOSE_SEXPR', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'ID',
|
||||
'CLOSE_SEXPR',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[1], 'ID', 'foo');
|
||||
shouldBeToken(result[3], 'ID', 'a-x');
|
||||
shouldBeToken(result[4], 'ID', 'b-y');
|
||||
@@ -413,7 +666,21 @@ describe('Tokenizer', function() {
|
||||
|
||||
it('tokenizes nested subexpressions', function() {
|
||||
var result = tokenize('{{foo (bar (lol rofl)) (baz)}}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'OPEN_SEXPR', 'ID', 'ID', 'CLOSE_SEXPR', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'CLOSE_SEXPR', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'ID',
|
||||
'CLOSE_SEXPR',
|
||||
'CLOSE_SEXPR',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'CLOSE_SEXPR',
|
||||
'CLOSE'
|
||||
]);
|
||||
shouldBeToken(result[3], 'ID', 'bar');
|
||||
shouldBeToken(result[5], 'ID', 'lol');
|
||||
shouldBeToken(result[6], 'ID', 'rofl');
|
||||
@@ -421,29 +688,109 @@ describe('Tokenizer', function() {
|
||||
});
|
||||
|
||||
it('tokenizes nested subexpressions: literals', function() {
|
||||
var result = tokenize("{{foo (bar (lol true) false) (baz 1) (blah 'b') (blorg \"c\")}}");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'OPEN_SEXPR', 'ID', 'BOOLEAN', 'CLOSE_SEXPR', 'BOOLEAN', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'NUMBER', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'STRING', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'STRING', 'CLOSE_SEXPR', 'CLOSE']);
|
||||
var result = tokenize(
|
||||
'{{foo (bar (lol true) false) (baz 1) (blah \'b\') (blorg "c")}}'
|
||||
);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN',
|
||||
'ID',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'BOOLEAN',
|
||||
'CLOSE_SEXPR',
|
||||
'BOOLEAN',
|
||||
'CLOSE_SEXPR',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'NUMBER',
|
||||
'CLOSE_SEXPR',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'STRING',
|
||||
'CLOSE_SEXPR',
|
||||
'OPEN_SEXPR',
|
||||
'ID',
|
||||
'STRING',
|
||||
'CLOSE_SEXPR',
|
||||
'CLOSE'
|
||||
]);
|
||||
});
|
||||
|
||||
it('tokenizes block params', function() {
|
||||
var result = tokenize('{{#foo as |bar|}}');
|
||||
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN_BLOCK',
|
||||
'ID',
|
||||
'OPEN_BLOCK_PARAMS',
|
||||
'ID',
|
||||
'CLOSE_BLOCK_PARAMS',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{#foo as |bar baz|}}');
|
||||
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN_BLOCK',
|
||||
'ID',
|
||||
'OPEN_BLOCK_PARAMS',
|
||||
'ID',
|
||||
'ID',
|
||||
'CLOSE_BLOCK_PARAMS',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{#foo as | bar baz |}}');
|
||||
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN_BLOCK',
|
||||
'ID',
|
||||
'OPEN_BLOCK_PARAMS',
|
||||
'ID',
|
||||
'ID',
|
||||
'CLOSE_BLOCK_PARAMS',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{#foo as as | bar baz |}}');
|
||||
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN_BLOCK',
|
||||
'ID',
|
||||
'ID',
|
||||
'OPEN_BLOCK_PARAMS',
|
||||
'ID',
|
||||
'ID',
|
||||
'CLOSE_BLOCK_PARAMS',
|
||||
'CLOSE'
|
||||
]);
|
||||
|
||||
result = tokenize('{{else foo as |bar baz|}}');
|
||||
shouldMatchTokens(result, ['OPEN_INVERSE_CHAIN', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN_INVERSE_CHAIN',
|
||||
'ID',
|
||||
'OPEN_BLOCK_PARAMS',
|
||||
'ID',
|
||||
'ID',
|
||||
'CLOSE_BLOCK_PARAMS',
|
||||
'CLOSE'
|
||||
]);
|
||||
});
|
||||
|
||||
it('tokenizes raw blocks', function() {
|
||||
var result = tokenize('{{{{a}}}} abc {{{{/a}}}} aaa {{{{a}}}} abc {{{{/a}}}}');
|
||||
shouldMatchTokens(result, ['OPEN_RAW_BLOCK', 'ID', 'CLOSE_RAW_BLOCK', 'CONTENT', 'END_RAW_BLOCK', 'CONTENT', 'OPEN_RAW_BLOCK', 'ID', 'CLOSE_RAW_BLOCK', 'CONTENT', 'END_RAW_BLOCK']);
|
||||
var result = tokenize(
|
||||
'{{{{a}}}} abc {{{{/a}}}} aaa {{{{a}}}} abc {{{{/a}}}}'
|
||||
);
|
||||
shouldMatchTokens(result, [
|
||||
'OPEN_RAW_BLOCK',
|
||||
'ID',
|
||||
'CLOSE_RAW_BLOCK',
|
||||
'CONTENT',
|
||||
'END_RAW_BLOCK',
|
||||
'CONTENT',
|
||||
'OPEN_RAW_BLOCK',
|
||||
'ID',
|
||||
'CLOSE_RAW_BLOCK',
|
||||
'CONTENT',
|
||||
'END_RAW_BLOCK'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
+244
-54
@@ -1,7 +1,7 @@
|
||||
describe('track ids', function() {
|
||||
var context;
|
||||
beforeEach(function() {
|
||||
context = {is: {a: 'foo'}, slave: {driver: 'bar'}};
|
||||
context = { is: { a: 'foo' }, slave: { driver: 'bar' } };
|
||||
});
|
||||
|
||||
it('should not include anything without the flag', function() {
|
||||
@@ -16,38 +16,70 @@ describe('track ids', function() {
|
||||
}
|
||||
};
|
||||
|
||||
equals(template({}, {helpers: helpers}), 'success');
|
||||
equals(template({}, { helpers: helpers }), 'success');
|
||||
});
|
||||
it('should include argument ids', function() {
|
||||
var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {trackIds: true});
|
||||
var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {
|
||||
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;
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS is.a:foo slave.driver:bar');
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'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 template = CompilerContext.compile(
|
||||
'{{wycats bat=is.a baz=slave.driver}}',
|
||||
{ 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;
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.hashIds.bat +
|
||||
':' +
|
||||
options.hash.bat +
|
||||
' ' +
|
||||
options.hashIds.baz +
|
||||
':' +
|
||||
options.hash.baz
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS is.a:foo slave.driver:bar');
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'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 template = CompilerContext.compile(
|
||||
'{{wycats ./is.a ../slave.driver this.is.a this}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun, thiz, thiz2, options) {
|
||||
@@ -56,29 +88,57 @@ describe('track ids', function() {
|
||||
equal(options.ids[2], 'is.a');
|
||||
equal(options.ids[3], '');
|
||||
|
||||
return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS is.a:foo ../slave.driver:undefined');
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'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 template = CompilerContext.compile('{{wycats @is.a @slave.driver}}', {
|
||||
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;
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(template({}, {helpers: helpers, data: context}), 'HELP ME MY BOSS @is.a:foo @slave.driver:bar');
|
||||
equals(
|
||||
template({}, { helpers: helpers, data: context }),
|
||||
'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 template = CompilerContext.compile('{{wycats 1 "foo" key=false}}', {
|
||||
trackIds: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
@@ -86,17 +146,31 @@ describe('track ids', function() {
|
||||
equal(options.ids[1], null);
|
||||
equal(options.hashIds.key, null);
|
||||
|
||||
return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun + ' ' + options.hash.key;
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
noun +
|
||||
' ' +
|
||||
options.hash.key
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS 1 foo false');
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'HELP ME MY BOSS 1 foo false'
|
||||
);
|
||||
});
|
||||
it('should return true for subexpressions', function() {
|
||||
var template = CompilerContext.compile('{{wycats (sub)}}', {trackIds: true});
|
||||
var template = CompilerContext.compile('{{wycats (sub)}}', {
|
||||
trackIds: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
sub: function() { return 1; },
|
||||
sub: function() {
|
||||
return 1;
|
||||
},
|
||||
wycats: function(passiveVoice, options) {
|
||||
equal(options.ids[0], true);
|
||||
|
||||
@@ -104,28 +178,43 @@ describe('track ids', function() {
|
||||
}
|
||||
};
|
||||
|
||||
equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS 1');
|
||||
equals(template(context, { helpers: helpers }), '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 template = CompilerContext.compile(
|
||||
'{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
doIt: function(options) {
|
||||
var blockParams = [this.is];
|
||||
blockParams.path = ['zomg'];
|
||||
return options.fn(this, {blockParams: blockParams});
|
||||
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;
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS zomg.a:foo slave.driver:bar');
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'HELP ME MY BOSS zomg.a:foo slave.driver:bar'
|
||||
);
|
||||
});
|
||||
|
||||
describe('builtin helpers', function() {
|
||||
@@ -140,53 +229,119 @@ 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});
|
||||
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');
|
||||
equals(
|
||||
template(
|
||||
{ array: [{ name: 'foo' }, { name: 'bar' }] },
|
||||
{ helpers: helpers }
|
||||
),
|
||||
'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});
|
||||
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');
|
||||
equals(
|
||||
template(
|
||||
{ object: { foo: { name: 'foo' }, bar: { name: 'bar' } } },
|
||||
{ helpers: helpers }
|
||||
),
|
||||
'foo:object.foo\nbar:object.bar\n'
|
||||
);
|
||||
});
|
||||
it('should handle nesting', function() {
|
||||
var template = CompilerContext.compile('{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}', {trackIds: true});
|
||||
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');
|
||||
equals(
|
||||
template(
|
||||
{ array: [{ name: 'foo' }, { name: 'bar' }] },
|
||||
{ helpers: helpers }
|
||||
),
|
||||
'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});
|
||||
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');
|
||||
equals(
|
||||
template(
|
||||
{ array: [{ name: 'foo' }, { name: 'bar' }] },
|
||||
{ helpers: helpers }
|
||||
),
|
||||
'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});
|
||||
var template = CompilerContext.compile(
|
||||
'{{#with field}}{{wycats name}}{{/with}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(template({field: {name: 'foo'}}, {helpers: helpers}), 'foo:field\n');
|
||||
equals(
|
||||
template({ field: { name: 'foo' } }, { helpers: helpers }),
|
||||
'foo:field\n'
|
||||
);
|
||||
});
|
||||
it('should handle nesting', function() {
|
||||
var template = CompilerContext.compile('{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}', {trackIds: true});
|
||||
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');
|
||||
equals(
|
||||
template({ bat: { field: { name: 'foo' } } }, { helpers: helpers }),
|
||||
'foo:bat.field\n'
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('#blockHelperMissing', function() {
|
||||
it('should track contextPath for arrays', function() {
|
||||
var template = CompilerContext.compile('{{#field}}{{wycats name}}{{/field}}', {trackIds: true});
|
||||
var template = CompilerContext.compile(
|
||||
'{{#field}}{{wycats name}}{{/field}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(template({field: [{name: 'foo'}]}, {helpers: helpers}), 'foo:field.0\n');
|
||||
equals(
|
||||
template({ field: [{ name: 'foo' }] }, { helpers: helpers }),
|
||||
'foo:field.0\n'
|
||||
);
|
||||
});
|
||||
it('should track contextPath for keys', function() {
|
||||
var template = CompilerContext.compile('{{#field}}{{wycats name}}{{/field}}', {trackIds: true});
|
||||
var template = CompilerContext.compile(
|
||||
'{{#field}}{{wycats name}}{{/field}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(template({field: {name: 'foo'}}, {helpers: helpers}), 'foo:field\n');
|
||||
equals(
|
||||
template({ field: { name: 'foo' } }, { helpers: helpers }),
|
||||
'foo:field\n'
|
||||
);
|
||||
});
|
||||
it('should handle nesting', function() {
|
||||
var template = CompilerContext.compile('{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}', {trackIds: true});
|
||||
var template = CompilerContext.compile(
|
||||
'{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(template({bat: {field: {name: 'foo'}}}, {helpers: helpers}), 'foo:bat.field\n');
|
||||
equals(
|
||||
template({ bat: { field: { name: 'foo' } } }, { helpers: helpers }),
|
||||
'foo:bat.field\n'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -202,36 +357,71 @@ describe('track ids', function() {
|
||||
};
|
||||
|
||||
it('should pass track id for basic partial', function() {
|
||||
var template = CompilerContext.compile('Dudes: {{#dudes}}{{> dude}}{{/dudes}}', {trackIds: true}),
|
||||
hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
|
||||
var template = CompilerContext.compile(
|
||||
'Dudes: {{#dudes}}{{> dude}}{{/dudes}}',
|
||||
{ trackIds: true }
|
||||
),
|
||||
hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
|
||||
var partials = {
|
||||
dude: CompilerContext.compile('{{wycats name}}', {trackIds: true})
|
||||
dude: CompilerContext.compile('{{wycats name}}', { trackIds: true })
|
||||
};
|
||||
|
||||
equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:dudes.0\nAlan:dudes.1\n');
|
||||
equals(
|
||||
template(hash, { helpers: helpers, partials: partials }),
|
||||
'Dudes: Yehuda:dudes.0\nAlan:dudes.1\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass track id for context partial', function() {
|
||||
var template = CompilerContext.compile('Dudes: {{> dude dudes}}', {trackIds: true}),
|
||||
hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
|
||||
var template = CompilerContext.compile('Dudes: {{> dude dudes}}', {
|
||||
trackIds: true
|
||||
}),
|
||||
hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
|
||||
var partials = {
|
||||
dude: CompilerContext.compile('{{#each this}}{{wycats name}}{{/each}}', {trackIds: true})
|
||||
dude: CompilerContext.compile(
|
||||
'{{#each this}}{{wycats name}}{{/each}}',
|
||||
{ trackIds: true }
|
||||
)
|
||||
};
|
||||
|
||||
equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:dudes..0\nAlan:dudes..1\n');
|
||||
equals(
|
||||
template(hash, { helpers: helpers, partials: partials }),
|
||||
'Dudes: Yehuda:dudes..0\nAlan:dudes..1\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('should invalidate context for partials with parameters', function() {
|
||||
var template = CompilerContext.compile('Dudes: {{#dudes}}{{> dude . bar="foo"}}{{/dudes}}', {trackIds: true}),
|
||||
hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
|
||||
var template = CompilerContext.compile(
|
||||
'Dudes: {{#dudes}}{{> dude . bar="foo"}}{{/dudes}}',
|
||||
{ trackIds: true }
|
||||
),
|
||||
hash = {
|
||||
dudes: [
|
||||
{ name: 'Yehuda', url: 'http://yehuda' },
|
||||
{ name: 'Alan', url: 'http://alan' }
|
||||
]
|
||||
};
|
||||
|
||||
var partials = {
|
||||
dude: CompilerContext.compile('{{wycats name}}', {trackIds: true})
|
||||
dude: CompilerContext.compile('{{wycats name}}', { trackIds: true })
|
||||
};
|
||||
|
||||
equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:true\nAlan:true\n');
|
||||
equals(
|
||||
template(hash, { helpers: helpers, partials: partials }),
|
||||
'Dudes: Yehuda:true\nAlan:true\n'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+16
-5
@@ -5,19 +5,30 @@ describe('utils', function() {
|
||||
if (!(safe instanceof Handlebars.SafeString)) {
|
||||
throw new Error('Must be instance of SafeString');
|
||||
}
|
||||
equals(safe.toString(), 'testing 1, 2, 3', 'SafeString is equivalent to its underlying string');
|
||||
equals(
|
||||
safe.toString(),
|
||||
'testing 1, 2, 3',
|
||||
'SafeString is equivalent to its underlying string'
|
||||
);
|
||||
});
|
||||
|
||||
it('it should not escape SafeString properties', function() {
|
||||
var name = new Handlebars.SafeString('<em>Sean O'Malley</em>');
|
||||
|
||||
shouldCompileTo('{{name}}', [{name: name}], '<em>Sean O'Malley</em>');
|
||||
shouldCompileTo(
|
||||
'{{name}}',
|
||||
[{ name: name }],
|
||||
'<em>Sean O'Malley</em>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#escapeExpression', function() {
|
||||
it('shouhld escape html', function() {
|
||||
equals(Handlebars.Utils.escapeExpression('foo<&"\'>'), 'foo<&"'>');
|
||||
equals(
|
||||
Handlebars.Utils.escapeExpression('foo<&"\'>'),
|
||||
'foo<&"'>'
|
||||
);
|
||||
equals(Handlebars.Utils.escapeExpression('foo='), 'foo=');
|
||||
});
|
||||
it('should not escape SafeString', function() {
|
||||
@@ -58,7 +69,7 @@ describe('utils', function() {
|
||||
equals(Handlebars.Utils.isEmpty(0), false);
|
||||
equals(Handlebars.Utils.isEmpty([1]), false);
|
||||
equals(Handlebars.Utils.isEmpty('foo'), false);
|
||||
equals(Handlebars.Utils.isEmpty({bar: 1}), false);
|
||||
equals(Handlebars.Utils.isEmpty({ bar: 1 }), false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,7 +80,7 @@ describe('utils', function() {
|
||||
}
|
||||
A.prototype.b = 4;
|
||||
|
||||
var b = {b: 2};
|
||||
var b = { b: 2 };
|
||||
|
||||
Handlebars.Utils.extend(b, new A());
|
||||
|
||||
|
||||
+46
-24
@@ -7,7 +7,11 @@ describe('Visitor', function() {
|
||||
// Simply run the thing and make sure it does not fail and that all of the
|
||||
// stub methods are executed
|
||||
var visitor = new Handlebars.Visitor();
|
||||
visitor.accept(Handlebars.parse('{{foo}}{{#foo (bar 1 "1" true undefined null) foo=@data}}{{!comment}}{{> bar }} {{/foo}}'));
|
||||
visitor.accept(
|
||||
Handlebars.parse(
|
||||
'{{foo}}{{#foo (bar 1 "1" true undefined null) foo=@data}}{{!comment}}{{> bar }} {{/foo}}'
|
||||
)
|
||||
);
|
||||
visitor.accept(Handlebars.parse('{{#> bar }} {{/bar}}'));
|
||||
visitor.accept(Handlebars.parse('{{#* bar }} {{/bar}}'));
|
||||
visitor.accept(Handlebars.parse('{{* bar }}'));
|
||||
@@ -40,7 +44,11 @@ describe('Visitor', function() {
|
||||
equal(comment.value, 'comment');
|
||||
};
|
||||
|
||||
visitor.accept(Handlebars.parse('{{#foo.bar (foo.bar 1 "2" true) foo=@foo.bar}}{{!comment}}{{> bar }} {{/foo.bar}}'));
|
||||
visitor.accept(
|
||||
Handlebars.parse(
|
||||
'{{#foo.bar (foo.bar 1 "2" true) foo=@foo.bar}}{{!comment}}{{> bar }} {{/foo.bar}}'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
describe('mutating', function() {
|
||||
@@ -50,12 +58,15 @@ describe('Visitor', function() {
|
||||
|
||||
visitor.mutating = true;
|
||||
visitor.StringLiteral = function(string) {
|
||||
return {type: 'NumberLiteral', value: 42, loc: string.loc};
|
||||
return { type: 'NumberLiteral', value: 42, loc: string.loc };
|
||||
};
|
||||
|
||||
var ast = Handlebars.parse('{{foo foo="foo"}}');
|
||||
visitor.accept(ast);
|
||||
equals(Handlebars.print(ast), '{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n');
|
||||
equals(
|
||||
Handlebars.print(ast),
|
||||
'{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n'
|
||||
);
|
||||
});
|
||||
it('should treat undefined resonse as identity', function() {
|
||||
var visitor = new Handlebars.Visitor();
|
||||
@@ -63,7 +74,10 @@ describe('Visitor', function() {
|
||||
|
||||
var ast = Handlebars.parse('{{foo foo=42}}');
|
||||
visitor.accept(ast);
|
||||
equals(Handlebars.print(ast), '{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n');
|
||||
equals(
|
||||
Handlebars.print(ast),
|
||||
'{{ PATH:foo [] HASH{foo=NUMBER{42}} }}\n'
|
||||
);
|
||||
});
|
||||
it('should remove false responses', function() {
|
||||
var visitor = new Handlebars.Visitor();
|
||||
@@ -78,30 +92,38 @@ describe('Visitor', function() {
|
||||
equals(Handlebars.print(ast), '{{ PATH:foo [] }}\n');
|
||||
});
|
||||
it('should throw when removing required values', function() {
|
||||
shouldThrow(function() {
|
||||
var visitor = new Handlebars.Visitor();
|
||||
shouldThrow(
|
||||
function() {
|
||||
var visitor = new Handlebars.Visitor();
|
||||
|
||||
visitor.mutating = true;
|
||||
visitor.PathExpression = function() {
|
||||
return false;
|
||||
};
|
||||
visitor.mutating = true;
|
||||
visitor.PathExpression = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
var ast = Handlebars.parse('{{foo 42}}');
|
||||
visitor.accept(ast);
|
||||
}, Handlebars.Exception, 'MustacheStatement requires path');
|
||||
var ast = Handlebars.parse('{{foo 42}}');
|
||||
visitor.accept(ast);
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'MustacheStatement requires path'
|
||||
);
|
||||
});
|
||||
it('should throw when returning non-node responses', function() {
|
||||
shouldThrow(function() {
|
||||
var visitor = new Handlebars.Visitor();
|
||||
shouldThrow(
|
||||
function() {
|
||||
var visitor = new Handlebars.Visitor();
|
||||
|
||||
visitor.mutating = true;
|
||||
visitor.PathExpression = function() {
|
||||
return {};
|
||||
};
|
||||
visitor.mutating = true;
|
||||
visitor.PathExpression = function() {
|
||||
return {};
|
||||
};
|
||||
|
||||
var ast = Handlebars.parse('{{foo 42}}');
|
||||
visitor.accept(ast);
|
||||
}, Handlebars.Exception, 'Unexpected node type "undefined" found when accepting path on MustacheStatement');
|
||||
var ast = Handlebars.parse('{{foo 42}}');
|
||||
visitor.accept(ast);
|
||||
},
|
||||
Handlebars.Exception,
|
||||
'Unexpected node type "undefined" found when accepting path on MustacheStatement'
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('arrays', function() {
|
||||
@@ -110,7 +132,7 @@ describe('Visitor', function() {
|
||||
|
||||
visitor.mutating = true;
|
||||
visitor.StringLiteral = function(string) {
|
||||
return {type: 'NumberLiteral', value: 42, loc: string.locInfo};
|
||||
return { type: 'NumberLiteral', value: 42, loc: string.locInfo };
|
||||
};
|
||||
|
||||
var ast = Handlebars.parse('{{foo "foo"}}');
|
||||
|
||||
+64
-15
@@ -1,6 +1,6 @@
|
||||
describe('whitespace control', function() {
|
||||
it('should strip whitespace around mustache calls', function() {
|
||||
var hash = {foo: 'bar<'};
|
||||
var hash = { foo: 'bar<' };
|
||||
|
||||
shouldCompileTo(' {{~foo~}} ', hash, 'bar<');
|
||||
shouldCompileTo(' {{~foo}} ', hash, 'bar< ');
|
||||
@@ -14,15 +14,23 @@ describe('whitespace control', function() {
|
||||
|
||||
describe('blocks', function() {
|
||||
it('should strip whitespace around simple block calls', function() {
|
||||
var hash = {foo: 'bar<'};
|
||||
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 ');
|
||||
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 '
|
||||
);
|
||||
});
|
||||
it('should strip whitespace around inverse block calls', function() {
|
||||
var hash = {};
|
||||
@@ -32,10 +40,14 @@ describe('whitespace control', function() {
|
||||
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(
|
||||
' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ',
|
||||
hash,
|
||||
'bar'
|
||||
);
|
||||
});
|
||||
it('should strip whitespace around complex block calls', function() {
|
||||
var hash = {foo: 'bar<'};
|
||||
var hash = { foo: 'bar<' };
|
||||
|
||||
shouldCompileTo('{{#if foo~}} bar {{~^~}} baz {{~/if}}', hash, 'bar');
|
||||
shouldCompileTo('{{#if foo~}} bar {{^~}} baz {{/if}}', hash, 'bar ');
|
||||
@@ -44,8 +56,16 @@ describe('whitespace control', function() {
|
||||
|
||||
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<');
|
||||
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<'
|
||||
);
|
||||
|
||||
hash = {};
|
||||
|
||||
@@ -56,21 +76,50 @@ describe('whitespace control', function() {
|
||||
|
||||
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');
|
||||
shouldCompileTo(
|
||||
'\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n',
|
||||
hash,
|
||||
'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 {{~> 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');
|
||||
shouldCompileToWithPartials(
|
||||
'foo\n {{~> dude}} ',
|
||||
[{}, {}, { dude: 'bar' }],
|
||||
true,
|
||||
'foobar'
|
||||
);
|
||||
shouldCompileToWithPartials(
|
||||
'foo\n {{> dude}} ',
|
||||
[{}, {}, { dude: 'bar' }],
|
||||
true,
|
||||
'foo\n bar'
|
||||
);
|
||||
});
|
||||
|
||||
it('should only strip whitespace once', function() {
|
||||
var hash = {foo: 'bar'};
|
||||
var hash = { foo: 'bar' };
|
||||
|
||||
shouldCompileTo(' {{~foo~}} {{foo}} {{foo}} ', hash, 'barbar bar ');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user