mirror of
https://github.com/twbs/bootstrap.git
synced 2024-12-04 16:24:22 +01:00
848 lines
28 KiB
JavaScript
848 lines
28 KiB
JavaScript
/*
|
|
* Copyright 2011 Twitter, Inc.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
var Hogan = Hogan || require('../lib/hogan')
|
|
, doc = this["document"]
|
|
|
|
function testScanTextNoTags() {
|
|
var text = "<h2>hi</h2>";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 1, "One token");
|
|
is(tokens[0]+'', text, "text is equal to first token");
|
|
}
|
|
|
|
function testScanOneTag() {
|
|
var text = "{{hmm}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 1, "One token");
|
|
is(tokens[0].n, "hmm", "First token content is variable name.");
|
|
}
|
|
|
|
function testScanMultipleTags() {
|
|
var text = "asdf{{hmm}}asdf2{{hmm2}}asdf3";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 5, "3 text tokens, 2 tag tokens.");
|
|
is(tokens[0]+'', "asdf", "first token is text");
|
|
is(tokens[1].n, "hmm", "second token is tag");
|
|
is(tokens[1].tag, "_v", "second token is a variable");
|
|
is(tokens[2]+'', "asdf2", "third token is text");
|
|
is(tokens[3].n, "hmm2", "fourth token is tag");
|
|
is(tokens[3].tag, "_v", "fourth token is a variable");
|
|
is(tokens[4]+'', "asdf3", "Fifth token is text");
|
|
}
|
|
|
|
function testScanSectionOpen() {
|
|
var text = "{{#hmm}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 1, "One token");
|
|
is(tokens[0].n, "hmm", "First token content is variable name.");
|
|
is(tokens[0].tag, "#", "First token is a section.");
|
|
}
|
|
|
|
function testScanSectionClose() {
|
|
var text = "{{/hmm}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 1, "One token");
|
|
is(tokens[0].n, "hmm", "First token content is variable name.");
|
|
is(tokens[0].tag, "/", "First token is a section.");
|
|
}
|
|
|
|
function testScanSection() {
|
|
var text = "{{#hmm}}{{/hmm}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 2, "One token");
|
|
is(tokens[0].n, "hmm", "First token content is variable name.");
|
|
is(tokens[0].tag, "#", "First token is a section.");
|
|
is(tokens[1].n, "hmm", "Second token content is variable name.");
|
|
is(tokens[1].tag, "/", "Second token is a section.");
|
|
}
|
|
|
|
function testScanSectionInContent() {
|
|
var text = "abc{{#hmm}}def{{/hmm}}ghi";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 5, "3 text tokens, 2 tag tokens.");
|
|
is(tokens[0]+'', "abc", "first token is text");
|
|
is(tokens[1].n, "hmm", "second token is tag");
|
|
is(tokens[1].tag, "#", "second token is a variable");
|
|
is(tokens[2]+'', "def", "third token is text");
|
|
is(tokens[3].n, "hmm", "fourth token is tag");
|
|
is(tokens[3].tag, "/", "fourth token is a variable");
|
|
is(tokens[4]+'', "ghi", "Fifth token is text");
|
|
}
|
|
|
|
function testScanNegativeSection() {
|
|
var text = "{{^hmm}}{{/hmm}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 2, "One token");
|
|
is(tokens[0].n, "hmm", "First token content is variable name.");
|
|
is(tokens[0].tag, "^", "First token is a negative section.");
|
|
is(tokens[1].n, "hmm", "First token content is variable name.");
|
|
is(tokens[1].tag, "/", "Second token is a section.");
|
|
}
|
|
|
|
function testScanPartial() {
|
|
var text = "{{>hmm}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 1, "One token");
|
|
is(tokens[0].n, "hmm", "First token content is variable name.");
|
|
is(tokens[0].tag, ">", "First token is a partial.");
|
|
}
|
|
|
|
|
|
function testScanBackwardPartial() {
|
|
var text = "{{<hmm}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 1, "One token");
|
|
is(tokens[0].n, "hmm", "First token content is variable name.");
|
|
is(tokens[0].tag, "<", "First token is a backward partial.");
|
|
}
|
|
|
|
function testScanAmpersandNoEscapeTag() {
|
|
var text = "{{&hmm}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 1, "One token");
|
|
is(tokens[0].n, "hmm", "First token content is variable name.");
|
|
is(tokens[0].tag, "&", "First token is an ampersand no-escape.");
|
|
}
|
|
|
|
function testScanTripleStache() {
|
|
var text = "{{{hmm}}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 1, "One token");
|
|
is(tokens[0].n, "hmm", "First token content is variable name.");
|
|
is(tokens[0].tag, "{", "First token is a triple-stache.");
|
|
}
|
|
|
|
function testScanSectionWithTripleStacheInside() {
|
|
var text = "a{{#yo}}b{{{hmm}}}c{{/yo}}d";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 7, "One token");
|
|
is(tokens[0]+'', "a", "First token content is correct text.");
|
|
is(tokens[1].n, "yo", "Second token content is correct text.");
|
|
is(tokens[1].tag, "#", "Second token is a section.");
|
|
is(tokens[2]+'', "b", "Third token content is correct text.");
|
|
is(tokens[3].n, "hmm", "Fourth token content is correct text.");
|
|
is(tokens[3].tag, "{", "Fourth token is a triple stache.");
|
|
is(tokens[4]+'', "c", "Fifth token content is correct text.");
|
|
is(tokens[5].n, "yo", "Sixth token content is correct text.");
|
|
is(tokens[5].tag, "/", "Sixth token is a close.");
|
|
is(tokens[6]+'', "d", "Seventh token content is correct text.");
|
|
}
|
|
|
|
function testScanSetDelimiter() {
|
|
var text = "a{{=<% %>=}}b";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 2, "change delimiter doesn't appear as token.");
|
|
is(tokens[0]+'', "a", "text before change delimiter is processed.");
|
|
is(tokens[1]+'', "b", "text after change delimiter is processed.");
|
|
}
|
|
|
|
function testScanResetDelimiter() {
|
|
var text = "a{{=<% %>=}}b<%hmm%>c<%={{ }}=%>d{{hmm}}";
|
|
var tokens = Hogan.scan(text);
|
|
is(tokens.length, 6, "8 tokens, delimiter changes don't count.");
|
|
is(tokens[0]+'', "a", "first token is correct.");
|
|
is(tokens[1]+'', "b", "third token is correct.");
|
|
is(tokens[2].tag, "_v", "third token is correct tag.");
|
|
is(tokens[2].n, "hmm", "third token is correct name.");
|
|
is(tokens[3]+'', "c", "fifth token is correct.");
|
|
is(tokens[4]+'', "d", "seventh token is correct.");
|
|
is(tokens[5].tag, "_v", "eighth token is correct tag.");
|
|
is(tokens[5].n, "hmm", "eighth token is correct name.");
|
|
}
|
|
|
|
function testSingleCharDelimiter() {
|
|
var text = '({{foo}} {{=[ ]=}}[text])';
|
|
var tokens = Hogan.scan(text);
|
|
|
|
var t = Hogan.compile(text);
|
|
s = t.render({foo: "bar", text: 'It worked!'});
|
|
is(s, '(bar It worked!)', "Hogan substitution worked after custom delimiters.");
|
|
}
|
|
|
|
function testSetDelimiterWithWhitespace() {
|
|
var text = "{{= | | =}}|foo|";
|
|
var t = Hogan.compile(text);
|
|
s = t.render({foo: "bar"});
|
|
is(s, 'bar', "custom delimiters with whitespace works.")
|
|
}
|
|
|
|
function testParseBasic() {
|
|
var text = "test";
|
|
var tree = Hogan.parse(Hogan.scan(text));
|
|
is(tree.length, 1, "one parse node");
|
|
is(tree[0]+'', "test", "text is correct");
|
|
}
|
|
|
|
function testParseVariables() {
|
|
var text = "test{{foo}}test!{{bar}}test!!{{baz}}test!!!";
|
|
var tree = Hogan.parse(Hogan.scan(text));
|
|
is(tree.length, 7, "one parse node");
|
|
is(tree[0]+'', "test", "first text is correct");
|
|
is(tree[2]+'', "test!", "second text is correct")
|
|
is(tree[4]+'', "test!!", "third text is correct")
|
|
is(tree[6]+'', "test!!!", "last text is correct")
|
|
is(tree[1].n, "foo", "first var is correct");
|
|
is(tree[3].n, "bar", "second var is correct");
|
|
is(tree[5].n, "baz", "third var is correct");
|
|
}
|
|
|
|
function testParseSection() {
|
|
var text = "a{{#foo}}b{{/foo}}c";
|
|
var tree = Hogan.parse(Hogan.scan(text));
|
|
is(tree.length, 3, "three nodes at base");
|
|
is(tree[0]+'', "a", "correct text in first node");
|
|
is(tree[1].hasOwnProperty('nodes'), true, "second node is a section");
|
|
is(tree[1].tag, '#', "second node is a section");
|
|
is(tree[1].n, "foo", "correct name for section");
|
|
is(tree[1].nodes[0]+'', "b", "correct text in section");
|
|
is(tree[2]+'', "c", "correct text in last node");
|
|
}
|
|
|
|
function testParseIndexes() {
|
|
var text = "abc{{#foo}}asdf{{bar}}asdf{{/foo}}def";
|
|
var tree = Hogan.parse(Hogan.scan(text));
|
|
is(text.substring(tree[1].i, tree[1].end), "asdf{{bar}}asdf", "section text indexes are correct");
|
|
}
|
|
|
|
function testParseNegativeSection() {
|
|
var text = "a{{^foo}}b{{/foo}}c";
|
|
var tree = Hogan.parse(Hogan.scan(text));
|
|
|
|
is(tree.length, 3, "three nodes at base");
|
|
is(tree[0]+'', "a", "correct text in first node");
|
|
is(tree[1].hasOwnProperty('nodes'), true, "second node is a section");
|
|
is(tree[1].tag, '^', "second node is a negative section");
|
|
is(tree[1].n, "foo", "correct name for section");
|
|
is(tree[1].nodes[0]+'', "b", "correct text in section");
|
|
is(tree[2]+'', "c", "correct text in last node");
|
|
}
|
|
|
|
function testParseNestedSections() {
|
|
var text = "{{#bar}}{{#foo}}c{{/foo}}{{/bar}}"
|
|
var tree = Hogan.parse(Hogan.scan(text));
|
|
|
|
is(tree.length, 1, "one node at base");
|
|
is(tree[0].tag, "#", "open section is first node");
|
|
is(tree[0].n, "bar", "first section name is 'bar'");
|
|
is(tree[0].nodes.length, 1, "first section contains one node.");
|
|
is(tree[0].nodes[0].n, "foo", "correct name for nested section");
|
|
is(tree[0].nodes[0].nodes[0]+'', "c", "correct text in nested section");
|
|
}
|
|
|
|
function testMissingClosingTag() {
|
|
var text = "a{{#foo}}bc";
|
|
var msg = '';
|
|
try {
|
|
var tree = Hogan.parse(Hogan.scan(text));
|
|
} catch (e) {
|
|
msg = e.message;
|
|
}
|
|
is(msg, "missing closing tag: foo", "Error is generated");
|
|
}
|
|
|
|
function testBadNesting() {
|
|
var text = "a{{#foo}}{{#bar}}b{{/foo}}{{/bar}}c";
|
|
var msg = '';
|
|
try {
|
|
var tree = Hogan.parse(Hogan.scan(text));
|
|
} catch (e) {
|
|
msg = e.message;
|
|
}
|
|
is(msg, "Nesting error: bar vs. foo", "Error is generated");
|
|
}
|
|
|
|
function testBasicOutput() {
|
|
var text = "test";
|
|
var t = Hogan.compile(text);
|
|
is(t.render(), text, "template renders one text node");
|
|
}
|
|
|
|
function testBasicOutputAsString() {
|
|
var text = "test";
|
|
var textFunc = Hogan.compile(text, true);
|
|
is(textFunc, "function(context, partials){this.buffer.push('test');};", "template renders correct text function.");
|
|
}
|
|
|
|
function testOneVariable() {
|
|
var text = "test {{foo}} test";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo:'bar'});
|
|
is(s, "test bar test", "basic variable substitution works.");
|
|
}
|
|
|
|
function testOneVariableAsString() {
|
|
var text = "test {{foo}} test";
|
|
var funcText = Hogan.compile(text, true);
|
|
is(funcText, "function(context, partials){this.buffer.push('test ');\nthis.buffer.push(this.find('foo', context));\nthis.buffer.push(' test');};",
|
|
"Function text is correct with variable substitution.");
|
|
}
|
|
|
|
function testRenderWithWhitespace() {
|
|
var text = "{{ string }}";
|
|
var t = Hogan.compile(text);
|
|
is(t.render({string: "---" }), "---", "tags with whitespace render correctly.");
|
|
}
|
|
|
|
function testRenderWithWhitespaceAroundTripleStache() {
|
|
var text = " {{{string}}}\n";
|
|
var t = Hogan.compile(text);
|
|
is(t.render({string: "---" }), " ---\n", "triple stache surrounded by whitespace render correctly.");
|
|
}
|
|
|
|
function testRenderWithWhitespaceAroundAmpersand() {
|
|
var text = " {{& string }}\n";
|
|
var t = Hogan.compile(text);
|
|
is(t.render({string: "---" }), " ---\n", "ampersand surrounded by whitespace render correctly.");
|
|
}
|
|
|
|
function testMultipleVariables() {
|
|
var text = "test {{foo}} test {{bar}} test {{baz}} test {{foo}} test";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo:'42', bar: '43', baz: '44'});
|
|
is(s, "test 42 test 43 test 44 test 42 test", "all variables render correctly.");
|
|
}
|
|
|
|
function testNumberValues() {
|
|
var text = "integer: {{foo}} float: {{bar}} negative: {{baz}}";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo: 42, bar: 42.42, baz: -42});
|
|
is(s, "integer: 42 float: 42.42 negative: -42", "numbers render correctly");
|
|
}
|
|
|
|
function testObjectRender() {
|
|
var text = "object: {{foo}}";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo: {}});
|
|
is(s, "object: [object Object]", "objects render default toString.");
|
|
}
|
|
|
|
function testObjectToStringRender() {
|
|
var text = "object: {{foo}}";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo: {toString: function(){ return "yo!"}}});
|
|
is(s, "object: yo!", "objects render supplied toString.");
|
|
}
|
|
|
|
function testArrayRender() {
|
|
var text = "array: {{foo}}";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo: ["a","b","c"]});
|
|
is(s, "array: a,b,c", "arrays render default toString.");
|
|
}
|
|
|
|
function testEscaping() {
|
|
var text = "{{foo}}";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render();
|
|
var s = t.render({foo: "< > <div> \' \" &"});
|
|
is(s, "< > <div> ' " &", "input correctly escaped.");
|
|
|
|
var ec ={ "'": "'", '"': """, "<": "<", ">": ">", "&": "&"}
|
|
for (var char in ec) {
|
|
var s = t.render({foo: char + " just me"});
|
|
is(s, ec[char] + " just me", "input correctly escaped.");
|
|
}
|
|
|
|
}
|
|
|
|
function testMustacheInjection() {
|
|
var text = "{{foo}}";
|
|
var t = Hogan.compile(text);
|
|
s = t.render({foo:"{{{<42}}}"})
|
|
is(s, "{{{<42}}}", "Can't inject mustache");
|
|
}
|
|
|
|
function testTripleStache() {
|
|
var text = "{{{foo}}}";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo: "< > <div> \' \" &"});
|
|
is(s, "< > <div> \' \" &", "input correctly not-escaped.");
|
|
}
|
|
|
|
function testAmpNoEscaping() {
|
|
var text = "{{&foo}}";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo: "< > <div> \' \" &"});
|
|
is(s, "< > <div> \' \" &", "input correctly not-escaped.");
|
|
}
|
|
|
|
function testPartial() {
|
|
var partialText = "this is text from the partial--the magic number {{foo}} is from a variable";
|
|
var p = Hogan.compile(partialText);
|
|
|
|
var text = "This template contains a partial ({{>testPartial}})."
|
|
var t = Hogan.compile(text);
|
|
|
|
var s = t.render({foo: 42}, {testPartial: p});
|
|
is(s, "This template contains a partial (this is text from the partial--the magic number 42 is from a variable).", "partials work");
|
|
}
|
|
|
|
function testNestedPartials() {
|
|
var partialText = "this is text from the partial--the magic number {{foo}} is from a variable";
|
|
var p = Hogan.compile(partialText);
|
|
|
|
var partialText2 = "This template contains a partial ({{>testPartial}})."
|
|
var p2 = Hogan.compile(partialText2);
|
|
|
|
var text = "This template contains a partial that contains a partial [{{>testPartial2}}]."
|
|
var t = Hogan.compile(text);
|
|
|
|
var s = t.render({foo: 42}, {testPartial: p, testPartial2: p2});
|
|
is(s, "This template contains a partial that contains a partial [This template contains a partial (this is text from the partial--the magic number 42 is from a variable).].", "nested partials work");
|
|
}
|
|
|
|
function testNegativeSection() {
|
|
var text = "This template {{^foo}}BOO {{/foo}}contains an inverted section."
|
|
var t = Hogan.compile(text);
|
|
var s = t.render();
|
|
is(s, "This template BOO contains an inverted section.", "inverted sections with no context work");
|
|
|
|
s = t.render({foo:[]});
|
|
is(s, "This template BOO contains an inverted section.", "inverted sections with empty list context work");
|
|
|
|
s = t.render({ foo:false });
|
|
is(s, "This template BOO contains an inverted section.", "inverted sections with false context work");
|
|
|
|
s = t.render({foo:''});
|
|
is(s, "This template contains an inverted section.", "inverted sections with empty string context work");
|
|
|
|
s = t.render({foo:true});
|
|
is(s, "This template contains an inverted section.", "inverted sections with true context work");
|
|
|
|
s = t.render({foo: function() { return false; }});
|
|
is(s, "This template BOO contains an inverted section.", "inverted sections with false returning method in context work");
|
|
}
|
|
|
|
function testSectionElision() {
|
|
var text = "This template {{#foo}}BOO {{/foo}}contains a section."
|
|
var t = Hogan.compile(text);
|
|
var s = t.render();
|
|
is(s, "This template contains a section.", "sections with no context work");
|
|
|
|
s = t.render({foo:[]});
|
|
is(s, "This template contains a section.", "sections with empty list context work");
|
|
|
|
s = t.render({foo:false});
|
|
is(s, "This template contains a section.", "sections with false context work");
|
|
}
|
|
|
|
function testSectionObjectContext() {
|
|
var text = "This template {{#foo}}{{bar}} {{/foo}}contains a section."
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo:{bar:42}});
|
|
is(s, "This template 42 contains a section.", "sections with object context work");
|
|
}
|
|
|
|
function testSectionArrayContext() {
|
|
var text = "This template {{#foo}}{{bar}} {{/foo}}contains a section."
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo:[{bar:42}, {bar:43}, {bar:44}]});
|
|
is(s, "This template 42 43 44 contains a section.", "sections with object ctx and array values work");
|
|
}
|
|
|
|
function testFalsyVariableNoRender() {
|
|
var text = "I ({{cannot}}) be seen!";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render();
|
|
is(s, "I () be seen!", "missing value doesn't render.");
|
|
}
|
|
|
|
function testSectionExtensions() {
|
|
var text = "Test {{_//|__foo}}bar{{/foo}}";
|
|
var options = {sectionTags:[{o:'_//|__foo', c:'foo'}]};
|
|
var tree = Hogan.parse(Hogan.scan(text), options);
|
|
is(tree[1].tag, "#", "_//|__foo node transformed to section");
|
|
is(tree[1].n, "_//|__foo", "_//|__foo node transformed to section");
|
|
|
|
var t = Hogan.compile(text, options );
|
|
var s = t.render({'_//|__foo':true});
|
|
is(s, "Test bar", "Custom sections work");
|
|
}
|
|
|
|
function testMisnestedSectionExtensions() {
|
|
var text = "Test {{__foo}}bar{{/bar}}";
|
|
var options = {sectionTags:[{o:'__foo', c:'foo'}, {o:'__bar', c:'bar'}]};
|
|
var msg = '';
|
|
try {
|
|
var tree = Hogan.parse(Hogan.scan(text), options);
|
|
} catch (e) {
|
|
msg = e.message;
|
|
}
|
|
is(msg, "Nesting error: __foo vs. bar", "Error is generated");
|
|
}
|
|
|
|
function testNestedSection() {
|
|
var text = "{{#foo}}{{#bar}}{{baz}}{{/bar}}{{/foo}}";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo: 42, bar: 42, baz:42});
|
|
is(s, "42", "can reach up context stack");
|
|
}
|
|
|
|
function testDottedNames() {
|
|
var text = '"{{person.name}}" == "{{#person}}{{name}}{{/person}}"';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({person:{name:'Joe'}});
|
|
is(s, '"Joe" == "Joe"', "dotted names work");
|
|
}
|
|
|
|
function testImplicitIterator() {
|
|
var text = '{{#stuff}} {{.}} {{/stuff}}';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({stuff:[42,43,44]});
|
|
is(s, " 42 43 44 ", "implicit iterators work");
|
|
}
|
|
|
|
function testPartialsAndDelimiters() {
|
|
var text = '{{>include}}*\n{{= | | =}}\n*|>include|';
|
|
var partialText = ' .{{value}}. ';
|
|
var partial = Hogan.compile(partialText);
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({value:"yes"}, {'include':partial});
|
|
is(s, " .yes. *\n* .yes. ", "partials work around delimiters");
|
|
}
|
|
|
|
function testStringPartials() {
|
|
var text = "foo{{>mypartial}}baz";
|
|
var partialText = " bar ";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({}, {'mypartial': partialText});
|
|
is(s, "foo bar baz", "string partial works.");
|
|
}
|
|
|
|
function testMissingPartials() {
|
|
var text = "foo{{>mypartial}} bar";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({});
|
|
is(s, "foo bar", "missing partial works.");
|
|
}
|
|
|
|
function testIndentedStandaloneComment() {
|
|
var text = 'Begin.\n {{! Indented Comment Block! }}\nEnd.';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render();
|
|
is(s, 'Begin.\nEnd.', "Standalone comment blocks are removed.");
|
|
}
|
|
|
|
function testNewLineBetweenDelimiterChanges() {
|
|
var data = { section: true, data: 'I got interpolated.' };
|
|
var text = '\n{{#section}}\n {{data}}\n |data|\n{{/section}}x\n\n{{= | | =}}\n|#section|\n {{data}}\n |data|\n|/section|';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render(data);
|
|
is(s, '\n I got interpolated.\n |data|\nx\n\n {{data}}\n I got interpolated.\n', 'render correct')
|
|
}
|
|
|
|
function testMustacheJSApostrophe() {
|
|
var text = '{{apos}}{{control}}';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({'apos':"'", 'control':"X"});
|
|
is(s, ''X', 'Apostrophe is escaped.');
|
|
}
|
|
|
|
function testMustacheJSArrayOfImplicitPartials() {
|
|
var text = 'Here is some stuff!\n{{#numbers}}\n{{>partial}}\n{{/numbers}}\n';
|
|
var partialText = '{{.}}\n';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({numbers:[1,2,3,4]}, {partial: partialText});
|
|
is(s, 'Here is some stuff!\n1\n2\n3\n4\n', 'Partials with implicit iterators work.');
|
|
}
|
|
|
|
function testMustacheJSArrayOfPartials() {
|
|
var text = 'Here is some stuff!\n{{#numbers}}\n{{>partial}}\n{{/numbers}}\n';
|
|
var partialText = '{{i}}\n';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({numbers:[{i:1},{i:2},{i:3},{i:4}]}, {partial: partialText});
|
|
is(s, 'Here is some stuff!\n1\n2\n3\n4\n', 'Partials with arrays work.');
|
|
}
|
|
|
|
function testMustacheJSArrayOfStrings() {
|
|
var text = '{{#strings}}{{.}} {{/strings}}';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({strings:['foo', 'bar', 'baz']});
|
|
is(s, 'foo bar baz ', 'array of strings works with implicit iterators.');
|
|
}
|
|
|
|
function testMustacheJSUndefinedString() {
|
|
var text = 'foo{{bar}}baz';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({bar:undefined});
|
|
is(s, 'foobaz', 'undefined value does not render.');
|
|
}
|
|
|
|
function testMustacheJSTripleStacheAltDelimiter() {
|
|
var text = '{{=<% %>=}}<% foo %> {{foo}} <%{bar}%> {{{bar}}}';
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({foo:'yeah', bar:'hmm'});
|
|
is(s, 'yeah {{foo}} hmm {{{bar}}}', 'triple stache inside alternate delimiter works.');
|
|
}
|
|
|
|
/* shootout benchmark tests */
|
|
|
|
function testShootOutString() {
|
|
var text = "Hello World!";
|
|
var expected = "Hello World!"
|
|
var t = Hogan.compile(text)
|
|
var s = t.render({})
|
|
is(s, expected, "Shootout String compiled correctly");
|
|
}
|
|
|
|
function testShootOutReplace() {
|
|
var text = "Hello {{name}}! You have {{count}} new messages.";
|
|
var expected = "Hello Mick! You have 30 new messages.";
|
|
var t = Hogan.compile(text)
|
|
var s = t.render({ name: "Mick", count: 30 })
|
|
is(s, expected, "Shootout Replace compiled correctly");
|
|
}
|
|
|
|
function testShootOutArray() {
|
|
var text = "{{#names}}{{name}}{{/names}}";
|
|
var expected = "MoeLarryCurlyShemp";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({ names: [{name: "Moe"}, {name: "Larry"}, {name: "Curly"}, {name: "Shemp"}] })
|
|
is(s, expected, "Shootout Array compiled correctly");
|
|
}
|
|
|
|
function testShootOutObject() {
|
|
var text = "{{#person}}{{name}}{{age}}{{/person}}";
|
|
var expected = "Larry45";
|
|
var t = Hogan.compile(text)
|
|
var s = t.render({ person: { name: "Larry", age: 45 } })
|
|
is(s, expected, "Shootout Object compiled correctly");
|
|
}
|
|
|
|
function testShootOutPartial() {
|
|
var text = "{{#peeps}}{{>replace}}{{/peeps}}";
|
|
var t = Hogan.compile(text);
|
|
var partial = Hogan.compile(" Hello {{name}}! You have {{count}} new messages.");
|
|
var s = t.render({ peeps: [{name: "Moe", count: 15}, {name: "Larry", count: 5}, {name: "Curly", count: 2}] }, { replace: partial });
|
|
var expected = " Hello Moe! You have 15 new messages. Hello Larry! You have 5 new messages. Hello Curly! You have 2 new messages.";
|
|
is(s, expected, "Shootout Partial compiled correctly");
|
|
}
|
|
|
|
function testShootOutRecurse() {
|
|
var text = "{{name}}{{#kids}}{{>recursion}}{{/kids}}";
|
|
var t = Hogan.compile(text);
|
|
var partial = Hogan.compile("{{name}}{{#kids}}{{>recursion}}{{/kids}}");
|
|
var s = t.render({
|
|
name: '1',
|
|
kids: [
|
|
{
|
|
name: '1.1',
|
|
kids: [
|
|
{ name: '1.1.1', kids: [] }
|
|
]
|
|
}
|
|
]
|
|
}, { recursion: partial });
|
|
var expected = "11.11.1.1";
|
|
is(s, expected, "Shootout Recurse compiled correctly");
|
|
}
|
|
|
|
function testShootOutFilter() {
|
|
var text = "{{#filter}}foo {{bar}}{{/filter}}";
|
|
var t = Hogan.compile(text);
|
|
var s = t.render({
|
|
filter: function() {
|
|
return function(text, render) {
|
|
return render(text).toUpperCase();
|
|
}
|
|
},
|
|
bar: "bar"
|
|
});
|
|
var expected = "FOO BAR"
|
|
is(s, expected, "Shootout Filter compiled correctly");
|
|
}
|
|
|
|
function testShootOutComplex() {
|
|
var text =
|
|
"<h1>{{header}}</h1>" +
|
|
"{{#hasItems}}" +
|
|
"<ul>" +
|
|
"{{#items}}" +
|
|
"{{#current}}" +
|
|
"<li><strong>{{name}}</strong></li>" +
|
|
"{{/current}}" +
|
|
"{{^current}}" +
|
|
"<li><a href=\"{{url}}\">{{name}}</a></li>" +
|
|
"{{/current}}" +
|
|
"{{/items}}" +
|
|
"</ul>" +
|
|
"{{/hasItems}}" +
|
|
"{{^hasItems}}" +
|
|
"<p>The list is empty.</p>" +
|
|
"{{/hasItems}}";
|
|
|
|
var expected = "<h1>Colors</h1><ul><li><strong>red</strong></li><li><a href=\"#Green\">green</a></li><li><a href=\"#Blue\">blue</a></li></ul>";
|
|
var t = Hogan.compile(text)
|
|
var s = t.render({
|
|
header: function() {
|
|
return "Colors";
|
|
},
|
|
items: [
|
|
{name: "red", current: true, url: "#Red"},
|
|
{name: "green", current: false, url: "#Green"},
|
|
{name: "blue", current: false, url: "#Blue"}
|
|
],
|
|
hasItems: function() {
|
|
return this.items.length !== 0;
|
|
},
|
|
empty: function() {
|
|
return this.items.length === 0;
|
|
}
|
|
})
|
|
|
|
is(s, expected, "Shootout Complex compiled correctly");
|
|
}
|
|
|
|
function testRenderOutput() {
|
|
if (doc) return
|
|
var fs = require('fs');
|
|
var inPath = 'test/templates';
|
|
var outPath = 'test/html';
|
|
|
|
fs.readdirSync(inPath).forEach(function (file) {
|
|
var i = fs.readFileSync([inPath, file].join('/'), 'utf-8');
|
|
var t = Hogan.compile(i);
|
|
var r = t.render({});
|
|
var o = fs.readFileSync([outPath, file].join('/').replace(/mustache$/, 'html')).toString();
|
|
is(r === o, true, file + ' should correctly render html')
|
|
})
|
|
}
|
|
|
|
function testDefaultRenderImpl() {
|
|
var ht = new Hogan.Template();
|
|
is(ht.render() === '', true, 'default renderImpl returns an array.');
|
|
}
|
|
|
|
|
|
function appendText(el, text) {
|
|
var textNode = document.createTextNode(text);
|
|
el.appendChild(textNode);
|
|
el.appendChild(document.createElement('br'));
|
|
}
|
|
|
|
if (!this["output"]) {
|
|
var output = function (s) {
|
|
return doc ? appendText(doc.getElementById('console'), s) : console.log(s);
|
|
};
|
|
}
|
|
var passed = 0;
|
|
var failed = 0;
|
|
|
|
function is(got, expected, msg) {
|
|
if (got === expected) {
|
|
output("OK: " + msg);
|
|
++passed;
|
|
} else {
|
|
output("FAIL: " + msg);
|
|
output("Expected |" + expected + "|");
|
|
output(" Got |" + got + "|");
|
|
++failed;
|
|
}
|
|
}
|
|
|
|
function complete() {
|
|
output("\nTests Complete");
|
|
output("--------------");
|
|
output("Passed: " + passed);
|
|
output("Failed: " + failed);
|
|
output("\n");
|
|
}
|
|
|
|
function runTests() {
|
|
output("Tests Starting");
|
|
output("--------------");
|
|
testScanTextNoTags();
|
|
testScanOneTag();
|
|
testScanMultipleTags();
|
|
testScanSectionOpen();
|
|
testScanSectionClose();
|
|
testScanSection();
|
|
testScanSectionInContent();
|
|
testScanNegativeSection();
|
|
testScanPartial();
|
|
testScanBackwardPartial();
|
|
testScanAmpersandNoEscapeTag();
|
|
testScanTripleStache();
|
|
testScanSectionWithTripleStacheInside();
|
|
testScanSetDelimiter();
|
|
testScanResetDelimiter();
|
|
testSetDelimiterWithWhitespace();
|
|
testSingleCharDelimiter();
|
|
testParseBasic();
|
|
testParseVariables();
|
|
testParseSection();
|
|
testParseIndexes();
|
|
testParseNegativeSection();
|
|
testParseNestedSections();
|
|
testMissingClosingTag();
|
|
testBadNesting();
|
|
testBasicOutput();
|
|
//testBasicOutputAsString();
|
|
testOneVariable();
|
|
//testOneVariableAsString();
|
|
testMultipleVariables();
|
|
testNumberValues();
|
|
testObjectRender();
|
|
testObjectToStringRender();
|
|
testArrayRender();
|
|
testEscaping();
|
|
testMustacheInjection();
|
|
testTripleStache();
|
|
testAmpNoEscaping();
|
|
testPartial();
|
|
testNestedPartials();
|
|
testNegativeSection();
|
|
testSectionElision();
|
|
testSectionObjectContext();
|
|
testSectionArrayContext();
|
|
testRenderWithWhitespace();
|
|
testRenderWithWhitespaceAroundTripleStache();
|
|
testRenderWithWhitespaceAroundAmpersand();
|
|
testFalsyVariableNoRender();
|
|
testRenderOutput();
|
|
testDefaultRenderImpl();
|
|
testSectionExtensions();
|
|
testMisnestedSectionExtensions();
|
|
testNestedSection();
|
|
testShootOutString();
|
|
testShootOutReplace();
|
|
testShootOutArray();
|
|
testShootOutObject();
|
|
testShootOutPartial();
|
|
testShootOutRecurse();
|
|
testShootOutFilter();
|
|
testShootOutComplex();
|
|
testDottedNames();
|
|
testImplicitIterator();
|
|
testPartialsAndDelimiters();
|
|
testStringPartials();
|
|
testMissingPartials();
|
|
testIndentedStandaloneComment();
|
|
testNewLineBetweenDelimiterChanges();
|
|
testMustacheJSApostrophe();
|
|
testMustacheJSArrayOfImplicitPartials();
|
|
testMustacheJSArrayOfPartials();
|
|
testMustacheJSArrayOfStrings();
|
|
testMustacheJSUndefinedString();
|
|
testMustacheJSTripleStacheAltDelimiter();
|
|
complete();
|
|
}
|
|
|
|
if (doc) {
|
|
window.onload = runTests;
|
|
} else {
|
|
runTests();
|
|
} |