diff options
Diffstat (limited to 'spec')
-rw-r--r-- | spec/qunit_spec.js | 75 | ||||
-rw-r--r-- | spec/tokenizer_spec.rb | 33 |
2 files changed, 101 insertions, 7 deletions
diff --git a/spec/qunit_spec.js b/spec/qunit_spec.js index cbbc138..5c94253 100644 --- a/spec/qunit_spec.js +++ b/spec/qunit_spec.js @@ -33,7 +33,13 @@ Handlebars.registerHelper('helperMissing', function(helper, context) { function shouldCompileTo(string, hashOrArray, expected, message) { shouldCompileToWithPartials(string, hashOrArray, false, expected, message); } + function shouldCompileToWithPartials(string, hashOrArray, partials, expected, message) { + var result = compileWithPartials(string, hashOrArray, partials); + equal(result, expected, "'" + expected + "' should === '" + result + "': " + message); +} + +function compileWithPartials(string, hashOrArray, partials) { var template = CompilerContext[partials ? 'compileWithPartial' : 'compile'](string), ary; if(Object.prototype.toString.call(hashOrArray) === "[object Array]") { var helpers = hashOrArray[1]; @@ -51,18 +57,31 @@ function shouldCompileToWithPartials(string, hashOrArray, partials, expected, me ary = [hashOrArray]; } - var result = template.apply(this, ary); - equal(result, expected, "'" + expected + "' should === '" + result + "': " + message); + return template.apply(this, ary); } function shouldThrow(fn, exception, message) { - var caught = false; + var caught = false, + exType, exMessage; + + if (exception instanceof Array) { + exType = exception[0]; + exMessage = exception[1]; + } else if (typeof exception === 'string') { + exType = Error; + exMessage = exception; + } else { + exType = exception; + } + try { fn(); } catch (e) { - if (e instanceof exception) { - caught = true; + if (e instanceof exType) { + if (!exMessage || e.message === exMessage) { + caught = true; + } } } @@ -123,6 +142,8 @@ test("escaping expressions", function() { shouldCompileTo("{{awesome}}", {awesome: "&\"'`\\<>"}, '&"'`\\<>', "by default expressions should be escaped"); + shouldCompileTo("{{awesome}}", {awesome: "Escaped, <b> looks like: <b>"}, 'Escaped, <b> looks like: &lt;b&gt;', + "escaping should properly handle amperstands"); }); test("functions returning safestrings shouldn't be escaped", function() { @@ -134,10 +155,14 @@ test("functions returning safestrings shouldn't be escaped", function() { test("functions", function() { shouldCompileTo("{{awesome}}", {awesome: function() { return "Awesome"; }}, "Awesome", "functions are called and render their output"); + shouldCompileTo("{{awesome}}", {awesome: function() { return this.more; }, more: "More awesome"}, "More awesome", + "functions are bound to the context"); }); test("paths with hyphens", function() { shouldCompileTo("{{foo-bar}}", {"foo-bar": "baz"}, "baz", "Paths can contain hyphens (-)"); + shouldCompileTo("{{foo.foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)"); + shouldCompileTo("{{foo/foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)"); }); test("nested paths", function() { @@ -234,6 +259,16 @@ test("array", function() { }); +test("array with @index", function() { + var string = "{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!"; + var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"}; + + var template = CompilerContext.compile(string); + var result = template(hash); + + equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used"); +}); + test("empty block", function() { var string = "{{#goodbyes}}{{/goodbyes}}cruel {{world}}!"; var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"}; @@ -459,14 +494,14 @@ test("rendering undefined partial throws an exception", function() { shouldThrow(function() { var template = CompilerContext.compile("{{> whatever}}"); template(); - }, Handlebars.Exception, "Should throw exception"); + }, [Handlebars.Exception, 'The partial whatever could not be found'], "Should throw exception"); }); test("rendering template partial in vm mode throws an exception", function() { shouldThrow(function() { var template = CompilerContext.compile("{{> whatever}}"); template(); - }, Handlebars.Exception, "Should throw exception"); + }, [Handlebars.Exception, 'The partial whatever could not be found'], "Should throw exception"); }); test("rendering function partial in vm mode", function() { @@ -602,6 +637,11 @@ test("Invert blocks work in knownHelpers only mode", function() { var result = template({foo: false}); equal(result, "bar", "'bar' should === '" + result); }); +test("Functions are bound to the context in knownHelpers only mode", function() { + var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true}); + var result = template({foo: function() { return this.bar; }, bar: 'bar'}); + equal(result, "bar", "'bar' should === '" + result); +}); suite("blockHelperMissing"); @@ -610,6 +650,11 @@ test("lambdas are resolved by blockHelperMissing, not handlebars proper", functi var data = { truthy: function() { return true; } }; shouldCompileTo(string, data, "yep"); }); +test("lambdas resolved by blockHelperMissing are bound to the context", function() { + var string = "{{#truthy}}yep{{/truthy}}"; + var boundData = { truthy: function() { return this.truthiness(); }, truthiness: function() { return false; } }; + shouldCompileTo(string, boundData, ""); +}); var teardown; suite("built-in helpers", { @@ -659,6 +704,22 @@ test("each", function() { "each with array argument ignores the contents when empty"); }); +test("each with an object and @key", function() { + var string = "{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!"; + var hash = {goodbyes: {"<b>#1</b>": {text: "goodbye"}, 2: {text: "GOODBYE"}}, world: "world"}; + + // Object property iteration order is undefined according to ECMA spec, + // so we need to check both possible orders + // @see http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop + var actual = compileWithPartials(string, hash); + var expected1 = "<b>#1</b>. goodbye! 2. GOODBYE! cruel world!"; + var expected2 = "2. GOODBYE! <b>#1</b>. goodbye! cruel world!"; + + ok(actual === expected1 || actual === expected2, "each with object argument iterates over the contents when not empty"); + shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!", + "each with object argument ignores the contents when empty"); +}); + test("each with @index", function() { var string = "{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!"; var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"}; diff --git a/spec/tokenizer_spec.rb b/spec/tokenizer_spec.rb index 2517ade..7a771ba 100644 --- a/spec/tokenizer_spec.rb +++ b/spec/tokenizer_spec.rb @@ -51,6 +51,15 @@ describe "Tokenizer" do result[4].should be_token("CONTENT", "{{bar}} ") end + it "supports escaping multiple delimiters" do + result = tokenize("{{foo}} \\{{bar}} \\{{baz}}") + result.should match_tokens(%w(OPEN ID CLOSE CONTENT CONTENT CONTENT)) + + result[3].should be_token("CONTENT", " ") + result[4].should be_token("CONTENT", "{{bar}} ") + result[5].should be_token("CONTENT", "{{baz}}") + end + it "supports escaping a triple stash" do result = tokenize("{{foo}} \\{{{bar}}} {{baz}}") result.should match_tokens(%w(OPEN ID CLOSE CONTENT CONTENT OPEN ID CLOSE)) @@ -149,6 +158,18 @@ describe "Tokenizer" do result[1].should be_token("COMMENT", " this is a comment ") end + it "tokenizes a block comment as 'COMMENT'" do + result = tokenize("foo {{!-- this is a {{comment}} --}} bar {{ baz }}") + result.should match_tokens(%w(CONTENT COMMENT CONTENT OPEN ID CLOSE)) + result[1].should be_token("COMMENT", " this is a {{comment}} ") + end + + it "tokenizes a block comment with whitespace as 'COMMENT'" do + result = tokenize("foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}") + result.should match_tokens(%w(CONTENT COMMENT CONTENT OPEN ID CLOSE)) + result[1].should be_token("COMMENT", " this is a\n{{comment}}\n") + end + it "tokenizes open and closing blocks as 'OPEN_BLOCK ID CLOSE ... OPEN_ENDBLOCK ID CLOSE'" do result = tokenize("{{#foo}}content{{/foo}}") result.should match_tokens(%w(OPEN_BLOCK ID CLOSE CONTENT OPEN_ENDBLOCK ID CLOSE)) @@ -186,6 +207,12 @@ describe "Tokenizer" do result[3].should be_token("STRING", "baz") end + it "tokenizes mustaches with String params using single quotes as 'OPEN ID ID STRING CLOSE'" do + result = tokenize("{{ foo bar \'baz\' }}") + result.should match_tokens(%w(OPEN ID ID STRING CLOSE)) + result[3].should be_token("STRING", "baz") + end + it "tokenizes String params with spaces inside as 'STRING'" do result = tokenize("{{ foo bar \"baz bat\" }}") result.should match_tokens(%w(OPEN ID ID STRING CLOSE)) @@ -198,6 +225,12 @@ describe "Tokenizer" do result[2].should be_token("STRING", %{bar"baz}) end + it "tokenizes String params using single quotes with escapes quotes as 'STRING'" do + result = tokenize(%|{{ foo 'bar\\'baz' }}|) + result.should match_tokens(%w(OPEN ID STRING CLOSE)) + result[2].should be_token("STRING", %{bar'baz}) + end + it "tokenizes numbers" do result = tokenize(%|{{ foo 1 }}|) result.should match_tokens(%w(OPEN ID INTEGER CLOSE)) |