diff options
Diffstat (limited to 'spec/qunit_spec.js')
-rw-r--r-- | spec/qunit_spec.js | 262 |
1 files changed, 201 insertions, 61 deletions
diff --git a/spec/qunit_spec.js b/spec/qunit_spec.js index e144ba2..56cb687 100644 --- a/spec/qunit_spec.js +++ b/spec/qunit_spec.js @@ -6,19 +6,25 @@ Handlebars.registerHelper('helperMissing', function(helper, context) { } }); -var shouldCompileTo = function(string, hash, expected, message) { - var template = Handlebars.compile(string); - if(Object.prototype.toString.call(hash) === "[object Array]") { - if(hash[1]) { +var shouldCompileTo = function(string, hashOrArray, expected, message) { + var template = Handlebars.compile(string), ary; + if(Object.prototype.toString.call(hashOrArray) === "[object Array]") { + helpers = hashOrArray[1]; + + if(helpers) { for(var prop in Handlebars.helpers) { - hash[1][prop] = Handlebars.helpers[prop]; + helpers[prop] = Handlebars.helpers[prop]; } } + + ary = []; + ary.push(hashOrArray[0]); + ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] }); } else { - hash = [hash]; + ary = [hashOrArray]; } - result = template.apply(this, hash) + result = template.apply(this, ary); equal(result, expected, "'" + expected + "' should === '" + result + "': " + message); }; @@ -72,7 +78,8 @@ test("newlines", function() { test("escaping text", function() { shouldCompileTo("Awesome's", {}, "Awesome's", "text is escaped so that it doesn't get caught on single quotes"); shouldCompileTo("Awesome\\", {}, "Awesome\\", "text is escaped so that the closing quote can't be ignored"); - shouldCompileTo("Awesome\\ foo", {}, "Awesome\\ foo", "text is escaped so that it doesn't mess up backslashes"); + shouldCompileTo("Awesome\\\\ foo", {}, "Awesome\\\\ foo", "text is escaped so that it doesn't mess up backslashes"); + shouldCompileTo("Awesome {{foo}}", {foo: '\\'}, "Awesome \\", "text is escaped so that it doesn't mess up backslashes"); shouldCompileTo(' " " ', {}, ' " " ', "double quotes never produce invalid javascript"); }); @@ -80,12 +87,12 @@ test("escaping expressions", function() { shouldCompileTo("{{{awesome}}}", {awesome: "&\"\\<>"}, '&\"\\<>', "expressions with 3 handlebars aren't escaped"); - shouldCompileTo("{{awesome}}", {awesome: "&\"\\<>"}, '&\"\\<>', - "by default expressions should be escaped"); - shouldCompileTo("{{&awesome}}", {awesome: "&\"\\<>"}, '&\"\\<>', "expressions with {{& handlebars aren't escaped"); + shouldCompileTo("{{awesome}}", {awesome: "&\"'`\\<>"}, '&"'`\\<>', + "by default expressions should be escaped"); + }); test("functions returning safestrings shouldn't be escaped", function() { @@ -106,6 +113,10 @@ test("functions with context argument", function() { "Frank", "functions are called with context arguments"); }); +test("paths with hyphens", function() { + shouldCompileTo("{{foo-bar}}", {"foo-bar": "baz"}, "baz", "Paths can contain hyphens (-)"); +}); + test("nested paths", function() { shouldCompileTo("Goodbye {{alan/expression}} world!", {alan: {expression: "beautiful"}}, "Goodbye beautiful world!", "Nested paths access nested objects"); @@ -128,7 +139,7 @@ test("--- TODO --- bad idea nested paths", function() { shouldCompileTo(string, hash, "world world world ", "Same context (.) is ignored in paths"); }); -test("that current context path ({{.}}) doesn't hit fallback", function() { +test("that current context path ({{.}}) doesn't hit helpers", function() { shouldCompileTo("test: {{.}}", [null, {helper: "awesome"}], "test: "); }); @@ -220,16 +231,16 @@ test("block with complex lookup", function() { test("helper with complex lookup", function() { var string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}" var hash = {prefix: "/root", goodbyes: [{text: "Goodbye", url: "goodbye"}]}; - var fallback = {link: function(prefix) { + var helpers = {link: function(prefix) { return "<a href='" + prefix + "/" + this.url + "'>" + this.text + "</a>" }}; - shouldCompileTo(string, [hash, fallback], "<a href='/root/goodbye'>Goodbye</a>") + shouldCompileTo(string, [hash, helpers], "<a href='/root/goodbye'>Goodbye</a>") }); test("helper block with complex lookup expression", function() { var string = "{{#goodbyes}}{{../name}}{{/goodbyes}}" var hash = {name: "Alan"}; - var fallback = {goodbyes: function(fn) { + var helpers = {goodbyes: function(fn) { var out = ""; var byes = ["Goodbye", "goodbye", "GOODBYE"]; for (var i = 0,j = byes.length; i < j; i++) { @@ -237,16 +248,16 @@ test("helper block with complex lookup expression", function() { } return out; }}; - shouldCompileTo(string, [hash, fallback], "Goodbye Alan! goodbye Alan! GOODBYE Alan! "); + shouldCompileTo(string, [hash, helpers], "Goodbye Alan! goodbye Alan! GOODBYE Alan! "); }); test("helper with complex lookup and nested template", function() { var string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}"; var hash = {prefix: '/root', goodbyes: [{text: "Goodbye", url: "goodbye"}]}; - var fallback = {link: function (prefix, fn) { + var helpers = {link: function (prefix, fn) { return "<a href='" + prefix + "/" + this.url + "'>" + fn(this) + "</a>"; }}; - shouldCompileTo(string, [hash, fallback], "<a href='/root/goodbye'>Goodbye</a>") + shouldCompileTo(string, [hash, helpers], "<a href='/root/goodbye'>Goodbye</a>") }); test("block with deep nested complex lookup", function() { @@ -359,24 +370,24 @@ test("block helper inverted sections", function() { // so we should see the output of both shouldCompileTo(string, hash, "<ul><li>Alan</li><li>Yehuda</li></ul>", "an inverse wrapper is passed in as a new context"); shouldCompileTo(string, empty, "<p><em>Nobody's here</em></p>", "an inverse wrapper can be optionally called"); - shouldCompileTo(messageString, rootMessage, "<p>Nobody's here</p>", "the context of an inverse is the parent of the block"); + shouldCompileTo(messageString, rootMessage, "<p>Nobody's here</p>", "the context of an inverse is the parent of the block"); }); -module("fallback hash"); +module("helpers hash"); -test("providing a fallback hash", function() { +test("providing a helpers hash", function() { shouldCompileTo("Goodbye {{cruel}} {{world}}!", [{cruel: "cruel"}, {world: "world"}], "Goodbye cruel world!", - "Fallback hash is available"); + "helpers hash is available"); shouldCompileTo("Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!", [{iter: [{cruel: "cruel"}]}, {world: "world"}], - "Goodbye cruel world!", "Fallback hash is available inside other blocks"); + "Goodbye cruel world!", "helpers hash is available inside other blocks"); }); test("in cases of conflict, the explicit hash wins", function() { }); -test("the fallback hash is available is nested contexts", function() { +test("the helpers hash is available is nested contexts", function() { }); @@ -423,10 +434,15 @@ test("GH-14: a partial preceding a selector", function() { module("String literal parameters"); test("simple literals work", function() { - var string = 'Message: {{hello "world"}}'; + var string = 'Message: {{hello "world" 12 true false}}'; var hash = {}; - var fallback = {hello: function(param) { return "Hello " + param; }} - shouldCompileTo(string, [hash, fallback], "Message: Hello world", "template with a simple String literal"); + var helpers = {hello: function(param, times, bool1, bool2) { + if(typeof times !== 'number') { times = "NaN"; } + if(typeof bool1 !== 'boolean') { bool1 = "NaB"; } + if(typeof bool2 !== 'boolean') { bool2 = "NaB"; } + return "Hello " + param + " " + times + " times: " + bool1 + " " + bool2; + }} + shouldCompileTo(string, [hash, helpers], "Message: Hello world 12 times: true false", "template with a simple String literal"); }); test("using a quote in the middle of a parameter raises an error", function() { @@ -437,17 +453,17 @@ test("using a quote in the middle of a parameter raises an error", function() { }); test("escaping a String is possible", function(){ - var string = 'Message: {{hello "\\"world\\""}}'; + var string = 'Message: {{{hello "\\"world\\""}}}'; var hash = {} - var fallback = {hello: function(param) { return "Hello " + param; }} - shouldCompileTo(string, [hash, fallback], "Message: Hello \"world\"", "template with an escaped String literal"); + var helpers = {hello: function(param) { return "Hello " + param; }} + shouldCompileTo(string, [hash, helpers], "Message: Hello \"world\"", "template with an escaped String literal"); }); test("it works with ' marks", function() { - var string = 'Message: {{hello "Alan\'s world"}}'; + var string = 'Message: {{{hello "Alan\'s world"}}}'; var hash = {} - var fallback = {hello: function(param) { return "Hello " + param; }} - shouldCompileTo(string, [hash, fallback], "Message: Hello Alan's world", "template with a ' mark"); + var helpers = {hello: function(param) { return "Hello " + param; }} + shouldCompileTo(string, [hash, helpers], "Message: Hello Alan's world", "template with a ' mark"); }); module("multiple parameters"); @@ -455,17 +471,17 @@ module("multiple parameters"); test("simple multi-params work", function() { var string = 'Message: {{goodbye cruel world}}'; var hash = {cruel: "cruel", world: "world"} - var fallback = {goodbye: function(cruel, world) { return "Goodbye " + cruel + " " + world; }} - shouldCompileTo(string, [hash, fallback], "Message: Goodbye cruel world", "regular helpers with multiple params"); + var helpers = {goodbye: function(cruel, world) { return "Goodbye " + cruel + " " + world; }} + shouldCompileTo(string, [hash, helpers], "Message: Goodbye cruel world", "regular helpers with multiple params"); }); test("block multi-params work", function() { var string = 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}'; var hash = {cruel: "cruel", world: "world"} - var fallback = {goodbye: function(cruel, world, fn) { + var helpers = {goodbye: function(cruel, world, fn) { return fn({greeting: "Goodbye", adj: cruel, noun: world}); }} - shouldCompileTo(string, [hash, fallback], "Message: Goodbye cruel world", "block helpers with multiple params"); + shouldCompileTo(string, [hash, helpers], "Message: Goodbye cruel world", "block helpers with multiple params"); }) module("safestring"); @@ -523,7 +539,7 @@ test("overriding property lookup", function() { test("passing in data to a compiled function that expects data - works with helpers", function() { - var template = Handlebars.compile("{{hello}}", true); + var template = Handlebars.compile("{{hello}}", {data: true}); var helpers = { hello: function(options) { @@ -531,12 +547,12 @@ test("passing in data to a compiled function that expects data - works with help } }; - var result = template({noun: "cat"}, helpers, null, {adjective: "happy"}); + var result = template({noun: "cat"}, {helpers: helpers, data: {adjective: "happy"}}); equals("happy cat", result); }); test("passing in data to a compiled function that expects data - works with helpers and parameters", function() { - var template = Handlebars.compile("{{hello world}}", true); + var template = Handlebars.compile("{{hello world}}", {data: true}); var helpers = { hello: function(noun, options) { @@ -544,12 +560,12 @@ test("passing in data to a compiled function that expects data - works with help } }; - var result = template({exclaim: true, world: "world"}, helpers, null, {adjective: "happy"}); + var result = template({exclaim: true, world: "world"}, {helpers: helpers, data: {adjective: "happy"}}); equals("happy world!", result); }); test("passing in data to a compiled function that expects data - works with block helpers", function() { - var template = Handlebars.compile("{{#hello}}{{world}}{{/hello}}", true); + var template = Handlebars.compile("{{#hello}}{{world}}{{/hello}}", {data: true}); var helpers = { hello: function(fn) { @@ -560,12 +576,12 @@ test("passing in data to a compiled function that expects data - works with bloc } }; - var result = template({exclaim: true}, helpers, null, {adjective: "happy"}); + var result = template({exclaim: true}, {helpers: helpers, data: {adjective: "happy"}}); equals("happy world!", result); }); test("passing in data to a compiled function that expects data - works with block helpers that use ..", function() { - var template = Handlebars.compile("{{#hello}}{{world ../zomg}}{{/hello}}", true); + var template = Handlebars.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true}); var helpers = { hello: function(fn) { @@ -576,12 +592,12 @@ test("passing in data to a compiled function that expects data - works with bloc } }; - var result = template({exclaim: true, zomg: "world"}, helpers, null, {adjective: "happy"}); + var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy"}}); equals("happy world?", result); }); test("passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..", function() { - var template = Handlebars.compile("{{#hello}}{{world ../zomg}}{{/hello}}", true); + var template = Handlebars.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true}); var helpers = { hello: function(fn, inverse) { @@ -592,40 +608,40 @@ test("passing in data to a compiled function that expects data - data is passed } }; - var result = template({exclaim: true, zomg: "world"}, helpers, null, {adjective: "happy", accessData: "#win"}); + var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy", accessData: "#win"}}); equals("#win happy world?", result); }); test("you can override inherited data when invoking a helper", function() { - var template = Handlebars.compile("{{#hello}}{{world zomg}}{{/hello}}", true); + var template = Handlebars.compile("{{#hello}}{{world zomg}}{{/hello}}", {data: true}); var helpers = { hello: function(fn) { - return fn({exclaim: "?", zomg: "world"}, null, null, {adjective: "sad"}); + return fn({exclaim: "?", zomg: "world"}, { data: {adjective: "sad"} }); }, world: function(thing, options) { return options.data.adjective + " " + thing + (this.exclaim || ""); } }; - var result = template({exclaim: true, zomg: "planet"}, helpers, null, {adjective: "happy"}); + var result = template({exclaim: true, zomg: "planet"}, {helpers: helpers, data: {adjective: "happy"}}); equals("sad world?", result); }); test("you can override inherited data when invoking a helper with depth", function() { - var template = Handlebars.compile("{{#hello}}{{world ../zomg}}{{/hello}}", true); + var template = Handlebars.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true}); var helpers = { hello: function(fn) { - return fn({exclaim: "?"}, null, null, {adjective: "sad"}); + return fn({exclaim: "?"}, { data: {adjective: "sad"} }); }, world: function(thing, options) { return options.data.adjective + " " + thing + (this.exclaim || ""); } }; - var result = template({exclaim: true, zomg: "world"}, helpers, null, {adjective: "happy"}); + var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy"}}); equals("sad world?", result); }); @@ -647,7 +663,7 @@ test("helpers take precedence over same-named context properties", function() { world: "world" }; - var result = template(context, helpers); + var result = template(context, {helpers: helpers}); equals(result, "GOODBYE cruel WORLD"); }); @@ -669,34 +685,158 @@ test("helpers take precedence over same-named context properties", function() { world: "world" }; - var result = template(context, helpers); + var result = template(context, {helpers: helpers}); equals(result, "GOODBYE cruel WORLD"); }); test("helpers can take an optional hash", function() { - var template = Handlebars.compile('{{goodbye cruel="CRUEL" world="WORLD"}}'); + var template = Handlebars.compile('{{goodbye cruel="CRUEL" world="WORLD" times=12}}'); var helpers = { goodbye: function(options) { - return "GOODBYE " + options.hash.cruel + " " + options.hash.world; + return "GOODBYE " + options.hash.cruel + " " + options.hash.world + " " + options.hash.times + " TIMES"; } }; var context = {}; - var result = template(context, helpers); + var result = template(context, {helpers: helpers}); + equals(result, "GOODBYE CRUEL WORLD 12 TIMES"); +}); + +test("helpers can take an optional hash with booleans", function() { + var helpers = { + goodbye: function(options) { + if (options.hash.print === true) { + return "GOODBYE " + options.hash.cruel + " " + options.hash.world; + } else if (options.hash.print === false) { + return "NOT PRINTING"; + } else { + return "THIS SHOULD NOT HAPPEN"; + } + } + }; + + var context = {}; + + var template = Handlebars.compile('{{goodbye cruel="CRUEL" world="WORLD" print=true}}'); + var result = template(context, {helpers: helpers}); equals(result, "GOODBYE CRUEL WORLD"); + + var template = Handlebars.compile('{{goodbye cruel="CRUEL" world="WORLD" print=false}}'); + var result = template(context, {helpers: helpers}); + equals(result, "NOT PRINTING"); }); test("block helpers can take an optional hash", function() { - var template = Handlebars.compile('{{#goodbye cruel="CRUEL"}}world{{/goodbye}}'); + var template = Handlebars.compile('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}'); var helpers = { goodbye: function(options) { - return "GOODBYE " + options.hash.cruel + " " + options.fn(this); + return "GOODBYE " + options.hash.cruel + " " + options.fn(this) + " " + options.hash.times + " TIMES"; } }; - var result = template({}, helpers); + var result = template({}, {helpers: helpers}); + equals(result, "GOODBYE CRUEL world 12 TIMES"); +}); + +test("block helpers can take an optional hash with booleans", function() { + var helpers = { + goodbye: function(options) { + if (options.hash.print === true) { + return "GOODBYE " + options.hash.cruel + " " + options.fn(this); + } else if (options.hash.print === false) { + return "NOT PRINTING"; + } else { + return "THIS SHOULD NOT HAPPEN"; + } + } + }; + + var template = Handlebars.compile('{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}'); + var result = template({}, {helpers: helpers}); equals(result, "GOODBYE CRUEL world"); + + var template = Handlebars.compile('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}'); + var result = template({}, {helpers: helpers}); + equals(result, "NOT PRINTING"); +}); + + +test("arguments to helpers can be retrieved from options hash in string form", function() { + var template = Handlebars.compile('{{wycats is.a slave.driver}}', {stringParams: true}); + + var helpers = { + wycats: function(passiveVoice, noun, options) { + return "HELP ME MY BOSS " + passiveVoice + ' ' + noun; + } + }; + + var result = template({}, {helpers: helpers}); + + equals(result, "HELP ME MY BOSS is.a slave.driver"); +}); + +test("when using block form, arguments to helpers can be retrieved from options hash in string form", function() { + var template = Handlebars.compile('{{#wycats is.a slave.driver}}help :({{/wycats}}', {stringParams: true}); + + var helpers = { + wycats: function(passiveVoice, noun, options) { + return "HELP ME MY BOSS " + passiveVoice + ' ' + + noun + ': ' + options.fn(this); + } + }; + + var result = template({}, {helpers: helpers}); + + equals(result, "HELP ME MY BOSS is.a slave.driver: help :("); }); + +test("when inside a block in String mode, .. passes the appropriate context in the options hash", function() { + var template = Handlebars.compile('{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}', {stringParams: true}); + + var helpers = { + tomdale: function(desire, noun, options) { + return "STOP ME FROM READING HACKER NEWS I " + + options.contexts[0][desire] + " " + noun; + }, + + "with": function(context, options) { + return options.fn(options.contexts[0][context]); + } + }; + + var result = template({ + dale: {}, + + need: 'need-a' + }, {helpers: helpers}); + + equals(result, "STOP ME FROM READING HACKER NEWS I need-a dad.joke"); +}); + +test("when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper", function() { + var template = Handlebars.compile('{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}', {stringParams: true}); + + var helpers = { + tomdale: function(desire, noun, options) { + return "STOP ME FROM READING HACKER NEWS I " + + options.contexts[0][desire] + " " + noun + " " + + options.fn(this); + }, + + "with": function(context, options) { + return options.fn(options.contexts[0][context]); + } + }; + + var result = template({ + dale: {}, + + need: 'need-a' + }, {helpers: helpers}); + + equals(result, "STOP ME FROM READING HACKER NEWS I need-a dad.joke wot"); +}); + |