requirejs-dustjs
Version:
A requirejs plugin for loading and compiling dustjs templates.
1,246 lines (1,240 loc) • 67.8 kB
JavaScript
var coreTests = [
{
name: "core tests",
tests: [
{
name: "streaming render",
source: "{#stream}{#delay}{.}{/delay}{/stream}",
context: (function(){
var d = 1;
return {
stream: function() {
return "asynchronous templates for the browser and node.js".split('');
},
delay: function(chunk, context, bodies) {
return chunk.map(function(chunk) {
setTimeout(function() {
chunk.render(bodies.block, context).end();
}, d++ * 15);
});
}
};
}),
expected: '',
message: "should test the stream rendering"
},
{
name: "hello_world",
source: "Hello World!",
context: {},
expected: "Hello World!",
message: "should test basic text rendering"
},
{
name: "global_template",
source: '{#helper foo="bar" boo="boo"} {/helper}',
context: { "helper": function(chunk, context, bodies, params)
{
currentTemplateName = context.getTemplateName();
return chunk.write(currentTemplateName);
}
},
expected: "global_template",
message: "should render the template name"
},
{
name: "apps/test/foo.tl&v=0.1",
source: '{#helper foo="bar" boo="boo" template="tl/apps/test"} {/helper}',
context: { "helper": function(chunk, context, bodies, params)
{
// top of the current stack
currentTemplateName = context.getTemplateName();
return chunk.write(currentTemplateName);
}
},
expected: "apps/test/foo.tl&v=0.1",
message: "should render the template name with paths"
},
{
name: "makeBase_missing_global",
source: '{#helper}{/helper}',
context: { "helper": function(chunk, context, bodies, params)
{
var newContext = {};
return bodies.block(chunk, dust.makeBase(newContext));
}
},
expected: "",
message: "should render the helper with missing global context"
},
{
name: "reference",
source: "{?one}{one}{/one}",
context: {"one": 0 },
expected: "0",
message: "should test a basic reference"
},
{
name: "implicit array",
source: "{#names}{.}{~n}{/names}",
context: { names: ["Moe", "Larry", "Curly"] },
expected: "Moe\nLarry\nCurly\n",
message: "should test an implicit array"
},
{
name: "inline param from outer scope",
source: "{#person foo=root}{foo}: {name}, {age}{/person}",
context: { root: "Subject", person: { name: "Larry", age: 45 } },
expected: "Subject: Larry, 45",
message: "should test renaming a key"
},
{
name: "force local",
source: "{#person}{.root}: {name}, {age}{/person}",
context: { root: "Subject", person: { name: "Larry", age: 45 } },
expected: ": Larry, 45",
message: "should test force a key"
},
{
name: "filter un-escape",
source: "{safe|s}{~n}{unsafe}",
context: { safe: "<script>alert('Hello!')</script>", unsafe: "<script>alert('Goodbye!')</script>" },
expected: "<script>alert('Hello!')</script>\n<script>alert('Goodbye!')</script>",
message: "should test escaped characters"
},
{
name: "escape pragma",
source: "{%esc:s}\n {unsafe}{~n}\n {%esc:h}\n {unsafe}\n {/esc}\n{/esc}",
context: { unsafe: "<script>alert('Goodbye!')</script>" },
expected: "<script>alert('Goodbye!')</script>\n<script>alert('Goodbye!')</script>",
message: "should test escape_pragma"
},
{
name: "use . for creating a block and set params",
source: "{#. test=\"you\"}{name} {test}{/.}",
context: { name: "me"},
expected: "me you",
message: ". creating a block"
},
{
name: "functions in context",
source: "Hello {type} World!",
context: {
type: function(chunk) {
return "Sync";
}
},
expected: "Hello Sync World!",
message: "should functions in context"
},
{
name: "async functions in context",
source: "Hello {type} World!",
context: {
type: function(chunk) {
return chunk.map(function(chunk) {
dust.nextTick(function() {
chunk.end("Async");
});
});
}
},
expected: "Hello Async World!",
message: "should test functions in context"
},
{
name: "sync chunk write test",
source: "Hello {type} World!",
context: {
type: function(chunk) {
return chunk.write("Chunky");
}
},
expected: "Hello Chunky World!",
message: "should test sync chunk write"
},
{
name: "base_template",
source: "Start{~n}" +
"{+title}" +
"Base Title" +
"{/title}" +
"{~n}" +
"{+main}" +
"Base Content" +
"{/main}" +
"{~n}" +
"End",
context: {},
expected: "Start\nBase Title\nBase Content\nEnd",
message: "should test base template"
},
{
name: "child_template",
source: "{^xhr}" +
"{>base_template/}" +
"{:else}" +
"{+main/}" +
"{/xhr}" +
"{<title}" +
"Child Title" +
"{/title}" +
"{<main}" +
"Child Content" +
"{/main}",
context: {xhr: false},
expected: "Start\nChild Title\nChild Content\nEnd",
message: "should test child template"
},
{
name: "issue322",
source: 'hi{+"{name}"/}',
context: {},
expected: "hi",
message: "should setup base template for next test. hi should not be part of base block name"
},
{
name: "issue322 use base template picks up prefix chunk data",
source: '{>issue322 name="abc"/}' +
"{<abc}ABC{/abc}",
context: {},
expected: "hiABC",
message: "should use base template and honor name passed in"
},
{
name: "recursion",
source: "{name}{~n}{#kids}{>recursion:./}{/kids}",
context: {
name: '1',
kids: [
{
name: '1.1',
kids: [
{name: '1.1.1'}
]
}
]
},
expected: "1\n1.1\n1.1.1\n",
message: "should test recursion"
},
{
name: "comments",
source: "{!\n" +
" Multiline\n" +
" {#foo}{bar}{/foo}\n" +
"!}\n" +
"{!before!}Hello{!after!}",
context: {},
expected: "Hello",
message: "should test comments"
},
{
name: "context",
source: "{#list:projects}{name}{:else}No Projects!{/list}",
context: {
list: function(chunk, context, bodies) {
var items = context.current(),
len = items.length;
if (len) {
chunk.write("<ul>\n");
for (var i=0; i<len; i++) {
chunk = chunk.write("<li>")
.render(bodies.block, context.push(items[i]))
.write("</li>\n");
}
return chunk.write("</ul>");
} else if (bodies['else']) {
return chunk.render(bodies['else'], context);
}
return chunk;
},
projects: [
{name: "Mayhem"},
{name: "Flash"},
{name: "Thunder"}
]
},
expected: "<ul>\n" +
"<li>Mayhem</li>\n" +
"<li>Flash</li>\n" +
"<li>Thunder</li>\n" +
"</ul>",
message: "should test the context"
}
]
},
{
name: "truth/falsy tests",
tests: [
{
name: "false value in context is treated as empty, same as undefined",
source: "{false}",
context: { "false": false },
expected: "",
message: "should test for false in the context, evaluated and prints nothing"
},
{
name: "numeric 0 value in context is treated as non empty",
source: "{zero}",
context: { "zero": 0 },
expected: "0",
message: "should test for numeric zero in the context, prints the numeric zero"
},
{
name: "empty string context is treated as empty",
source: "{emptyString}",
context: { "emptyString": "" },
expected: "",
message: "should test emptyString, prints nothing"
},
{
name: "empty string, single quoted in context is treated as empty",
source: "{emptyString}",
context: { "emptyString": '' },
expected: "",
message: "should test emptyString single quoted, prints nothing"
},
{
name: "null in the context treated as empty",
source: "{NULL}",
context: { "NULL": null },
expected: "",
message: "should test null in the context treated as empty"
},
{
name: "undefined in the context treated as empty",
source: "{UNDEFINED}",
context: {"UNDEFINED": undefined },
expected: "",
message: "should test undefined in the context treated as empty"
},
{
name: "undefined string in the context treated as non empty",
source: "{UNDEFINED}",
context: {"UNDEFINED": "undefined"},
expected: "undefined",
message: "should test string undefined in the context as non empty"
},
{
name: "null is treated as empty in exists",
source: "{?scalar}true{:else}false{/scalar}",
context: {"scalar": null},
expected: "false",
message: "should test null as empty in exists section"
},
{
name: "undefined is treated as empty in exists",
source: "{?scalar}true{:else}false{/scalar}",
context: {"scalar": undefined},
expected: "false",
message: "should test null treated as empty in exists"
},
{
name: "null is treated as truthy in not exists",
source: "{^scalar}true{:else}false{/scalar}",
context: {"scalar": null},
expected: "true",
message: "should test null as truthy in not exists"
},
{
name: "undefined is treated as truthy in not exists",
source: "{^scalar}true{:else}false{/scalar}",
context: {"scalar": undefined},
expected: "true",
message: "should test undefined as truthy in not exists"
},
{
name: "undefined is treated as empty in exists",
source: "{?scalar}true{:else}false{/scalar}",
context: {"scalar": undefined},
expected: "false",
message: "should test null treated as empty in exists"
}
]
},
{
name: "scalar data tests",
tests: [
{
name: "scalar null in a # section",
source: "{#scalar}true{:else}false{/scalar}",
context: {"scalar": null},
expected: "false",
message: "should test for a scalar null in a # section"
},
{
name: "scalar numeric 0 in a # section",
source: "{#scalar}true{:else}false{/scalar}",
context: {"scalar": 0},
expected: "true",
message: "should test for a scalar numeric 0 in a # section"
},
{
name: "scalar numeric non-zero in a # section",
source: "{#scalar}true{:else}false{/scalar}",
context: {"scalar": 42},
expected: "true",
message: "should test for a scalar numeric non-zero in a # section"
},
{
name: "scalar non empty string in a # section",
source: "{#scalar}true{:else}false{/scalar}",
context: {"scalar": 'abcde'},
expected: "true",
message: "should test for a scalar string in a # section"
},
{
name: "scalar non empty string in a # section",
source: "{#scalar}{.}{:else}false{/scalar}",
context: {"scalar": 'abcde'},
expected: "abcde",
message: "should test for a scalar string in a # section"
},
{
name: "missing scalar value",
source: "{#scalar}true{:else}false{/scalar}",
context: {"foo": 0},
expected: "false",
message: "should test a missing/undefined scalar value"
},
{
name: "scalar true value in the # section",
source: "{#scalar}true{:else}false{/scalar}",
context: {"scalar": true},
expected: "true",
message: "shoud test for scalar true value in the # section"
},
{
name: "scalar false value in the # section",
source: "{#scalar}true{:else}false{/scalar}",
context: {"scalar": false},
expected: "false",
message: "shoud test for scalar false value in the # section"
},
{
name: "scalar values true and false are supported in # nor else blocks ",
source: "{#foo}" +
"foo,{~s}" +
"{:else}" +
"not foo,{~s}" +
"{/foo}" +
"{#bar}" +
"bar!" +
"{:else}" +
"not bar!" +
"{/bar}",
context: { foo: true, bar: false },
expected: "foo, not bar!",
message:"should test scalar values true and false are supported in # nor else blocks"
}
]
},
{
name: "empty data tests",
tests: [
{
name: "empty array is treated as empty in exists",
source: "{?array}true{:else}false{/array}",
context: {"array": []},
expected: "false",
message: "empty array is treated as empty in exists"
},
{
name: "empty {} is treated as non empty in exists",
source: "{?object}true{:else}false{/object}",
context: {"object": {}},
expected: "true",
message: "empty {} is treated as non empty in exists"
},
{
name: "empty array is treated as empty in a section",
source: "{#array}true{:else}false{/array}",
context: {"array": []},
expected: "false",
message: "empty array is treated as empty in a section"
},
{
name: "empty {} is treated as non empty in a section",
source: "{#object}true{:else}false{/object}",
context: {"object": {}},
expected: "true",
message: "empty {} is treated as non empty"
},
{
name: "non-empty array in a reference",
source: "{array}",
context: {"array": ['1','2']},
expected: "1,2",
message: "non-empty array in a reference"
},
{
name: "null string in the context treated as non empty",
source: "{NULL}",
context: {"NULL": "null" },
expected: "null",
message: "should test null string in the context treated as non empty"
},
{
name: "string 0 value in context is treated as non empty",
source: "{zero}",
context: {"zero": "0" },
expected: "0",
message: "should test for string zero in the context, prints zero"
},
{
name: "empty array",
source: "{#names}{title} {name}{~n}{/names}",
context: { title: "Sir", names: [] },
expected: "",
message: "should test an empty array"
}
]
},
{
name: "array/index-access tests",
tests: [
{
name: "array",
source: "{#names}{title} {name}{~n}{/names}",
context: { title: "Sir", names: [ { name: "Moe" }, { name: "Larry" }, { name: "Curly" } ] },
expected: "Sir Moe\nSir Larry\nSir Curly\n",
message: "should test an array"
},
{
name: "accessing array element by index when element value is a primitive",
source: '{do.re[0]}',
context: { "do": { "re" : ["hello!","bye!"] } },
expected: "hello!",
message: "should return a specific array element by index when element value is a primitive"
},
{
name: "accessing array by index when element value is a object",
source: '{do.re[0].mi}',
context: { "do": { "re" : [{"mi" : "hello!"},"bye!"] } },
expected: "hello!",
message: "should return a specific array element by index when element value is a object"
},
{
name: "accessing array by index when element is a nested object",
source: '{do.re[0].mi[1].fa}',
context: { "do": { "re" : [{"mi" : ["one", {"fa" : "hello!"}]},"bye!"] } },
expected: "hello!",
message: "should return a specific array element by index when element is a nested object"
},
{
name: "accessing array by index when element is list of primitives",
source: '{do[0]}',
context: { "do": ["lala", "lele"] },
expected: "lala",
message: "should return a specific array element by index when element is list of primitives"
},
{
name: "accessing array inside a loop using the current context",
source: '{#list3}{.[0].biz}{/list3}',
context: { "list3": [[ { "biz" : "123" } ], [ { "biz" : "345" } ]]},
expected: "123345",
message: "should return a specific array element using the current context"
},
{
name: "array: reference $idx in iteration on objects",
source: "{#names}({$idx}).{title} {name}{~n}{/names}",
context: { title: "Sir", names: [ { name: "Moe" }, { name: "Larry" }, { name: "Curly" } ] },
expected: "(0).Sir Moe\n(1).Sir Larry\n(2).Sir Curly\n",
message: "array: reference $idx in iteration on objects"
},
{
name: "array: reference $len in iteration on objects",
source: "{#names}Size=({$len}).{title} {name}{~n}{/names}",
context: { title: "Sir", names: [ { name: "Moe" }, { name: "Larry" }, { name: "Curly" } ] },
expected: "Size=(3).Sir Moe\nSize=(3).Sir Larry\nSize=(3).Sir Curly\n",
message: "test array: reference $len in iteration on objects"
},
{
name: "array reference $idx in iteration on simple type",
source: "{#names}({$idx}).{title} {.}{~n}{/names}",
context: { title: "Sir", names: [ "Moe", "Larry", "Curly" ] },
expected: "(0).Sir Moe\n(1).Sir Larry\n(2).Sir Curly\n",
message: "test array reference $idx in iteration on simple types"
},
{
name: "array reference $len in iteration on simple type",
source: "{#names}Size=({$len}).{title} {.}{~n}{/names}",
context: { title: "Sir", names: [ "Moe", "Larry", "Curly" ] },
expected: "Size=(3).Sir Moe\nSize=(3).Sir Larry\nSize=(3).Sir Curly\n",
message: "test array reference $len in iteration on simple types"
},
{
name: "array reference $idx/$len on empty array case",
source: "{#names}Idx={$idx} Size=({$len}).{title} {.}{~n}{/names}",
context: { title: "Sir", names: [ ] },
expected: "",
message: "test array reference $idx/$len on empty array case"
},
{
name: "array reference $idx/$len on single element case (scalar case)",
source: "{#name}Idx={$idx} Size={$len} {.}{/name}",
context: { name: "Just one name" },
expected: "Idx= Size= Just one name",
message: "test array reference $idx/$len on single element case"
},
{
name: "array reference $idx/$len {#.} section case",
source: "{#names}{#.}{$idx}{.} {/.}{/names}",
context: { names: ["Moe", "Larry", "Curly"] },
expected: "0Moe 1Larry 2Curly ",
message: "test array reference $idx/$len {#.} section case"
},
{
name: "array reference $idx/$len not changed in nested object",
source: "{#results}{#info}{$idx}{name}-{$len} {/info}{/results}",
context: { results: [ {info: {name: "Steven"} }, {info: {name: "Richard"} } ] },
expected: "0Steven-2 1Richard-2 ",
message: "test array reference $idx/$len not changed in nested object"
},
{
name: "array reference $idx/$len nested loops",
source: "{#A}A loop:{$idx}-{$len},{#B}B loop:{$idx}-{$len}C[0]={.C[0]} {/B}A loop trailing: {$idx}-{$len}{/A}",
context: {"A": [ {"B": [ {"C": ["Ca1", "C2"]}, {"C": ["Ca2", "Ca22"]} ] }, {"B": [ {"C": ["Cb1", "C2"]}, {"C": ["Cb2", "Ca2"]} ] } ] },
expected: "A loop:0-2,B loop:0-2C[0]=Ca1 B loop:1-2C[0]=Ca2 A loop trailing: 0-2A loop:1-2,B loop:0-2C[0]=Cb1 B loop:1-2C[0]=Cb2 A loop trailing: 1-2",
message: "test array reference $idx/$len nested loops"
},
{
name: "using idx in array reference Accessing",
source: "{#list4} {name} {number[$idx]} {$idx}{/list4}",
context: { list4: [ {name:"Dog", number: [1,2,3]}, {name:"Cat", number: [4,5,6]}] },
expected: " Dog 1 0 Cat 5 1",
message: "should test the array reference access with idx"
},
{
name: "using len in array reference Accessing",
source: "{#list4} {name} {number[$len]}{/list4}",
context: { list4: [ {name:"Dog", number: [1,2,3]}, {name:"Cat", number: [4,5,6]}] },
expected: " Dog 3 Cat 6",
message: "should test the array reference access with len"
},
{
name: "using idx in array reference Accessing",
source: "{#list3}{.[$idx].biz}{/list3}",
context: { "list3": [[ { "biz" : "123" } ], [ { "biz" : "345" }, { "biz" : "456" } ]]},
expected: "123456",
message: "should test the array reference access with idx and current context"
},
{
name: "using len in array reference Accessing",
source: "{#list3}{.[$len].idx}{/list3}",
context: { "list3": [
[{"idx": "0"},
{"idx": "1"},
{"idx": "2"}],
[{"idx": "0"},
{"idx": "1"},
{"idx": "2"}]
]
},
expected: "22",
message: "should test the array reference access with len and current context"
},
{
name: "using idx in double nested array",
source: "{#test}{#.}{.}i:{$idx}l:{$len},{/.}{/test}",
context: { "test": [[ 1,2,3 ]]},
expected: "1i:0l:3,2i:1l:3,3i:2l:3,",
message: "should test double nested array and . reference: issue #340"
}
]
},
{
name: "object tests",
tests: [
{
name: "object",
source: "{#person}{root}: {name}, {age}{/person}",
context: { root: "Subject", person: { name: "Larry", age: 45 } },
expected: "Subject: Larry, 45",
message: "should test an object"
},
{
name: "path",
source: "{foo.bar}",
context: { foo: {bar: "Hello!"} },
expected: "Hello!",
message: "should test an object path"
}
]
},
{
name: "conditional tests",
tests: [
{
name: "conditional",
source: "{?tags}" +
"<ul>{~n}" +
"{#tags}" +
"{~s} <li>{.}</li>{~n}" +
"{/tags}" +
"</ul>" +
"{:else}" +
"No Tags!" +
"{/tags}" +
"{~n}" +
"{^likes}" +
"No Likes!" +
"{:else}" +
"<ul>{~n}" +
"{#likes}" +
"{~s} <li>{.}</li>{~n}" +
"{/likes}" +
"</ul>" +
"{/likes}",
context: { tags: [], likes: ["moe", "larry", "curly", "shemp"] },
expected: "No Tags!\n" +
"<ul>\n" +
" <li>moe</li>\n" +
" <li>larry</li>\n" +
" <li>curly</li>\n" +
" <li>shemp</li>\n" +
"</ul>",
message: "should test conditional tags"
},
{
name: "empty else block",
source: "{#foo}full foo{:else}empty foo{/foo}",
context: { foo: []},
expected: "empty foo",
message: "should test else block when array empty"
}
]
},
{
name: "nested path tests",
tests: [
{
name: "Verify local mode leading dot path in local mode",
source: "{#people}{.name} is {?.age}{.age} years old.{:else}not telling us their age.{/age}{/people}",
context: {
"name": "List of people",
"age": "8 hours",
"people": [
{ "name": "Alice" },
{ "name": "Bob", "age": 42 },
]
},
expected: "Alice is not telling us their age.Bob is 42 years old.",
message: "should test the leading dot behavior in local mode"
},
{
name: "check nested ref in global does not work in local mode",
source: "{glob.globChild}",
context: { },
expected: "",
message: "Should not find glob.globChild which is in context.global"
},
{
name: "Verify leading dot path not affected in global mode",
source: "{#people}{.name} is {?.age}{.age} years old.{:else}not telling us their age.{/age}{/people}",
options: {pathScope: "global"},
context: {
"name": "List of people",
"age": "8 hours",
"people": [
{ "name": "Alice" },
{ "name": "Bob", "age": 42 },
]
},
expected: "Alice is not telling us their age.Bob is 42 years old.",
message: "should test the leading dot behavior preserved"
},
{
name: "Standard dotted path with falsey value. Issue 317",
source: "{foo.bar}",
options: {pathScope: "global"},
context: { foo: {bar: 0} },
expected: "0",
message: "should work when value at end of path is falsey"
},
{
name: "dotted path resolution up context",
source: "{#data.A.list}Aname{data.A.name}{/data.A.list}",
options: {pathScope: "global"},
context: { "data":{"A":{"name":"Al","list":[{"name": "Joe"},{"name": "Mary"}],"B":{"name":"Bob","Blist":["BB1","BB2"]}}} },
expected: "AnameAlAnameAl",
message: "should test usage of dotted path resolution up context"
},
{
name: "dotted path resolution up context 2",
source: "{#data.A.B.Blist}Aname{data.A.name}{/data.A.B.Blist}",
options: {pathScope: "global"},
context: { "data":{"A":{"name":"Al","list":[{"name": "Joe"},{"name": "Mary"}],"B":{"name":"Bob","Blist":["BB1","BB2"]}}} },
expected: "AnameAlAnameAl",
message: "should test usage of dotted path resolution up context"
},
{
name: "dotted path resolution without explicit context",
source: "{#data.A}Aname{name}{data.C.name}{/data.A}",
options: {pathScope: "global"},
context: { "data":{"A":{"name":"Al","list":[{"name": "Joe"},{"name": "Mary"}],"B":{"name":"Bob","Blist":["BB1","BB2"]}},C:{name:"cname"}} },
expected: "AnameAlcname",
message: "should test usage of dotted path resolution up context"
},
{
name: "same as previous test but with explicit context",
source: "{#data.A:B}Aname{name}{data.C.name}{/data.A}",
options: {pathScope: "global"},
context: { "data":{"A":{"name":"Al","list":[{"name": "Joe"},{"name": "Mary"}],"B":{"name":"Bob","Blist":["BB1","BB2"]}},C:{name:"cname"}} },
expected: "AnameAl",
message: "should test explicit context blocks looking further up stack"
},
{
name: "explicit context but gets value from global",
source: "{#data.A:B}Aname{name}{glob.globChild}{/data.A}",
options: {pathScope: "global"},
base: { glob: { globChild: "testGlobal"} },
context: { "data":{"A":{"name":"Al","list":[{"name": "Joe"},{"name": "Mary"}],"B":{"name":"Bob","Blist":["BB1","BB2"]}},C:{name:"cname"}} },
expected: "AnameAltestGlobal",
message: "should test access global despite explicit context"
},
{
name: "nested dotted path resolution",
source: "{#data.A.list}{#data.A.B.Blist}{.}Aname{data.A.name}{/data.A.B.Blist}{/data.A.list}",
options: {pathScope: "global"},
context: { "data":{"A":{"name":"Al","list":[{"name": "Joe"},{"name": "Mary"}],"B":{"name":"Bob","Blist":["BB1"]}}} },
expected: "BB1AnameAlBB1AnameAl",
message: "should test nested usage of dotted path resolution"
},
{
name: "check nested ref in global works in global mode",
source: "{glob.globChild}",
options: {pathScope: "global"},
base: { glob: { globChild: "testGlobal"} },
context: { },
expected: "testGlobal",
message: "Should find glob.globChild which is in context.global"
},
{
name: "dotted path resolution up context with partial match in current context",
source: "{#data}{#A}{C.name}{/A}{/data}",
options: {pathScope: "global"},
context: { "data":{ "A":{ "name":"Al", "B": "Ben", "C": { namex: "Charlie"} }, C: {name: "Charlie Sr."} } },
expected: "",
message: "should test usage of dotted path resolution up context"
},
{
name: "check nested ref not found in global if partial match",
source: "{#data}{#A}{C.name}{/A}{/data}",
options: {pathScope: "global"},
base: { C: {name: "Big Charlie"} },
context: { "data":{ "A":{ "name":"Al", "B": "Ben", "C": { namex: "Charlie"} }, C: {namey: "Charlie Sr."} } },
expected: "",
message: "Should find glob.globChild which is in context.global"
},
{
name: "method invocation",
source: "Hello {person.fullName}",
options: {pathScope: "global"},
context: {
person: {
firstName: "Peter",
lastName: "Jones",
fullName: function() {
return this.firstName + ' ' + this.lastName;
}
}
},
expected: "Hello Peter Jones",
message: "should test resolve correct 'this' when invoking method"
},
{
name: "check null values in section iteration don't break path resolution",
source: "{#nulls}{names[0].name}{/nulls}",
options: {pathScope: "global"},
context: { "nulls": [1, null, null, 2],"names": [{"name": "Moe"}, {"name": "Curly"}] },
expected: "MoeMoeMoeMoe",
message: "Should resolve path correctly"
},
{
name: "check falsey value in section iteration don't break path resolution",
source: "{#list}{a.b}{/list}",
options: {pathScope: "global"},
context: { "list": ['', 2, ''],"a": {"b": "B"} },
expected: "BBB",
message: "Should resolve path correctly"
},
{
name: "check true value in section iteration are also OK",
source: "{#list}{a.b}{/list}",
options: {pathScope: "global"},
context: { "list": [true, 2, true],"a": {"b": "B"} },
expected: "BBB",
message: "Should resolve path correctly"
}
]
},
{
name: "filter tests",
tests: [
{
name: "filter",
source: "{#filter}foo {bar}{/filter}",
context: {
filter: function(chunk, context, bodies) {
return chunk.tap(function(data) {
return data.toUpperCase();
}).render(bodies.block, context).untap();
},
bar: "bar"
},
expected: "FOO BAR",
message: "should test the filter tag"
},
{
name: "invalid filter",
source: "{obj|nullcheck|invalid}",
context: { obj: "test" },
error: "Invalid filter [nullcheck]",
message: "should fail hard for invalid filter"
},
{
name: "escapeJs filter without DQ",
source: "{obj|j|s}",
context: { obj: "<script>\\testBS\\ \rtestCR\r \u2028testLS\u2028 \u2029testPS\u2029 \ntestNL\n \ftestLF\f 'testSQ' \ttestTB\t /testFS/</script>" },
expected: "<script>\\\\testBS\\\\ \\rtestCR\\r \\u2028testLS\\u2028 \\u2029testPS\\u2029 \\ntestNL\\n \\ftestLF\\f \\'testSQ\\' \\ttestTB\\t \\/testFS\\/<\\/script>",
message: "should escapeJs a string when using the j filter"
},
{
name: "escapeJs filter with only DQ",
source: "{obj|j|s}",
context: { obj: '"testDQ"' },
expected: '\\"testDQ\\"',
message: "should escapeJs a string with double quotes when using the j filter"
},
{
name: "JSON.stringify filter",
source: "{obj|js|s}",
context: { obj: { id: 1, name: "bob", occupation: "construction" } },
expected: JSON.stringify({ id: 1, name: "bob", occupation: "construction" }),
message: "should stringify a JSON literal when using the js filter"
},
{
name: "JSON.parse filter",
source: "{obj|jp}",
context: { obj: JSON.stringify({ id: 1, name: "bob", occupation: "construction" }) },
expected: JSON.parse(JSON.stringify({ id: 1, name: "bob", occupation: "construction" })).toString(),
message: "should objectify a JSON string when using the jp filter"
}
]
},
{
name: "partial definitions",
tests: [
{
name: "partial",
source: "Hello {name}! You have {count} new messages.",
context: { name: "Mick", count: 30 },
expected: "Hello Mick! You have 30 new messages.",
message: "should test a basic replace in a template"
},
{
name: "partial_with_blocks",
source: "{+header}default header {/header}Hello {name}! You have {count} new messages.",
context: { name: "Mick", count: 30 },
expected: "default header Hello Mick! You have 30 new messages.",
message: "should test a block with defaults"
},
{
name: "partial_with_blocks_and_no_defaults",
source: "{+header/}Hello {name}! You have {count} new messages.",
context: { name: "Mick", count: 30 },
expected: "Hello Mick! You have 30 new messages.",
message: "should test a blocks with no defaults"
},
{
name: "partial_print_name",
source: "{#helper}{/helper}",
context: {},
expected: "",
message: "should test a template with missing helper"
},
{
name: "nested_partial_print_name",
source: "{>partial_print_name/}",
context: {},
expected: "",
message: "should test a template with missing helper"
},
{
name: "nested_nested_partial_print_name",
source: "{>nested_partial_print_name/}",
context: {},
expected: "",
message: "should test nested partial"
}
]
},
{
name: "partial/params tests",
tests : [
{
name: "partials",
source: '{>partial/} {>"hello_world"/} {>"{ref}"/}',
context: { name: "Jim", count: 42, ref: "hello_world" },
expected: "Hello Jim! You have 42 new messages. Hello World! Hello World!",
message: "should test partials"
},
{
name: "partial with context",
source: "{>partial:.profile/}",
context: { profile: { name: "Mick", count: 30 } },
expected: "Hello Mick! You have 30 new messages.",
message: "should test partial with context"
},
{
name: "partial with blocks, with no default values for blocks",
source: '{>partial_with_blocks_and_no_defaults/}',
context: { name: "Mick", count: 30 },
expected: "Hello Mick! You have 30 new messages.",
message: "partial with blocks, with no default values for blocks"
},
{
name: "partial with blocks, with no default values for blocks, but override default values with inline partials",
source: '{>partial_with_blocks_and_no_defaults/}{<header}override header {/header}',
context: { name: "Mick", count: 30 },
expected: "override header Hello Mick! You have 30 new messages.",
message: "partial with blocks, with no default values for blocks, but override default values with inline partials"
},
{
name: "partial with blocks, override default values with inline partials",
source: '{>partial_with_blocks/}{<header}my header {/header}',
context: { name: "Mick", count: 30 },
expected: "my header Hello Mick! You have 30 new messages.",
message: "partial with blocks, override default values with inline partials"
},
{
name: "partial with inline params",
source: '{>partial name=n count="{c}"/}',
context: { n: "Mick", c: 30 },
expected: "Hello Mick! You have 30 new messages.",
message: "should test partial with inline params"
},
{
name: "partial with inline params tree walk up",
source: '{#a}{#b}{#c}{#d}{>partial name=n count="{x}"/}{/d}{/c}{/b}{/a}',
context: { n: "Mick", x: 30, a:{b:{c:{d:{e:"1"}}}} },
expected: "Hello Mick! You have 30 new messages.",
message: "should test partial with inline params tree walk up"
},
{
name: "partial with inline params and context",
source: '{>partial:profile name="{n}" count="{c}"/}',
context: { profile: { n: "Mick", c: 30 } },
expected: "Hello Mick! You have 30 new messages.",
message: "should test partial with inline params and context"
},
{
name: "partial with inline params and context tree walk up",
source: '{#profile}{#a}{#b}{#c}{#d}{>partial:profile name=n count="{x}"/}{/d}{/c}{/b}{/a}{/profile}',
context: { profile:{ n: "Mick", x: 30, a:{b:{c:{d:{e:"1"}}}} } },
expected: "Hello Mick! You have 30 new messages.",
message: "should test partial with inline params and context tree walk up"
},
{
name: "partial with literal inline param and context",
source: '{>partial:profile name="Joe" count="99"/}',
context: { profile: { n: "Mick", count: 30 } },
expected: "Hello Joe! You have 30 new messages.",
message: "should test partial with literal inline param and context. Fallback values for name or count are undefined"
},
{
name: "partial with blocks and inline params",
source: '{>partial_with_blocks name=n count="{c}"/}',
context: { n: "Mick", c: 30 },
expected: "default header Hello Mick! You have 30 new messages.",
message: "should test partial with blocks and inline params"
},
{
name: "partial with blocks, override default values for blocks and inline params",
source: '{>partial_with_blocks name=n count="{c}"/}{<header}my header {/header}',
context: { n: "Mick", c: 30 },
expected: "my header Hello Mick! You have 30 new messages.",
message: "should test partial with blocks, override default values for blocks and inline params"
},
{
name: "partial with blocks and no defaults, override default values for blocks and inline params",
source: '{>partial_with_blocks_and_no_defaults name=n count="{c}"/}{<header}my header {/header}',
context: { n: "Mick", c: 30 },
expected: "my header Hello Mick! You have 30 new messages.",
message: "should test partial blocks and no defaults, override default values for blocks and inline params"
},
{
name: "partial with no blocks, ignore the override inline partials",
source: '{>partial name=n count="{c}"/}{<header}my header {/header}',
context: { n: "Mick", c: 30 },
expected: "Hello Mick! You have 30 new messages.",
message: "should test partial with no blocks, ignore the override inline partials"
},
{
name: "partial prints the current template name",
source: '{>partial_print_name/}',
context: { "helper": function(chunk, context, bodies, params)
{
currentTemplateName = context.getTemplateName();
return chunk.write(currentTemplateName);
}
},
expected: "partial_print_name",
message: "should print the current template name"
},
{
name: "partial prints the current dynamic template name",
source: '{>"{partial_print_name}"/}',
context: { "helper": function(chunk, context, bodies, params)
{
currentTemplateName = context.getTemplateName();
return chunk.write(currentTemplateName);
},
"partial_print_name" : "partial prints the current template name"
},
expected: "partial_print_name",
message: "should print the current dynamic template name"
},
{
name: "nested partial prints the current template name",
source: '{>nested_partial_print_name/}',
context: { "helper": function(chunk, context, bodies, params)
{
currentTemplateName = context.getTemplateName();
return chunk.write(currentTemplateName);
}
},
expected: "partial_print_name",
message: "should print the current template name"
},
{
name: "nested partial 2 levels deep from loadSource prints the current template name",
source: ['{#loadTemplate name="{contentTemplate}" source="{contentSource|s}"}{/loadTemplate}',
'{#loadTemplate name="{parentTemplate}" source="{parentSource|s}"}{/loadTemplate}',
'{>"{parentTemplate}"/} | additional parent output'].join("\n"),
context: { "loadTemplate": function(chunk, context, bodies, params)
{
var source = dust.testHelpers.tap(params.source, chunk, context),
name = dust.testHelpers.tap(params.name, chunk, context);
dust.loadSource(dust.compile(source, name));
return chunk.write('');
},
"printTemplateName": function(chunk, context, bodies, params)
{
return chunk.write(context.getTemplateName());
},
"parentTemplate": "parent",
"parentSource": "{?undefinedVar}{:else}{>\"content\"/}{/undefinedVar}",
"contentTemplate": "content",
"contentSource": "templateName: {#printTemplateName}{/printTemplateName} output: additional output"
},
expected: "templateName: content output: additional output | additional parent output",
message: "should print the current template name with some additional output"
},
{
name: "partial with makeBase_missing_global",
source: '{#helper template="partial"}{/helper}',
context: { "helper": function(chunk, context, bodies, params)
{
var newContext = {};
return chunk.partial(params.template, dust.makeBase(newContext));
}
},
expected: "Hello ! You have new messages.",
message: "should render the helper with missing global context"
},
]
},
{
name: "inline params tests",
tests: [
{
name: "params",
source: "{#helper foo=\"bar\"/}",
context: {
helper: function(chunk, context, bodies, params) {
return chunk.write(params.foo);
}
},
expected: "bar",
message: "should test inner params"
},
{
name: "inline params as integer",
source: "{#helper foo=10 /}",
context: { helper: function(chunk, context, bodies, params) { return chunk.write(params.foo); } },
expected: "10",
message: "Block handlers syntax should support integer number parameters"
},
{
name: "inline params as float",
source: "{#helper foo=3.14159 /}",
context: { helper: function(chunk, context, bodies, params) { return chunk.write(params.foo); } },
expected: "3.14159",
message: "Block handlers syntax should support decimal number parameters"
},
{
name: "inline params with dashes",
source: "{#helper data-foo=\"dashes\" /}",
context: { helper: function(chunk, context, bodies, params) { return chunk.write(params['data-foo']); } },
expected: "dashes",
message: "should test parameters with dashes"
}
]
},
{
name: "inline partial/block tests",
tests: [
{
name: "blocks with dynamic keys",
source: ['{<title_A}',
'AAA',
'{/title_A}',
'{<title_B}',
'BBB',
'{/title_B}',
'{+"title_{val}"/}'].join("\n"),
context: { "val" : "A" },
expected: "AAA",
message: "should test blocks with dynamic keys"
},
{
name: "blocks with more than one dynamic keys",
source: ['{<title_A}',
'AAA',
'{/title_A}',
'{<title_B}',
'BBB',
'{/title_B}',
'{+"{val1}_{val2}"/}'].join("\n"),
context: { "val1" : "title", "val2" : "A" },
expected: "AAA",
message: "should test blocks with more than one dynamic keys"
},
{
name: "blocks with dynamic key values as objects",
source: ['{<title_A}',
'AAA',
'{/title_A}',
'{<title_B}',
'BBB',
'{/title_B}',
'{+"{val1}_{obj.name}"/}'].join("\n"),
context: { "val1" : "title", "val2" : "A", "obj" : { "name" : "B" } },
expected: "BBB",
message: "should test blocks with dynamic key values as objects"
},
{
name: "blocks with dynamic key values as arrays",
source: ['{<title_A}',
'AAA',
'{/title_A}',
'{<title_B}',
'BBB',
'{/title_B}',
'{+"{val1}_{obj.name[0]}"/}'].join("\n"),
context: { "val1" : "title", "val2" : "A", "obj" : { "name" : ["A", "B"] } },
expected: "AAA",
message: "should test blocks with dynamic key values as arrays