blob: 855434813fe44ad9287f23d5c97f0fabe9119d73 [file] [log] [blame]
import 'package:unittest/unittest.dart';
import 'package:mustache/src/node.dart';
import 'package:mustache/src/parser.dart';
import 'package:mustache/src/scanner.dart';
import 'package:mustache/src/template_exception.dart';
import 'package:mustache/src/token.dart';
main() {
group('Scanner', () {
test('scan text', () {
var source = 'abc';
var scanner = new Scanner(source, 'foo', '{{ }}', lenient: false);
var tokens = scanner.scan();
expectTokens(tokens, [ new Token(TokenType.text, 'abc', 0, 3)]);
});
test('scan tag', () {
var source = 'abc{{foo}}def';
var scanner = new Scanner(source, 'foo', '{{ }}', lenient: false);
var tokens = scanner.scan();
expectTokens(tokens, [
new Token(TokenType.text, 'abc', 0, 3),
new Token(TokenType.openDelimiter, '{{', 3, 5),
new Token(TokenType.identifier, 'foo', 5, 8),
new Token(TokenType.closeDelimiter, '}}', 8, 10),
new Token(TokenType.text, 'def', 10, 13)
]);
});
test('scan tag whitespace', () {
var source = 'abc{{ foo }}def';
var scanner = new Scanner(source, 'foo', '{{ }}', lenient: false);
var tokens = scanner.scan();
expectTokens(tokens, [
new Token(TokenType.text, 'abc', 0, 3),
new Token(TokenType.openDelimiter, '{{', 3, 5),
new Token(TokenType.whitespace, ' ', 5, 6),
new Token(TokenType.identifier, 'foo', 6, 9),
new Token(TokenType.whitespace, ' ', 9, 10),
new Token(TokenType.closeDelimiter, '}}', 10, 12),
new Token(TokenType.text, 'def', 12, 15)
]);
});
test('scan tag sigil', () {
var source = 'abc{{ # foo }}def';
var scanner = new Scanner(source, 'foo', '{{ }}', lenient: false);
var tokens = scanner.scan();
expectTokens(tokens, [
new Token(TokenType.text, 'abc', 0, 3),
new Token(TokenType.openDelimiter, '{{', 3, 5),
new Token(TokenType.whitespace, ' ', 5, 6),
new Token(TokenType.sigil, '#', 6, 7),
new Token(TokenType.whitespace, ' ', 7, 8),
new Token(TokenType.identifier, 'foo', 8, 11),
new Token(TokenType.whitespace, ' ', 11, 12),
new Token(TokenType.closeDelimiter, '}}', 12, 14),
new Token(TokenType.text, 'def', 14, 17)
]);
});
test('scan tag dot', () {
var source = 'abc{{ foo.bar }}def';
var scanner = new Scanner(source, 'foo', '{{ }}', lenient: false);
var tokens = scanner.scan();
expectTokens(tokens, [
new Token(TokenType.text, 'abc', 0, 3),
new Token(TokenType.openDelimiter, '{{', 3, 5),
new Token(TokenType.whitespace, ' ', 5, 6),
new Token(TokenType.identifier, 'foo', 6, 9),
new Token(TokenType.dot, '.', 9, 10),
new Token(TokenType.identifier, 'bar', 10, 13),
new Token(TokenType.whitespace, ' ', 13, 14),
new Token(TokenType.closeDelimiter, '}}', 14, 16),
new Token(TokenType.text, 'def', 16, 19)
]);
});
test('scan triple mustache', () {
var source = 'abc{{{foo}}}def';
var scanner = new Scanner(source, 'foo', '{{ }}', lenient: false);
var tokens = scanner.scan();
expectTokens(tokens, [
new Token(TokenType.text, 'abc', 0, 3),
new Token(TokenType.openDelimiter, '{{{', 3, 6),
new Token(TokenType.identifier, 'foo', 6, 9),
new Token(TokenType.closeDelimiter, '}}}', 9, 12),
new Token(TokenType.text, 'def', 12, 15)
]);
});
test('scan triple mustache whitespace', () {
var source = 'abc{{{ foo }}}def';
var scanner = new Scanner(source, 'foo', '{{ }}', lenient: false);
var tokens = scanner.scan();
expectTokens(tokens, [
new Token(TokenType.text, 'abc', 0, 3),
new Token(TokenType.openDelimiter, '{{{', 3, 6),
new Token(TokenType.whitespace, ' ', 6, 7),
new Token(TokenType.identifier, 'foo', 7, 10),
new Token(TokenType.whitespace, ' ', 10, 11),
new Token(TokenType.closeDelimiter, '}}}', 11, 14),
new Token(TokenType.text, 'def', 14, 17)
]);
});
test('scan tag with equals', () {
var source = '{{foo=bar}}';
var scanner = new Scanner(source, 'foo', '{{ }}', lenient: true);
var tokens = scanner.scan();
expectTokens(tokens, [
new Token(TokenType.openDelimiter, '{{', 0, 2),
new Token(TokenType.identifier, 'foo=bar', 2, 9),
new Token(TokenType.closeDelimiter, '}}', 9, 11),
]);
});
test('scan comment with equals', () {
var source = '{{!foo=bar}}';
var scanner = new Scanner(source, 'foo', '{{ }}', lenient: false);
var tokens = scanner.scan();
expectTokens(tokens, [
new Token(TokenType.openDelimiter, '{{', 0, 2),
new Token(TokenType.sigil, '!', 2, 3),
new Token(TokenType.identifier, 'foo=bar', 3, 10),
new Token(TokenType.closeDelimiter, '}}', 10, 12),
]);
});
});
group('Parser', () {
test('parse variable', () {
var source = 'abc{{foo}}def';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expectNodes(nodes, [
new TextNode('abc', 0, 3),
new VariableNode('foo', 3, 10, escape: true),
new TextNode('def', 10, 13)
]);
});
test('parse variable whitespace', () {
var source = 'abc{{ foo }}def';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expectNodes(nodes, [
new TextNode('abc', 0, 3),
new VariableNode('foo', 3, 12, escape: true),
new TextNode('def', 12, 15)
]);
});
test('parse section', () {
var source = 'abc{{#foo}}def{{/foo}}ghi';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expectNodes(nodes, [
new TextNode('abc', 0, 3),
new SectionNode('foo', 3, 11, '{{ }}'),
new TextNode('ghi', 22, 25)
]);
expectNodes(nodes[1].children, [new TextNode('def', 11, 14)]);
});
test('parse section standalone tag whitespace', () {
var source = 'abc\n{{#foo}}\ndef\n{{/foo}}\nghi';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expectNodes(nodes, [
new TextNode('abc\n', 0, 4),
new SectionNode('foo', 4, 12, '{{ }}'),
new TextNode('ghi', 26, 29)
]);
expectNodes(nodes[1].children, [new TextNode('def\n', 13, 17)]);
});
test('parse section standalone tag whitespace consecutive', () {
var source = 'abc\n{{#foo}}\ndef\n{{/foo}}\n{{#foo}}\ndef\n{{/foo}}\nghi';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expectNodes(nodes, [
new TextNode('abc\n', 0, 4),
new SectionNode('foo', 4, 12, '{{ }}'),
new SectionNode('foo', 26, 34, '{{ }}'),
new TextNode('ghi', 48, 51),
]);
expectNodes(nodes[1].children, [new TextNode('def\n', 13, 17)]);
});
test('parse section standalone tag whitespace on first line', () {
var source = ' {{#foo}} \ndef\n{{/foo}}\nghi';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expectNodes(nodes, [
new SectionNode('foo', 2, 10, '{{ }}'),
new TextNode('ghi', 26, 29)
]);
expectNodes(nodes[0].children, [new TextNode('def\n', 13, 17)]);
});
test('parse section standalone tag whitespace on last line', () {
var source = '{{#foo}}def\n {{/foo}} ';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expectNodes(nodes, [
new SectionNode('foo', 0, 8, '{{ }}')
]);
expectNodes(nodes[0].children, [new TextNode('def\n', 8, 12)]);
});
test('parse whitespace', () {
var source = 'abc\n ';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expectNodes(nodes, [
new TextNode('abc\n ', 0, 7),
]);
});
test('parse partial', () {
var source = 'abc\n {{>foo}}def';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expectNodes(nodes, [
new TextNode('abc\n ', 0, 7),
new PartialNode('foo', 7, 15, ' '),
new TextNode('def', 15, 18)
]);
});
test('parse change delimiters', () {
var source = '{{= | | =}}<|#lambda|-|/lambda|>';
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
var nodes = parser.parse();
expect(nodes[1].delimiters, equals('| |'));
expectNodes(nodes, [
new TextNode('<', 11, 12),
new SectionNode('lambda', 12, 21, '| |'),
new TextNode('>', 31, 32),
]);
expectNodes(nodes[1].children, [new TextNode('-', 21, 22)]);
});
test('corner case strict', () {
var source = "{{{ #foo }}} {{{ /foo }}}";
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
try {
parser.parse();
fail('Should fail.');
} catch (e) {
expect(e is TemplateException, isTrue);
}
});
test('corner case lenient', () {
var source = "{{{ #foo }}} {{{ /foo }}}";
var parser = new Parser(source, 'foo', '{{ }}', lenient: true);
var nodes = parser.parse();
expectNodes(nodes, [
new VariableNode('#foo', 0, 12, escape: false),
new TextNode(' ', 12, 13),
new VariableNode('/foo', 13, 25, escape: false)
]);
});
test('toString', () {
new TextNode('foo', 1, 3).toString();
new VariableNode('foo', 1, 3).toString();
new PartialNode('foo', 1, 3, ' ').toString();
new SectionNode('foo', 1, 3, '{{ }}').toString();
new Token(TokenType.closeDelimiter, 'foo', 1, 3).toString();
TokenType.closeDelimiter.toString();
});
test('exception', () {
var source = "'{{ foo }} sdfffffffffffffffffffffffffffffffffffffffffffff "
"dsfsdf sdfdsa fdsfads fsdfdsfadsf dsfasdfsdf sdfdsfsadf sdfadsfsdf ";
var ex = new TemplateException('boom!', 'foo.mustache', source, 2);
ex.toString();
});
parseFail(source) {
try {
var parser = new Parser(source, 'foo', '{{ }}', lenient: false);
parser.parse();
fail('Did not throw.');
return null;
} catch (ex, st) {
if (ex is! TemplateException) {
print(ex);
print(st);
}
return ex;
}
}
test('parse eof', () {
expectTemplateEx(ex) => expect(ex is TemplateException, isTrue);
expectTemplateEx(parseFail('{{#foo}}{{bar}}{{/foo}'));
expectTemplateEx(parseFail('{{#foo}}{{bar}}{{/foo'));
expectTemplateEx(parseFail('{{#foo}}{{bar}}{{/'));
expectTemplateEx(parseFail('{{#foo}}{{bar}}{{'));
expectTemplateEx(parseFail('{{#foo}}{{bar}}{'));
expectTemplateEx(parseFail('{{#foo}}{{bar}}'));
expectTemplateEx(parseFail('{{#foo}}{{bar}'));
expectTemplateEx(parseFail('{{#foo}}{{bar'));
expectTemplateEx(parseFail('{{#foo}}{{'));
expectTemplateEx(parseFail('{{#foo}}{'));
expectTemplateEx(parseFail('{{#foo}}'));
expectTemplateEx(parseFail('{{#foo}'));
expectTemplateEx(parseFail('{{#'));
expectTemplateEx(parseFail('{{'));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }}{{ / foo }'));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }}{{ / foo '));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }}{{ / foo'));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }}{{ / '));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }}{{ /'));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }}{{ '));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }}{{'));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }}{'));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }}'));
expectTemplateEx(parseFail('{{ # foo }}{{ bar }'));
expectTemplateEx(parseFail('{{ # foo }}{{ bar '));
expectTemplateEx(parseFail('{{ # foo }}{{ bar'));
expectTemplateEx(parseFail('{{ # foo }}{{ '));
expectTemplateEx(parseFail('{{ # foo }}{{'));
expectTemplateEx(parseFail('{{ # foo }}{'));
expectTemplateEx(parseFail('{{ # foo }}'));
expectTemplateEx(parseFail('{{ # foo }'));
expectTemplateEx(parseFail('{{ # foo '));
expectTemplateEx(parseFail('{{ # foo'));
expectTemplateEx(parseFail('{{ # '));
expectTemplateEx(parseFail('{{ #'));
expectTemplateEx(parseFail('{{ '));
expectTemplateEx(parseFail('{{'));
expectTemplateEx(parseFail('{{= || || =}'));
expectTemplateEx(parseFail('{{= || || ='));
expectTemplateEx(parseFail('{{= || || '));
expectTemplateEx(parseFail('{{= || ||'));
expectTemplateEx(parseFail('{{= || |'));
expectTemplateEx(parseFail('{{= || '));
expectTemplateEx(parseFail('{{= ||'));
expectTemplateEx(parseFail('{{= |'));
expectTemplateEx(parseFail('{{= '));
expectTemplateEx(parseFail('{{='));
});
});
}
nodeEqual(a, b) {
if (a is TextNode) {
return b is TextNode
&& a.text == b.text
&& a.start == b.start
&& a.end == b.end;
} else if (a is VariableNode) {
return a is VariableNode
&& a.name == b.name
&& a.escape == b.escape
&& a.start == b.start
&& a.end == b.end;
} else if (a is SectionNode) {
return a is SectionNode
&& a.name == b.name
&& a.delimiters == b.delimiters
&& a.inverse == b.inverse
&& a.start == b.start
&& a.end == b.end;
} else if (a is PartialNode) {
return a is PartialNode
&& a.name == b.name
&& a.indent == b.indent;
} else {
return false;
}
}
tokenEqual(Token a, Token b) {
return a is Token
&& a.type == b.type
&& a.value == b.value
&& a.start == b.start
&& a.end == b.end;
}
expectTokens(List<Token> a, List<Token> b) {
expect(a.length, equals(b.length), reason: "$a != $b");
for (var i = 0; i < a.length; i++) {
expect(tokenEqual(a[i], b[i]), isTrue, reason: "$a != $b");
}
}
expectNodes(List<Node> a, List<Node> b) {
expect(a.length, equals(b.length), reason: "$a != $b");
for (var i = 0; i < a.length; i++) {
expect(nodeEqual(a[i], b[i]), isTrue, reason: "$a != $b");
}
}