describe('ast', function() { if (!Handlebars.AST) { return; } var AST = Handlebars.AST; 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"); }); }); 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); }); 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); }); it('should handle subexpressions', function() { 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: '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: '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); }); }); }); describe('Line Numbers', function() { var ast, body; function testColumns(node, firstLine, lastLine, firstColumn, lastColumn) { equals(node.loc.start.line, firstLine); equals(node.loc.start.column, firstColumn); equals(node.loc.end.line, lastLine); equals(node.loc.end.column, lastColumn); } 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 body = ast.body; it('gets ContentNode line numbers', function() { var contentNode = body[0]; testColumns(contentNode, 1, 1, 0, 7); }); it('gets MustacheStatement line numbers', function() { var mustacheNode = body[1]; testColumns(mustacheNode, 1, 1, 7, 21); }); it('gets line numbers correct when newlines appear', function() { testColumns(body[2], 1, 2, 21, 8); }); 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('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); }); 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); }); 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); }); }); describe('standalone flags', function() { describe('mustache', function() { it('does not mark mustaches as standalone', function() { var ast = Handlebars.parse(' {{comment}} '); equals(!!ast.body[0].value, true); equals(!!ast.body[2].value, true); }); }); 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]; equals(ast.body[0].value, ''); equals(block.program.body[0].value, 'foo\n'); equals(block.inverse.body[0].value, ' bar \n'); equals(ast.body[2].value, ''); }); it('marks initial block mustaches as standalone', function() { var ast = Handlebars.parse('{{# comment}} \nfoo\n {{/comment}}'), 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]; 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]; equals(body[0].value, ''); equals(block.program.body[0].value, 'foo\n'); equals(block.inverse.body[0].value, ' bar \n'); 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]; equals(body[0].omit, undefined); equals(block.program.body[0].value, ' \nfoo\n'); equals(block.inverse.body[0].value, ' bar \n '); 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]; equals(block.program.body[0].value, ' \nfoo\n'); equals(block.inverse.body[0].value, ' bar \n '); equals(body[0].omit, undefined); }); 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]; equals(ast.body[0].omit, undefined); equals(block.program.body[0].value, 'foo\n'); equals(block.inverse.body[0].value, ' bar \n'); equals(ast.body[2].value, ''); }); }); describe('partials', function() { it('marks partial as standalone', function() { var ast = Handlebars.parse('{{> partial }} '); equals(ast.body[1].value, ''); }); it('marks indented partial as standalone', function() { var ast = Handlebars.parse(' {{> partial }} '); equals(ast.body[0].value, ''); equals(ast.body[1].indent, ' '); equals(ast.body[2].value, ''); }); it('marks those around content as not standalone', function() { var ast = Handlebars.parse('a{{> partial }}'); equals(ast.body[0].omit, undefined); ast = Handlebars.parse('{{> partial }}a'); equals(ast.body[1].omit, undefined); }); }); describe('comments', function() { it('marks comment as standalone', function() { var ast = Handlebars.parse('{{! comment }} '); equals(ast.body[1].value, ''); }); it('marks indented comment as standalone', function() { var ast = Handlebars.parse(' {{! comment }} '); equals(ast.body[0].value, ''); equals(ast.body[2].value, ''); }); it('marks those around content as not standalone', function() { var ast = Handlebars.parse('a{{! comment }}'); equals(ast.body[0].omit, undefined); ast = Handlebars.parse('{{! comment }}a'); equals(ast.body[1].omit, undefined); }); }); }); });