const section_test = TestList{
name: 'Sections'
overview: ''
tests: [
TestCase{
name: 'True Boolean Section'
desc: 'True boolean sections should have their contents displayed.'
template: '"{{+boolean}}This should be rendered.{{/boolean}}"'
expected: '"This should be rendered."'
data: DataModel({
'boolean': DataModel(true)
})
},
TestCase{
name: 'False Boolean Section'
desc: 'False boolean sections should not have their contents displayed.'
template: '"{{+boolean}}This should not be rendered.{{/boolean}}"'
expected: '""'
data: DataModel({
'boolean': DataModel(false)
})
},
TestCase{
name: 'Guard Sections Positive'
desc: 'Use positive boolean sections to indicate presence of data.'
data: DataModel({
'has_name': DataModel(true)
'name': DataModel('Joe')
})
template: '"{{+has_name}}Hi {{name}}.{{/has_name}}"'
expected: '"Hi Joe."'
},
TestCase{
name: 'Guard Sections Negative'
desc: 'Use positive negative sections to indicate presence of data.'
data: DataModel({
'has_name': DataModel(false)
})
template: '"{{+has_name}}Hi {{name}}.{{/has_name}}{{-has_name}}Hi user.{{/has_name}}"'
expected: '"Hi user."'
},
TestCase{
name: 'Simple Map Section'
desc: 'Simple map keys can be queried properly.'
data: DataModel({
'person': DataModel({
'name': DataModel('Subhomoy')
'age': '24'
'indian': true
})
})
template: '{{#person}}
My name is {{name}}.
I am {{age}} years old.
{{+indian}}I'm from India{{/indian}}
{{/person}}
'
expected: '
My name is Subhomoy.
I am 24 years old.
I'm from India
'
},
TestCase{
name: 'Context'
desc: 'Objects and hashes should be pushed onto the context stack.'
data: DataModel({
'context': DataModel({
'name': DataModel('Joe')
})
})
template: '"{{#context}}Hi {{name}}.{{/context}}"'
expected: '"Hi Joe."'
},
TestCase{
name: 'Parent Context'
desc: 'Names missing in the current context are looked up in the stack.'
data: DataModel({
'a': DataModel('foo')
'b': 'wrong'
'sec': {
'b': DataModel('bar')
}
'c': {
'd': DataModel('baz')
}
})
template: '"{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}"'
expected: '"foo, bar, baz"'
},
TestCase{
name: 'Variable Test'
desc: 'Valid boolean sections should have their value available at the top of the context stack. This value must be accessible by {{.}}'
data: DataModel({
'check1': DataModel(false)
'check2': true
})
template: '{{-check1}}{{.}}{{/check1}} and {{+check2}}{{.}}{{/check2}}'
expected: 'false and true'
},
TestCase{
name: 'Deeply Nested Contexts'
desc: ''
data: DataModel({
'a': DataModel({
'one': DataModel('1')
})
'b': {
'two': DataModel('2')
}
'c': {
'three': DataModel('3')
'd': {
'four': DataModel('4')
'five': DataModel('5')
}
}
})
template: '
{{#a}}
{{one}}
{{#b}}
{{one}}{{two}}{{one}}
{{#c}}
{{one}}{{two}}{{three}}{{two}}{{one}}
{{#d}}
{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}
{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}
{{one}}{{two}}{{three}}{{four}}{{five}}6{{five}}{{four}}{{three}}{{two}}{{one}}
{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}
{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}
{{/d}}
{{one}}{{two}}{{three}}{{two}}{{one}}
{{/c}}
{{one}}{{two}}{{one}}
{{/b}}
{{one}}
{{/a}}
'
expected: '
1
121
12321
1234321
123454321
12345654321
123454321
1234321
12321
121
1
'
},
TestCase{
name: 'Doubled'
desc: 'Multiple sections per template should be permitted.'
data: DataModel({
'bool': DataModel(true)
'two': DataModel('second')
})
template: '
{{+bool}}
* first
{{/bool}}
* {{two}}
{{+bool}}
* third
{{/bool}}
'
expected: '
* first
* second
* third
'
},
TestCase{
name: 'Empty List'
desc: 'Empty list should be skipped over.'
data: DataModel({
'sample': DataModel('vibe')
'empty': []DataModel{}
})
template: '{{sample}}{{*empty}}{{sample}}{{/empty}}'
expected: 'vibe'
},
TestCase{
name: 'List'
desc: 'Lists should be iterated; list items should visit the context stack.'
data: DataModel({
'list': DataModel([DataModel({
'item': DataModel('1')
}), {
'item': DataModel('2')
}, {
'item': DataModel('3')
}])
})
template: '"{{*list}}{{item}}{{/list}}"'
expected: '"123"'
},
TestCase{
name: 'Empty List 2'
desc: 'Empty list should be skipped over.'
data: DataModel({
'list': DataModel([]DataModel{})
})
template: '"{{*list}}Yay lists!{{/list}}"'
expected: '""'
},
TestCase{
name: 'Nested (True Boolean)'
desc: 'Nested true boolean sections should have their contents rendered.'
data: DataModel({
'bool': DataModel(true)
})
template: '| A {{+bool}}B {{+bool}}C{{/bool}} D{{/bool}} E |'
expected: '| A B C D E |'
},
TestCase{
name: 'Nested (False Boolean)'
desc: 'Nested false boolean sections should be omitted.'
data: DataModel({
'bool': DataModel(false)
})
template: '| A {{+bool}}B {{+bool}}C{{/bool}} D{{/bool}} E |'
expected: '| A E |'
},
TestCase{
name: 'Implicit Iterator - String'
desc: 'Implicit iterators should directly interpolate strings.'
data: DataModel({
'list': DataModel([DataModel('a'), 'b', 'c', 'd', 'e'])
})
template: '"{{*list}}({{.}}){{/list}}"'
expected: '"(a)(b)(c)(d)(e)"'
},
TestCase{
name: 'Implicit Iterator - Boolean'
desc: 'Implicit iterators should directly interpolate booleans.'
data: DataModel({
'list': DataModel([DataModel(true), false, true, true, false])
})
template: '"{{*list}}({{.}}){{/list}}"'
expected: '"(true)(false)(true)(true)(false)"'
},
TestCase{
name: 'Implicit Iterator - Array'
desc: 'Implicit iterators should allow iterating over nested arrays.'
data: DataModel({
'list': DataModel([DataModel([DataModel('1'), '2', '3']),
DataModel([DataModel('a'), 'b', 'c'])])
})
template: '"{{*list}}({{*.}}{{.}}{{/.}}){{/list}}"'
expected: '"(123)(abc)"'
},
TestCase{
name: 'Dotted Names'
desc: 'Dotted Names should be valid for boolean section tags'
data: DataModel({
'a': DataModel({
'b': DataModel({
'c': DataModel(true)
'd': DataModel(false)
})
})
})
template: '"{{+a.b.c}}A{{/a.b.c}}{{-a.b.d}}B{{/a.b.d}}"'
expected: '"AB"'
},
TestCase{
name: 'Surrounding Whitespace'
desc: 'Sections should not alter surrounding whitespace.'
data: DataModel({
'boolean': DataModel(true)
})
template: ' | {{+boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n'
expected: ' | \n | \n'
},
TestCase{
name: 'Indented Inline Sections'
desc: 'Single-line sections should not alter surrounding whitespace.'
data: DataModel({
'boolean': DataModel(true)
})
template: ' {{+boolean}}YES{{/boolean}}\n {{+boolean}}GOOD{{/boolean}}\n'
expected: ' YES\n GOOD\n'
},
TestCase{
name: 'Standalone Lines'
desc: 'Standalone lines should be removed from the template.'
data: DataModel({
'boolean': DataModel(true)
})
template: '|
| This Is
{{+boolean}}
|
{{/boolean}}
| A Line'
expected: '|
| This Is
|
| A Line'
},
TestCase{
name: 'Indented Standalone Lines'
desc: 'Indented standalone lines should be removed from the template.'
data: DataModel({
'boolean': DataModel(true)
})
template: '
| This Is
{{+boolean}}
|
{{/boolean}}
| A Line'
expected: '
| This Is
|
| A Line'
},
TestCase{
name: 'Standalone Line Endings'
desc: '"\r\n" should be considered a newline for standalone tags.'
data: DataModel({
'boolean': DataModel(true)
})
template: '|\r\n{{+boolean}}\r\n{{/boolean}}\r\n|'
expected: '|\r\n|'
},
TestCase{
name: 'Standalone Without Previous Line'
desc: 'Standalone tags should not require a newline to precede them.'
data: DataModel({
'boolean': DataModel(true)
})
template: ' {{+boolean}}\n#{{/boolean}}\n/'
expected: '#\n/'
},
TestCase{
name: 'Standalone Without Newline'
desc: 'Standalone tags should not require a newline to follow them.'
data: DataModel({
'boolean': DataModel(true)
})
template: '#{{+boolean}}\n/\n {{/boolean}}'
expected: '#\n/\n'
},
TestCase{
name: 'Padding'
desc: 'Superfluous in-tag whitespace should be ignored.'
data: DataModel({
'boolean': DataModel(true)
})
template: '|{{+ boolean }}={{/ boolean }}|'
expected: '|=|'
},
TestCase{
name: 'Empty List As Boolean Section'
desc: 'Using empty lists in boolean sections should behave as if the boolean value was false.'
data: DataModel({
'list': DataModel([]DataModel{})
})
template: '{{+list}}Should not render.{{/list}}{{-list}}Should render.{{/list}}'
expected: 'Should render.'
},
TestCase{
name: 'Non-empty List As Boolean Section'
desc: 'Using non-empty lists in boolean sections should behave as if the boolean value was true.'
data: DataModel({
'list': DataModel([DataModel('hi')])
})
template: '{{+list}}Should render.{{/list}}{{-list}}Should not render.{{/list}}'
expected: 'Should render.'
},
TestCase{
name: 'Map As Boolean Section'
desc: 'Using maps in boolean sections should behave as if the boolean value was true.'
data: DataModel({
'map': DataModel({
'foo': DataModel('bar')
})
})
template: '{{+map}}Should render.{{/map}}{{-map}}Should not render.{{/map}}'
expected: 'Should render.'
},
TestCase{
name: 'Empty Map As Boolean Section'
desc: 'Using empty maps in boolean sections should behave as if the boolean value was false.'
data: DataModel({
'map': DataModel(map[string]DataModel{})
})
template: '{{+map}}Should not render.{{/map}}{{-map}}Should render.{{/map}}'
expected: 'Should render.'
},
TestCase{
name: 'Iterating On Maps (Full)'
desc: 'Iterating on maps should give access to key-value pairs one at a time.'
data: DataModel({
'map': DataModel({
'one': DataModel('ONE')
'two': DataModel('TWO')
'three': DataModel('THREE')
})
})
template: '{{*map}}{{key}}:{{value}}\n{{/map}}'
expected: 'one:ONE\ntwo:TWO\nthree:THREE\n'
},
TestCase{
name: 'Iterating On Maps (Empty)'
desc: 'Iterating on empty maps should behave like empty lists.'
data: DataModel({
'map': DataModel(map[string]DataModel{})
})
template: '{{*map}}Should not render: {{key}}{{value}}{{/map}}{{-map}}Should render.{{/map}}'
expected: 'Should render.'
},
]
}
const inverted_test = TestList{
name: 'Inverted Sections'
overview: ''
tests: [
TestCase{
name: 'False Boolean Value'
desc: 'False Boolean sections should have their contents rendered.'
data: DataModel({
'boolean': DataModel(false)
})
template: '"{{-boolean}}This should be rendered.{{/boolean}}"'
expected: '"This should be rendered."'
},
TestCase{
name: 'True Boolean Value'
desc: 'True sections should have their contents omitted.'
data: DataModel({
'boolean': DataModel(true)
})
template: '"{{-boolean}}This should not be rendered.{{/boolean}}"'
expected: '""'
},
TestCase{
name: 'Doubled'
desc: 'Multiple inverted sections per template should be permitted.'
data: DataModel({
'bool': DataModel(false)
'two': 'second'
})
template: '
{{-bool}}
* first
{{/bool}}
* {{two}}
{{-bool}}
* third
{{/bool}}
'
expected: '
* first
* second
* third
'
},
TestCase{
name: 'Nested (False)'
desc: 'Nested false sections should have their contents rendered.'
data: DataModel({
'bool': DataModel(false)
})
template: '| A {{-bool}}B {{-bool}}C{{/bool}} D{{/bool}} E |'
expected: '| A B C D E |'
},
TestCase{
name: 'Nested (True)'
desc: 'Nested true sections should be omitted.'
data: DataModel({
'bool': DataModel(true)
})
template: '| A {{-bool}}B {{-bool}}C{{/bool}} D{{/bool}} E |'
expected: '| A E |'
},
TestCase{
name: 'Dotted Names - True'
desc: 'Dotted names should be valid for Negative Section tags.'
data: DataModel({
'a': DataModel({
'b': DataModel({
'c': DataModel(true)
})
})
})
template: '"{{-a.b.c}}Not Here{{/a.b.c}}" == ""'
expected: '"" == ""'
},
TestCase{
name: 'Dotted Names - False'
desc: 'Dotted names should be valid for Negative Section tags.'
data: DataModel({
'a': DataModel({
'b': DataModel({
'c': DataModel(false)
})
})
})
template: '"{{-a.b.c}}Not Here{{/a.b.c}}" == "Not Here"'
expected: '"Not Here" == "Not Here"'
},
TestCase{
name: 'Surrounding Whitespace'
desc: 'Negative sections should not alter surrounding whitespace.'
data: DataModel({
'boolean': DataModel(false)
})
template: ' | {{-boolean}}\t|\t{{/boolean}} | \n'
expected: ' | \t|\t | \n'
},
TestCase{
name: 'Internal Whitespace'
desc: 'Negative section should not alter internal whitespace.'
data: DataModel({
'boolean': DataModel(false)
})
template: ' | {{-boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n'
expected: ' | \n | \n'
},
TestCase{
name: 'Indented Inline Sections'
desc: 'Single-line sections should not alter surrounding whitespace.'
data: DataModel({
'boolean': DataModel(false)
})
template: ' {{-boolean}}NO{{/boolean}}\n {{-boolean}}WAY{{/boolean}}\n'
expected: ' NO\n WAY\n'
},
TestCase{
name: 'Standalone Lines'
desc: 'Standalone lines should be removed from the template.'
data: DataModel({
'boolean': DataModel(false)
})
template: '
| This Is
{{-boolean}}
|
{{/boolean}}
| A Line
'
expected: '
| This Is
|
| A Line
'
},
TestCase{
name: 'Standalone Indented Lines'
desc: 'Standalone indented lines should be removed from the template.'
data: DataModel({
'boolean': DataModel(false)
})
template: '
| This Is
{{-boolean}}
|
{{/boolean}}
| A Line
'
expected: '
| This Is
|
| A Line
'
},
TestCase{
name: 'Standalone Line Endings'
desc: '"\r\n" should be considered a newline for standalone tags.'
data: DataModel({
'boolean': DataModel(false)
})
template: '|\r\n{{-boolean}}\r\n{{/boolean}}\r\n|'
expected: '|\r\n|'
},
TestCase{
name: 'Standalone Without Previous Line'
desc: 'Standalone tags should not require a newline to precede them.'
data: DataModel({
'boolean': DataModel(false)
})
template: ' {{-boolean}}\n^{{/boolean}}\n/'
expected: '^\n/'
},
TestCase{
name: 'Standalone Without Newline'
desc: 'Standalone tags should not require a newline to follow them.'
data: DataModel({
'boolean': DataModel(false)
})
template: '^{{-boolean}}\n/\n {{/boolean}}'
expected: '^\n/\n'
},
TestCase{
name: 'Padding'
desc: 'Superfluous in-tag whitespace should be ignored.'
data: DataModel({
'boolean': DataModel(false)
})
template: '|{{- boolean }}={{/ boolean }}|'
expected: '|=|'
},
]
}
const delimiter_tests = TestList{
name: 'Delimiters'
overview: '
Delimiter swap tags are used to change the tag delimiters for all content
following the tag in the current compilation unit.
The tag's content must be any two non-whitespace sequences separated by
whitespace except am equals sign ('=') followed by the current closing
delimiter.
Delimiter swap tags can be standalone tags.
'
tests: [
TestCase{
name: 'Pair Behaviour'
desc: 'The equals sign (used on both sides) should permit delimiter changes.'
data: {
'text': DataModel('Hey!')
}
template: '{{=<% %>=}}(<%text%>)'
expected: '(Hey!)'
},
TestCase{
name: 'Special Characters'
desc: 'Characters with special meaning regexen should be valid delimiters.'
data: {
'text': DataModel('It worked!')
}
template: '({{=[ ]=}}[text])'
expected: '(It worked!)'
},
TestCase{
name: 'Sections'
desc: 'Delimiters set outside sections should persist.'
data: {
'section': DataModel(true)
'data': 'I got interpolated.'
}
template: '
[
{{+section}}
{{data}}
|data|
{{/section}}
{{= | | =}}
|+section|
{{data}}
|data|
|/section|
]
'
expected: '
[
I got interpolated.
|data|
{{data}}
I got interpolated.
]
'
},
TestCase{
name: 'Inverted Sections'
desc: 'Delimiters set outside inverted sections should persist.'
data: {
'section': DataModel(false)
'data': 'I got interpolated.'
}
template: '
[
{{-section}}
{{data}}
|data|
{{/section}}
{{= | | =}}
|-section|
{{data}}
|data|
|/section|
]
'
expected: '
[
I got interpolated.
|data|
{{data}}
I got interpolated.
]
'
},
TestCase{
name: 'Partial Inheritance'
desc: 'Delimiters set in a parent template should not affect a partial.'
data: DataModel({
'value': DataModel('yes')
})
partials: {
'include': '.{{value}}.'
}
template: '
[ {{>include}} ]
{{= | | =}}
[ |>include| ]
'
expected: '
[ .yes. ]
[ .yes. ]
'
},
TestCase{
name: 'Post-Partial Behavior'
desc: 'Delimiters set in a partial should not affect the parent template.'
data: DataModel({
'value': DataModel('yes')
})
partials: {
'include': '.{{value}}. {{= | | =}} .|value|.'
}
template: '
[ {{>include}} ]
[ .{{value}}. .|value|. ]
'
expected: '
[ .yes. .yes. ]
[ .yes. .|value|. ]
'
},
TestCase{
name: 'Surrounding Whitespace'
desc: 'Surrounding whitespace should be left untouched.'
template: '| {{=@ @=}} |'
expected: '| |'
},
TestCase{
name: 'Outlying Whitespace (Inline)'
desc: 'Whitespace should be left untouched.'
template: ' | {{=@ @=}}\n'
expected: ' | \n'
},
TestCase{
name: 'Standalone Tag'
desc: 'Standalone lines should be removed from the template.'
template: '
Begin.
{{=@ @=}}
End.
'
expected: '
Begin.
End.
'
},
TestCase{
name: 'Indented Standalone Tag'
desc: 'Indented standalone lines should be removed from the template.'
template: '
Begin.
{{=@ @=}}
End.
'
expected: '
Begin.
End.
'
},
TestCase{
name: 'Standalone Line Endings'
desc: '"\r\n" should be considered a newline for standalone tags.'
template: '|\r\n{{= @ @ =}}\r\n|'
expected: '|\r\n|'
},
TestCase{
name: 'Standalone Without Previous Line'
desc: 'Standalone tags should not require a newline to precede them.'
template: ' {{=@ @=}}\n='
expected: '='
},
TestCase{
name: 'Standalone Without Newline'
desc: ' Standalone tags should not require a newline to follow them.'
template: '=\n {{=@ @=}}'
expected: '=\n'
},
TestCase{
name: 'Pair with Padding'
desc: ' Superfluous in-tag whitespace should be ignored.'
template: '|{{= @ @ =}}|'
expected: '||'
},
]
}
const comment_tests = TestList{
name: 'Comments'
overview: '
Comment tags represent content that should never appear in the resulting output.
The tag's content may contain any sequence of characters (including
whitespace and newlines) except the active closing delimiter.
Comment tags can act as standalone tags (taking up a line, indented, etc).
'
tests: [
TestCase{
name: 'Inline'
desc: 'Comment blocks should be removed from the template.'
template: '12345{{! Comment Block! }}67890'
expected: '1234567890'
},
TestCase{
name: 'Multiline'
desc: 'Multiline comments should be permitted.'
template: '12345{{!\n This is a\n multi-line comment...\n}}67890\n'
expected: '1234567890\n'
},
TestCase{
name: 'Standalone'
desc: 'All standalone comment lines should be removed.'
template: 'Begin.\n{{! Comment Block! }}\nEnd.\n'
expected: 'Begin.\nEnd.\n'
},
TestCase{
name: 'Indented Standalone'
desc: 'All standalone comment lines should be removed.'
template: 'Begin.\n {{! Indented Comment Block! }}\nEnd.\n'
expected: 'Begin.\nEnd.\n'
},
TestCase{
name: 'Standalone Line Endings'
desc: '"\\r\\n" should be considered a newline for standalone tags.'
template: '|\r\n{{! Standalone Comment }}\r\n|'
expected: '|\r\n|'
},
TestCase{
name: 'Standalone Without Previous Line'
desc: 'Standalone tags should not require a newline to precede them.'
template: ' {{! I'm Still Standalone }}\n!'
expected: '!'
},
TestCase{
name: 'Standalone Without Newline'
desc: 'Standalone tags should not require a newline to follow them.'
template: '!\n {{! I'm Still Standalone }}'
expected: '!\n'
},
TestCase{
name: 'Multiline Standalone'
desc: 'All standalone comment lines should be removed.'
template: 'Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n'
expected: 'Begin.\nEnd.\n'
},
TestCase{
name: 'Indented Multiline Standalone'
desc: 'All standalone comment lines should be removed.'
template: 'Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n'
expected: 'Begin.\nEnd.\n'
},
TestCase{
name: 'Indented Inline'
desc: 'Inline comments should not strip whitespace.'
template: ' 12 {{! 34 }}\n'
expected: ' 12 \n'
},
TestCase{
name: 'Surrounding Whitespace'
desc: 'Comment removal should preserve surrounding whitespace.'
template: '12345 {{! Comment Block! }} 67890'
expected: '12345 67890'
},
TestCase{
name: 'Variable Name Collision'
desc: 'Comments must never render, even if variable with same name exists.'
data: {
'! comment': DataModel('1')
'! comment ': '2'
'!comment': '3'
'comment': '4'
}
template: 'comments never show: >{{! comment }}<'
expected: 'comments never show: ><'
},
]
}
const interpolation_test = TestList{
name: 'Interpolation'
overview: 'Interpolation tags are used to integrate dynamic content into the template.
The tag's content MUST be a non-whitespace character sequence NOT containing
the current closing delimiter.'
tests: [
TestCase{
name: 'No Interpolation'
desc: 'Mustache-free templates should render as-is.'
template: '
Hello from {Mustache}!
'
expected: '
Hello from {Mustache}!
'
},
TestCase{
name: 'Basic Interpolation'
desc: 'Unadorned tags should interpolate content into the template.'
data: DataModel({
'subject': DataModel('world')
})
template: '
Hello, {{subject}}!
'
expected: '
Hello, world!
'
},
TestCase{
name: 'HTML Escaping'
desc: 'Basic interpolation should be HTML escaped.'
data: {
'forbidden': DataModel('& " < >')
}
template: '
These characters should be HTML escaped: {{forbidden}}
'
expected: '
These characters should be HTML escaped: & " < >
'
},
TestCase{
name: 'Triple Mustache'
desc: 'Triple mustaches should interpolate without HTML escaping.'
data: DataModel({
'forbidden': DataModel('& " < >')
})
template: '
These characters should not be HTML escaped: {{{forbidden}}}
'
expected: '
These characters should not be HTML escaped: & " < >
'
},
TestCase{
name: 'Ampersand'
desc: 'Ampersand should interpolate without HTML escaping.'
data: DataModel({
'forbidden': DataModel('& " < >')
})
template: '
These characters should not be HTML escaped: {{&forbidden}}
'
expected: '
These characters should not be HTML escaped: & " < >
'
},
TestCase{
name: 'Dotted Names - Basic Interpolation'
desc: 'Dotted names should be considered a form of shorthand for sections.'
data: DataModel({
'person': DataModel({
'name': DataModel('Joe')
})
})
template: '"{{person.name}}" == "{{#person}}{{name}}{{/person}}"'
expected: '"Joe" == "Joe"'
},
TestCase{
name: 'Dotted Names - Triple Mustache Interpolation'
desc: 'Dotted names should be considered a form of shorthand for sections.'
data: DataModel({
'person': DataModel({
'name': DataModel('Joe')
})
})
template: '"{{{person.name}}}" == "{{#person}}{{{name}}}{{/person}}"'
expected: '"Joe" == "Joe"'
},
TestCase{
name: 'Dotted Names - Ampersand Interpolation'
desc: 'Dotted names should be considered a form of shorthand for sections.'
data: DataModel({
'person': DataModel({
'name': DataModel('Joe')
})
})
template: '"{{&person.name}}" == "{{#person}}{{&name}}{{/person}}"'
expected: '"Joe" == "Joe"'
},
TestCase{
name: 'Dotted Names - Arbitrary Depth'
desc: 'Dotted names should be functional to any level of nesting.'
data: DataModel({
'a': DataModel({
'b': DataModel({
'c': DataModel({
'd': DataModel({
'e': DataModel({
'name': DataModel('Phil')
})
})
})
})
})
})
template: '"{{a.b.c.d.e.name}}" == "Phil"'
expected: '"Phil" == "Phil"'
},
TestCase{
name: 'Dotted Names - Initial Resolution'
desc: 'The first part of a dotted name should resolve as any other name.'
data: DataModel({
'a': DataModel({
'b': DataModel({
'c': DataModel({
'd': DataModel({
'e': DataModel({
'name': DataModel('Phil')
})
})
})
})
})
'b': DataModel({
'c': DataModel({
'd': DataModel({
'e': DataModel({
'name': DataModel('Wrong')
})
})
})
})
})
template: '"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"'
expected: '"Phil" == "Phil"'
},
TestCase{
name: 'Dotted Names - Context Precedence'
desc: 'Dotted names should be resolved against former resolutions.'
data: DataModel({
'a': DataModel({
'b': DataModel({
'c': DataModel('YES')
})
})
'b': DataModel({
'c': DataModel('ERROR')
})
})
template: '{{#a}}{{b.c}}{{/a}}'
expected: 'YES'
},
TestCase{
name: 'Implicit Iterators - Basic Interpolation'
desc: 'Unadorned tags should interpolate content into the template.'
data: DataModel('world')
template: '
Hello, {{.}}!
'
expected: '
Hello, world!
'
},
TestCase{
name: 'Implicit Iterators - HTML Escaping'
desc: ' Basic interpolation should be HTML escaped.'
data: '& " < >'
template: '
These characters should be HTML escaped: {{.}}
'
expected: '
These characters should be HTML escaped: & " < >
'
},
TestCase{
name: 'Implicit Iterators - Triple Mustache'
desc: 'Triple mustaches should interpolate without HTML escaping.'
data: '& " < >'
template: '
These characters should not be HTML escaped: {{{.}}}
'
expected: '
These characters should not be HTML escaped: & " < >
'
},
TestCase{
name: 'Implicit Iterators - Ampersand'
desc: 'Ampersand should interpolate without HTML escaping.'
data: '& " < >'
template: '
These characters should not be HTML escaped: {{&.}}
'
expected: '
These characters should not be HTML escaped: & " < >
'
},
TestCase{
name: 'Interpolation - Surrounding Whitespace'
desc: 'Interpolation should not alter surrounding whitespace.'
data: DataModel({
'string': DataModel('---')
})
template: '| {{string}} |'
expected: '| --- |'
},
TestCase{
name: 'Triple Mustache - Surrounding Whitespace'
desc: 'Interpolation should not alter surrounding whitespace.'
data: DataModel({
'string': DataModel('---')
})
template: '| {{{string}}} |'
expected: '| --- |'
},
TestCase{
name: 'Ampersand - Surrounding Whitespace'
desc: 'Interpolation should not alter surrounding whitespace.'
data: DataModel({
'string': DataModel('---')
})
template: '| {{&string}} |'
expected: '| --- |'
},
TestCase{
name: 'Interpolation - Standalone'
desc: 'Standalone interpolation should not alter surrounding whitespace.'
data: DataModel({
'string': DataModel('---')
})
template: ' {{string}}\n'
expected: ' ---\n'
},
TestCase{
name: 'Triple Mustache - Standalone'
desc: 'Standalone interpolation should not alter surrounding whitespace.'
data: DataModel({
'string': DataModel('---')
})
template: ' {{{string}}}\n'
expected: ' ---\n'
},
TestCase{
name: 'Ampersand - Standalone'
desc: 'Standalone interpolation should not alter surrounding whitespace.'
data: DataModel({
'string': DataModel('---')
})
template: ' {{&string}}\n'
expected: ' ---\n'
},
TestCase{
name: 'Interpolation With Padding'
desc: 'Superfluous in-tag whitespace should be ignored.'
data: DataModel({
'string': DataModel('---')
})
template: '|{{ string }}|'
expected: '|---|'
},
TestCase{
name: 'Triple Mustache With Padding'
desc: 'Superfluous in-tag whitespace should be ignored.'
data: DataModel({
'string': DataModel('---')
})
template: '|{{{ string }}}|'
expected: '|---|'
},
TestCase{
name: 'Ampersand With Padding'
desc: 'Superfluous in-tag whitespace should be ignored.'
data: DataModel({
'string': DataModel('---')
})
template: '|{{& string }}|'
expected: '|---|'
},
TestCase{
name: 'Inner Brace Raw Tag With Delimiter Swap'
desc: 'If the delimiter has been swapped, respect raw tag with curly braces.'
data: DataModel('<OK>')
template: '{{=[ ]=}}[.][{.}]'
expected: '<OK><OK>'
},
TestCase{
name: 'Delimiter Swap With Ampersand'
desc: 'Ampersand should still work after delimiter swap.'
data: DataModel('<OK>')
template: '{{=[ ]=}}[.][&.]'
expected: '<OK><OK>'
},
]
}
const partial_test = TestList{
name: 'Partials'
overview: '
Partials are external templates that can be plugged into the current template.
'
tests: [
TestCase{
name: 'Basic Behaviour'
desc: 'The greater-than operator should expand to the named partial.'
template: '"{{>text}}"'
expected: '"from partial"'
partials: {
'text': 'from partial'
}
},
TestCase{
name: 'Context'
desc: 'The greater-than operator should operate within the current context.'
data: {
'text': DataModel('content')
}
template: '"{{>partial}}"'
partials: {
'partial': '*{{text}}*'
}
expected: '"*content*"'
},
TestCase{
name: 'Recursion'
desc: 'The greater-than operator should properly recurse.'
data: DataModel({
'content': DataModel('X')
'nodes': DataModel([
DataModel({
'content': DataModel('Y')
'nodes': []DataModel{}
}),
])
})
template: '{{>node}}'
partials: {
'node': '{{content}}<{{*nodes}}{{>node}}{{/nodes}}>'
}
expected: 'X<Y<>>'
},
TestCase{
name: 'Surrounding Whitespace'
desc: 'The greater-than operator should not alter surrounding whitespace.'
template: '| {{>partial}} |'
partials: {
'partial': '\t|\t'
}
expected: '| \t|\t |'
},
TestCase{
name: 'Inline Indentation'
desc: 'Whitespace should be left untouched.'
data: DataModel({
'data': DataModel('|')
})
partials: {
'partial': '>\n>'
}
template: ' {{data}} {{> partial}}\n'
expected: ' | >\n>\n'
},
TestCase{
name: 'Standalone Line Endings'
desc: '"\r\n" should be considered a newline for standalone tags.'
template: '|\r\n{{>partial}}\r\n|'
expected: '|\r\n>|'
partials: {
'partial': '>'
}
},
TestCase{
name: 'Standalone Without Previous Line'
desc: 'Standalone tags should not require a newline to precede them.'
template: ' {{>partial}}\n>'
expected: ' >\n>>'
partials: {
'partial': '>\n>'
}
},
TestCase{
name: 'Standalone Without Newline'
desc: 'Standalone tags should not require a newline to follow them.'
template: '>\n {{>partial}}'
expected: '>\n>\n>'
partials: {
'partial': '>\n>'
}
},
TestCase{
name: 'Indentation Not Preserved'
desc: 'Partials are not indented. Use an external formatter after template'
data: DataModel({
'content': DataModel('cc')
})
template: 'aa
{{>partial}}
aa'
expected: 'aa
bb
bb cc
bb
aa'
partials: {
'partial': 'bb
bb {{content}}
bb
'
}
},
TestCase{
name: 'Padding Whitespace'
desc: 'Superfluous in-tag whitespace should be ignored.'
template: '|{{> partial }}|'
partials: {
'partial': '[]'
}
expected: '|[]|'
},
]
}
struct TestCase {
pub:
name string
desc string
data DataModel = DataModel(false)
template string
expected string
partials map[string]string = {}
}
struct TestList {
pub:
name string
overview string
tests []TestCase
}