blob: a601a1d8f4485cd360dbb5b9276ca9235a90c820 [file] [log] [blame]
import 'package:dartdoc/src/mustachio/parser.dart';
import 'package:test/test.dart';
final _filePath = Uri.parse('file:///foo.dart');
void main() {
test('parses an empty template', () {
var parser = MustachioParser('', _filePath);
var ast = parser.parse();
expect(ast, isEmpty);
});
test('parses "{" as text', () {
var parser = MustachioParser('{', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast.single, equals('{'), spanStart: 0, spanEnd: 1);
});
test('parses "{{" as text', () {
var parser = MustachioParser('{{', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast[0], equals('{{'), spanStart: 0, spanEnd: 2);
});
test('parses "{{}}" as text', () {
var parser = MustachioParser('{{}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast[0], equals('{{}}'), spanStart: 0, spanEnd: 4);
});
test('parses "{{{}}" as text', () {
var parser = MustachioParser('{{{}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast[0], equals('{{{}}'), spanStart: 0, spanEnd: 5);
});
test('parses text as text', () {
var parser = MustachioParser('Words, punctuation, #^!>/ etc.', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast.single, equals('Words, punctuation, #^!>/ etc.'),
spanStart: 0, spanEnd: 30);
});
test('drops comment, start of content', () {
var parser = MustachioParser('{{!comment}} Text', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast.single, equals(' Text'), spanStart: 12, spanEnd: 17);
});
test('drops comment, end of content', () {
var parser = MustachioParser('Text {{!comment}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast.single, equals('Text '), spanStart: 0, spanEnd: 5);
});
test('drops comment, entire content', () {
var parser = MustachioParser('{{!comment}}', _filePath);
var ast = parser.parse();
expect(ast, isEmpty);
});
test('drops comment with whitespace', () {
var parser = MustachioParser('Text {{ !comment }} Text', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '), spanStart: 0, spanEnd: 5);
_expectText(ast[1], equals(' Text'), spanStart: 21, spanEnd: 26);
});
test('drops comment with newlines', () {
var parser = MustachioParser('Text {{ \n !comment \n }} Text', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '), spanStart: 0, spanEnd: 5);
_expectText(ast[1], equals(' Text'), spanStart: 23, spanEnd: 28);
});
test('drops comment with various chars', () {
var parser = MustachioParser(
'Text {{!Text, punct. `!@#\$%^&*()-=+}} Text', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '), spanStart: 0, spanEnd: 5);
_expectText(ast[1], equals(' Text'), spanStart: 37, spanEnd: 42);
});
test('drops comment with newlines inside', () {
var parser = MustachioParser('Text {{!Text\nMore text}} Text', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '), spanStart: 0, spanEnd: 5);
_expectText(ast[1], equals(' Text'), spanStart: 24, spanEnd: 29);
});
test('parses variable', () {
var parser = MustachioParser('Text {{key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '), spanStart: 0, spanEnd: 5);
_expectVariable(ast[1], equals(['key']),
spanStart: 5, spanEnd: 12, keySpanStart: 7, keySpanEnd: 10);
});
test('parses variable with whitespace', () {
var parser = MustachioParser('Text {{ key }}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectVariable(ast[1], equals(['key']),
spanStart: 5, spanEnd: 16, keySpanStart: 9, keySpanEnd: 12);
});
test('parses variable with newlines', () {
var parser = MustachioParser('Text {{\n \nkey\n \n}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectVariable(ast[1], equals(['key']),
spanStart: 5, spanEnd: 20, keySpanStart: 11, keySpanEnd: 14);
});
test('parses variable with triple mustaches', () {
var parser = MustachioParser('Text {{{key}}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectVariable(ast[1], equals(['key']),
escape: false,
spanStart: 5,
spanEnd: 14,
keySpanStart: 8,
keySpanEnd: 11);
});
test('parses variable with triple mustaches, whitespace', () {
var parser = MustachioParser('Text {{{ key }}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectVariable(ast[1], equals(['key']),
escape: false,
spanStart: 5,
spanEnd: 18,
keySpanStart: 10,
keySpanEnd: 13);
});
test('parses "." pseudo-variable', () {
var parser = MustachioParser('Text {{.}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectVariable(ast[1], equals(['.']),
spanStart: 5, spanEnd: 10, keySpanStart: 7, keySpanEnd: 8);
});
test('parses "." pseudo-variable with whitespace', () {
var parser = MustachioParser('Text {{ . }}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectVariable(ast[1], equals(['.']),
spanStart: 5, spanEnd: 12, keySpanStart: 8, keySpanEnd: 9);
});
test('parses variable with multiple names', () {
var parser = MustachioParser('Text {{a.b}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectVariable(ast[1], equals(['a', 'b']),
spanStart: 5, spanEnd: 12, keySpanStart: 7, keySpanEnd: 10);
});
test('parses variable with multiple names and whitespace', () {
var parser = MustachioParser('Text {{ a.b }}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectVariable(ast[1], equals(['a', 'b']),
spanStart: 5, spanEnd: 14, keySpanStart: 8, keySpanEnd: 11);
});
test('parses almost-variable with trailing "." as text', () {
var parser = MustachioParser('Text {{ a.b. }}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast[0], equals('Text {{ a.b. }}'), spanStart: 0, spanEnd: 15);
});
test('parses almost-variable missing one "}" as text', () {
var parser = MustachioParser('Text {{ a.b }', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast[0], equals('Text {{ a.b }'), spanStart: 0, spanEnd: 13);
});
test('parses almost-variable missing one "{" as text', () {
var parser = MustachioParser('Text { a.b }}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast.single, equals('Text { a.b }}'), spanStart: 0, spanEnd: 13);
});
test('parses variable with extra "{"', () {
var parser = MustachioParser('Text {{{ a.b }}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text {'), spanStart: 0, spanEnd: 6);
_expectVariable(ast[1], equals(['a', 'b']), spanStart: 6, spanEnd: 15);
});
test('parses section', () {
var parser =
MustachioParser('Text {{#key}}Section text{{/key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var section = ast[1] as Section;
_expectSection(section, equals(['key']),
spanStart: 5, spanEnd: 33, keySpanStart: 8, keySpanEnd: 11);
expect(section.children, hasLength(1));
_expectText(section.children.single, equals('Section text'),
spanStart: 13, spanEnd: 25);
});
test('parses empty section', () {
var parser = MustachioParser('Text {{#key}}{{/key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var section = ast[1] as Section;
_expectSection(section, equals(['key']), spanStart: 5, spanEnd: 21);
expect(section.children, isEmpty);
});
test('parses section with variable tag inside', () {
var parser = MustachioParser('Text {{#key}}{{two}}{{/key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var section = ast[1] as Section;
_expectSection(section, equals(['key']), spanStart: 5, spanEnd: 28);
expect(section.children, hasLength(1));
_expectVariable(section.children.single, equals(['two']),
spanStart: 13, spanEnd: 20);
});
test('parses section with multi-name key', () {
var parser = MustachioParser(
'Text {{#one.two.three}}Text{{/one.two.three}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var sectionOne = ast[1] as Section;
_expectSection(sectionOne, equals(['one']),
spanStart: 5, spanEnd: 45, keySpanStart: 8, keySpanEnd: 11);
expect(sectionOne.children, hasLength(1));
var sectionTwo = sectionOne.children[0] as Section;
_expectSection(sectionTwo, equals(['two']),
spanStart: 5, spanEnd: 45, keySpanStart: 12, keySpanEnd: 15);
var sectionThree = sectionTwo.children[0] as Section;
_expectSection(sectionThree, equals(['three']),
spanStart: 5, spanEnd: 45, keySpanStart: 16, keySpanEnd: 21);
_expectText(sectionThree.children[0], equals('Text'),
spanStart: 23, spanEnd: 27);
});
test('parses inverse section with multi-name key', () {
var parser =
MustachioParser('Text {{^one.two}}Text{{/one.two}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var sectionOne = ast[1] as Section;
_expectSection(sectionOne, equals(['one']),
spanStart: 5, spanEnd: 33, keySpanStart: 8, keySpanEnd: 11);
expect(sectionOne.children, hasLength(1));
var sectionTwo = sectionOne.children[0] as Section;
_expectSection(sectionTwo, equals(['two']),
invert: true,
spanStart: 5,
spanEnd: 33,
keySpanStart: 12,
keySpanEnd: 15);
_expectText(sectionTwo.children[0], equals('Text'));
});
test('parses section with empty key as text', () {
var parser = MustachioParser('Text {{#}}{{/key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast[0], equals('Text {{#}}{{/key}}'));
});
test('parses section with missing closing tag as text', () {
var parser = MustachioParser('Text {{#}}{{/key}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast[0], equals('Text {{#}}{{/key}'));
});
test('parses section with other closing tag', () {
var parser = MustachioParser('Text {{#key}}{{/other}}{{/key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var section = ast[1] as Section;
_expectSection(section, equals(['key']), spanStart: 5, spanEnd: 31);
expect(section.children, hasLength(1));
_expectText(section.children.single, equals('{{/other}}'),
spanStart: 13, spanEnd: 23);
});
test('parses empty closing tag as text', () {
var parser = MustachioParser('Text {{#key}}{{/}}{{/key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var section = ast[1] as Section;
_expectSection(section, equals(['key']), spanStart: 5, spanEnd: 26);
expect(section.children, hasLength(1));
_expectText(section.children.single, equals('{{/}}'));
});
test('parses nested sections', () {
var parser = MustachioParser(
'Text {{#key1}} AA {{#key2}} BB {{/key2}} CC {{/key1}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var section = ast[1] as Section;
_expectSection(section, equals(['key1']), spanStart: 5, spanEnd: 53);
expect(section.children, hasLength(3));
_expectText(section.children[0], equals(' AA '));
var innerSection = section.children[1] as Section;
_expectSection(innerSection, equals(['key2']), spanStart: 18, spanEnd: 40);
expect(innerSection.children, hasLength(1));
_expectText(innerSection.children[0], equals(' BB '));
});
test('parses nested sections with the same key', () {
var parser = MustachioParser(
'Text {{#key}} AA {{#key}} BB {{/key}} CC {{/key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var section = ast[1] as Section;
_expectSection(section, equals(['key']));
expect(section.children, hasLength(3));
_expectText(section.children[0], equals(' AA '));
var innerSection = section.children[1] as Section;
_expectSection(innerSection, equals(['key']));
expect(innerSection.children, hasLength(1));
_expectText(innerSection.children[0], equals(' BB '));
});
test('parses inverted section', () {
var parser = MustachioParser('Text {{^key}} AA {{/key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
var section = ast[1] as Section;
_expectSection(section, equals(['key']),
invert: true,
spanStart: 5,
spanEnd: 25,
keySpanStart: 8,
keySpanEnd: 11);
expect(section.children, hasLength(1));
_expectText(section.children[0], equals(' AA '));
});
test('parses section with empty key as text', () {
var parser = MustachioParser('Text {{^}}{{/key}}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(1));
_expectText(ast[0], equals('Text {{^}}{{/key}}'));
});
test('parses partial', () {
var parser = MustachioParser('Text {{ >partial }}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectPartial(ast[1], equals('partial'),
spanStart: 5, spanEnd: 19, keySpanStart: 9, keySpanEnd: 16);
});
test('parses partial with various chars', () {
var parser =
MustachioParser('Text {{ >Text,punct.`!@#\$%^&*()-=+ }}', _filePath);
var ast = parser.parse();
expect(ast, hasLength(2));
_expectText(ast[0], equals('Text '));
_expectPartial(ast[1], equals('Text,punct.`!@#\$%^&*()-=+'),
spanStart: 5, spanEnd: 37, keySpanStart: 9, keySpanEnd: 34);
});
}
void _expectText(MustachioNode node, Object matcher,
{int spanStart, int spanEnd}) {
expect(node, isA<Text>().having((e) => e.content, 'content', matcher));
if (spanStart != null) {
expect(
node,
isA<Text>()
.having((e) => e.span.start.offset, 'span.start', spanStart));
}
if (spanEnd != null) {
expect(node,
isA<Text>().having((e) => e.span.end.offset, 'span.end', spanEnd));
}
}
void _expectVariable(MustachioNode node, Object matcher,
{bool escape = true,
int spanStart,
int spanEnd,
int keySpanStart,
int keySpanEnd}) {
expect(
node,
isA<Variable>()
.having((e) => e.key, 'key', matcher)
.having((e) => e.escape, 'escape', escape));
if (spanStart != null) {
var actualSpanStart = (node as Variable).span.start.offset;
_expectSpanOffset('Variable', 'start', actualSpanStart, spanStart);
}
if (spanEnd != null) {
var actualSpanEnd = (node as Variable).span.end.offset;
_expectSpanOffset('Variable', 'end', actualSpanEnd, spanEnd);
}
if (keySpanStart != null) {
var actualKeySpanStart = (node as Variable).keySpan.start.offset;
_expectSpanOffset(
'Variable key', 'start', actualKeySpanStart, keySpanStart);
}
if (keySpanEnd != null) {
var actualKeySpanEnd = (node as Variable).keySpan.end.offset;
_expectSpanOffset('Variable key', 'end', actualKeySpanEnd, keySpanEnd);
}
}
void _expectSection(MustachioNode node, Object matcher,
{bool invert = false,
int spanStart,
int spanEnd,
int keySpanStart,
int keySpanEnd}) {
expect(
node,
isA<Section>()
.having((e) => e.key, 'key', matcher)
.having((e) => e.invert, 'invert', invert));
if (spanStart != null) {
var actualSpanStart = (node as Section).span.start.offset;
_expectSpanOffset('Section', 'start', actualSpanStart, spanStart);
}
if (spanEnd != null) {
var actualSpanEnd = (node as Section).span.end.offset;
_expectSpanOffset('Section', 'end', actualSpanEnd, spanEnd);
}
if (keySpanStart != null) {
var actualKeySpanStart = (node as Section).keySpan.start.offset;
_expectSpanOffset('Section key', 'start', actualKeySpanStart, keySpanStart);
}
if (keySpanEnd != null) {
var actualKeySpanEnd = (node as Section).keySpan.end.offset;
_expectSpanOffset('Section key', 'end', actualKeySpanEnd, keySpanEnd);
}
}
void _expectPartial(MustachioNode node, Object matcher,
{int spanStart, int spanEnd, int keySpanStart, int keySpanEnd}) {
expect(node, isA<Partial>().having((e) => e.key, 'key', matcher));
if (spanStart != null) {
var actualSpanStart = (node as Partial).span.start.offset;
_expectSpanOffset('Partial', 'start', actualSpanStart, spanStart);
}
if (spanEnd != null) {
var actualSpanEnd = (node as Partial).span.end.offset;
_expectSpanOffset('Partial', 'end', actualSpanEnd, spanEnd);
}
if (keySpanStart != null) {
var actualKeySpanStart = (node as Partial).keySpan.start.offset;
_expectSpanOffset('Partial key', 'start', actualKeySpanStart, keySpanStart);
}
if (keySpanEnd != null) {
var actualKeySpanEnd = (node as Partial).keySpan.end.offset;
_expectSpanOffset('Partial key', 'end', actualKeySpanEnd, keySpanEnd);
}
}
void _expectSpanOffset(
String nodeType, String offsetType, int actualOffset, int expectedOffset) {
expect(actualOffset, expectedOffset,
reason:
'$nodeType span $offsetType offset expected to be $expectedOffset '
'but was $actualOffset');
}