Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions specification/comments.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,9 @@ tests:
data: { }
template: '12345 {{! Comment Block! }} 67890'
expected: '12345 67890'

- name: Variable Name Collision
desc: Comments must never render, even if variable with same name exists.
data: { '! comment': 1, '! comment ': 2, '!comment': 3, 'comment': 4}
template: 'comments never show: >{{! comment }}<'
expected: 'comments never show: ><'
79 changes: 79 additions & 0 deletions specification/interpolation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ tests:
expected: |
Hello, world!

- name: No Re-interpolation
desc: Interpolated tag output should not be re-interpolated.
data: { template: '{{planet}}', planet: 'Earth' }
template: '{{template}}: {{planet}}'
expected: '{{planet}}: Earth'

- name: HTML Escaping
desc: Basic interpolation should be HTML escaped.
data: { forbidden: '& " < >' }
Expand Down Expand Up @@ -101,6 +107,24 @@ tests:
template: '"{{&power}} jiggawatts!"'
expected: '"1.21 jiggawatts!"'

- name: Basic Null Interpolation
desc: Nulls should interpolate as the empty string.
data: { cannot: null }
template: "I ({{cannot}}) be seen!"
expected: "I () be seen!"

- name: Triple Mustache Null Interpolation
desc: Nulls should interpolate as the empty string.
data: { cannot: null }
template: "I ({{{cannot}}}) be seen!"
expected: "I () be seen!"

- name: Ampersand Null Interpolation
desc: Nulls should interpolate as the empty string.
data: { cannot: null }
template: "I ({{&cannot}}) be seen!"
expected: "I () be seen!"

# Context Misses

- name: Basic Context Miss Interpolation
Expand Down Expand Up @@ -179,6 +203,61 @@ tests:
template: '{{#a}}{{b.c}}{{/a}}'
expected: ''

- name: Dotted Names are never single keys
desc: Dotted names shall not be parsed as single, atomic keys
data:
a.b: c
template: '{{a.b}}'
expected: ''

- name: Dotted Names - No Masking
desc: Dotted Names in a given context are unvavailable due to dot splitting
data:
a.b: c
a: { b: d }
template: '{{a.b}}'
expected: 'd'

# Implicit Iterators

- name: Implicit Iterators - Basic Interpolation
desc: Unadorned tags should interpolate content into the template.
data: "world"
template: |
Hello, {{.}}!
expected: |
Hello, world!

- 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: &amp; &quot; &lt; &gt;

- 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: & " < >

- 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: & " < >

- name: Implicit Iterators - Basic Integer Interpolation
desc: Integers should interpolate seamlessly.
data: 85
template: '"{{.}} miles an hour!"'
expected: '"85 miles an hour!"'

# Whitespace Sensitivity

- name: Interpolation - Surrounding Whitespace
Expand Down
6 changes: 6 additions & 0 deletions specification/inverted.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ tests:
template: '"{{^boolean}}This should not be rendered.{{/boolean}}"'
expected: '""'

- name: Null is falsey
desc: Null is falsey.
data: { "null": null }
template: '"{{^null}}This should be rendered.{{/null}}"'
expected: '"This should be rendered."'

- name: Context
desc: Objects and hashes should behave like truthy values.
data: { context: { name: 'Joe' } }
Expand Down
7 changes: 7 additions & 0 deletions specification/partials.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ tests:
partials: { node: '{{content}}<{{#nodes}}{{>node}}{{/nodes}}>' }
expected: 'X<Y<>>'

- name: Nested
desc: The greater-than operator should work from within partials.
data: { a: "hello", b: "world" }
template: '{{>outer}}'
partials: { outer: '*{{a}} {{>inner}}*', inner: '{{b}}!' }
expected: '*hello world!*'

# Whitespace Sensitivity

- name: Surrounding Whitespace
Expand Down
94 changes: 81 additions & 13 deletions specification/sections.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
overview: |
Section tags and End Section tags are used in combination to wrap a section
of the template for iteration
of the template for iteration.

These tags' content MUST be a non-whitespace character sequence NOT
containing the current closing delimiter; each Section tag MUST be followed
by an End Section tag with the same content within the same section.

This tag's content names the data to replace the tag. Name resolution is as
This tag's content names the data to replace the tag. Name resolution is as
follows:
1) Split the name on periods; the first part is the name to resolve, any
1) If the name is a single period (.), the data is the item currently
sitting atop the context stack. Skip the rest of these steps.
2) Split the name on periods; the first part is the name to resolve, any
remaining parts should be retained.
2) Walk the context stack from top to bottom, finding the first context
3) Walk the context stack from top to bottom, finding the first context
that is a) a hash containing the name as a key OR b) an object responding
to a method with the given name.
3) If the context is a hash, the data is the value associated with the
4) If the context is a hash, the data is the value associated with the
name.
4) If the context is an object and the method with the given name has an
5) If the context is an object and the method with the given name has an
arity of 1, the method SHOULD be called with a String containing the
unprocessed contents of the sections; the data is the value returned.
5) Otherwise, the data is the value returned by calling the method with
6) Otherwise, the data is the value returned by calling the method with
the given name.
6) If any name parts were retained in step 1, each should be resolved
7) If any name parts were retained in step 1, each should be resolved
against a context stack containing only the result from the former
resolution. If any part fails resolution, the result should be considered
falsey, and should interpolate as the empty string.

If the data is not of a list type, it is coerced into a list as follows: if
the data is truthy (e.g. `!!data == true`), use a single-element list
containing the data, otherwise use an empty list.
Expand All @@ -47,20 +50,54 @@ tests:
template: '"{{#boolean}}This should not be rendered.{{/boolean}}"'
expected: '""'

- name: Null is falsey
desc: Null is falsey.
data: { "null": null }
template: '"{{#null}}This should not be rendered.{{/null}}"'
expected: '""'

- name: Context
desc: Objects and hashes should be pushed onto the context stack.
data: { context: { name: 'Joe' } }
template: '"{{#context}}Hi {{name}}.{{/context}}"'
expected: '"Hi Joe."'

- name: Parent contexts
desc: Names missing in the current context are looked up in the stack.
data: { a: "foo", b: "wrong", sec: { b: "bar" }, c : { d : "baz" } }
template: '"{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}"'
expected: '"foo, bar, baz"'

- name: Variable test
desc: |
Non-false sections have their value at the top of context,
accessible as {{.}} or through the parent context. This gives
a simple way to display content conditionally if a variable exists.
data: { foo: "bar" }
template: '"{{#foo}}{{.}} is {{foo}}{{/foo}}"'
expected: '"bar is bar"'

- name: List Contexts
desc: All elements on the context stack should be accessible within lists.
data:
tops:
- tname:
upper: "A"
lower: "a"
middles:
- mname: "1"
bottoms:
- bname: "x"
- bname: "y"
template: '{{#tops}}{{#middles}}{{tname.lower}}{{mname}}.{{#bottoms}}{{tname.upper}}{{mname}}{{bname}}.{{/bottoms}}{{/middles}}{{/tops}}'
expected: 'a1.A1x.A1y.'

- name: Deeply Nested Contexts
desc: All elements on the context stack should be accessible.
data:
a: { one: 1 }
b: { two: 2 }
c: { three: 3 }
d: { four: 4 }
e: { five: 5 }
c: { three: 3, d : { four : 4, five : 5 } }
template: |
{{#a}}
{{one}}
Expand All @@ -70,9 +107,11 @@ tests:
{{one}}{{two}}{{three}}{{two}}{{one}}
{{#d}}
{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}
{{#e}}
{{#five}}
{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}
{{one}}{{two}}{{three}}{{four}}{{.}}6{{.}}{{four}}{{three}}{{two}}{{one}}
{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}
{{/e}}
{{/five}}
{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}
{{/d}}
{{one}}{{two}}{{three}}{{two}}{{one}}
Expand All @@ -87,6 +126,8 @@ tests:
12321
1234321
123454321
12345654321
123454321
1234321
12321
121
Expand Down Expand Up @@ -168,6 +209,33 @@ tests:
template: '"{{#list}}({{#.}}{{.}}{{/.}}){{/list}}"'
expected: '"(123)(abc)"'

- name: Implicit Iterator - HTML Escaping
desc: Implicit iterators with basic interpolation should be HTML escaped.
data:
list: [ '&', '"', '<', '>' ]
template: '"{{#list}}({{.}}){{/list}}"'
expected: '"(&amp;)(&quot;)(&lt;)(&gt;)"'

- name: Implicit Iterator - Triple mustache
desc: Implicit iterators in triple mustache should interpolate without HTML escaping.
data:
list: [ '&', '"', '<', '>' ]
template: '"{{#list}}({{{.}}}){{/list}}"'
expected: '"(&)(")(<)(>)"'

- name: Implicit Iterator - Ampersand
desc: Implicit iterators in an Ampersand tag should interpolate without HTML escaping.
data:
list: [ '&', '"', '<', '>' ]
template: '"{{#list}}({{&.}}){{/list}}"'
expected: '"(&)(")(<)(>)"'

- name: Implicit Iterator - Root-level
desc: Implicit iterators should work on root-level lists.
data: [ { value: 'a' }, { value: 'b' } ]
template: '"{{#.}}({{value}}){{/.}}"'
expected: '"(a)(b)"'

# Dotted Names

- name: Dotted Names - Truthy
Expand Down
Loading