summaryrefslogtreecommitdiffstats
path: root/spec/parser.js
diff options
context:
space:
mode:
authorkpdecker <kpdecker@gmail.com>2013-06-01 16:25:45 -0500
committerkpdecker <kpdecker@gmail.com>2013-06-01 16:25:45 -0500
commitd13ae310d36a27fcbc333570f9b7ae9c7e641392 (patch)
tree51c664de2afcac915877847bb45f2859820052d5 /spec/parser.js
parent7f82f27c5f240f20f6432bd90dacf57c9ccfdded (diff)
downloadhandlebars.js-d13ae310d36a27fcbc333570f9b7ae9c7e641392.zip
handlebars.js-d13ae310d36a27fcbc333570f9b7ae9c7e641392.tar.gz
handlebars.js-d13ae310d36a27fcbc333570f9b7ae9c7e641392.tar.bz2
Convert parser and tokenizer tests to javascript
Diffstat (limited to 'spec/parser.js')
-rw-r--r--spec/parser.js170
1 files changed, 170 insertions, 0 deletions
diff --git a/spec/parser.js b/spec/parser.js
new file mode 100644
index 0000000..6fb8d9a
--- /dev/null
+++ b/spec/parser.js
@@ -0,0 +1,170 @@
+var Handlebars = require('../lib/handlebars');
+
+require('should');
+
+describe('parser', function() {
+ function ast_for(template) {
+ var ast = Handlebars.parse(template);
+ return Handlebars.print(ast);
+ }
+
+ it('parses simple mustaches', function() {
+ ast_for('{{foo}}').should.equal("{{ ID:foo [] }}\n");
+ ast_for('{{foo?}}').should.equal("{{ ID:foo? [] }}\n");
+ ast_for('{{foo_}}').should.equal("{{ ID:foo_ [] }}\n");
+ ast_for('{{foo-}}').should.equal("{{ ID:foo- [] }}\n");
+ ast_for('{{foo:}}').should.equal("{{ ID:foo: [] }}\n");
+ });
+
+ it('parses simple mustaches with data', function() {
+ ast_for("{{@foo}}").should.equal("{{ @ID:foo [] }}\n");
+ });
+
+ it('parses mustaches with paths', function() {
+ ast_for("{{foo/bar}}").should.equal("{{ PATH:foo/bar [] }}\n");
+ });
+
+ it('parses mustaches with this/foo', function() {
+ ast_for("{{this/foo}}").should.equal("{{ ID:foo [] }}\n");
+ });
+
+ it('parses mustaches with - in a path', function() {
+ ast_for("{{foo-bar}}").should.equal("{{ ID:foo-bar [] }}\n");
+ });
+
+ it('parses mustaches with parameters', function() {
+ ast_for("{{foo bar}}").should.equal("{{ ID:foo [ID:bar] }}\n");
+ });
+
+ it('parses mustaches with string parameters', function() {
+ ast_for("{{foo bar \"baz\" }}").should.equal('{{ ID:foo [ID:bar, "baz"] }}\n');
+ });
+
+ it('parses mustaches with INTEGER parameters', function() {
+ ast_for("{{foo 1}}").should.equal("{{ ID:foo [INTEGER{1}] }}\n");
+ });
+
+ it('parses mustaches with BOOLEAN parameters', function() {
+ ast_for("{{foo true}}").should.equal("{{ ID:foo [BOOLEAN{true}] }}\n");
+ ast_for("{{foo false}}").should.equal("{{ ID:foo [BOOLEAN{false}] }}\n");
+ });
+
+ it('parses mutaches with DATA parameters', function() {
+ ast_for("{{foo @bar}}").should.equal("{{ ID:foo [@ID:bar] }}\n");
+ });
+
+ it('parses mustaches with hash arguments', function() {
+ ast_for("{{foo bar=baz}}").should.equal("{{ ID:foo [] HASH{bar=ID:baz} }}\n");
+ ast_for("{{foo bar=1}}").should.equal("{{ ID:foo [] HASH{bar=INTEGER{1}} }}\n");
+ ast_for("{{foo bar=true}}").should.equal("{{ ID:foo [] HASH{bar=BOOLEAN{true}} }}\n");
+ ast_for("{{foo bar=false}}").should.equal("{{ ID:foo [] HASH{bar=BOOLEAN{false}} }}\n");
+ ast_for("{{foo bar=@baz}}").should.equal("{{ ID:foo [] HASH{bar=@ID:baz} }}\n");
+
+ ast_for("{{foo bar=baz bat=bam}}").should.equal("{{ ID:foo [] HASH{bar=ID:baz, bat=ID:bam} }}\n");
+ ast_for("{{foo bar=baz bat=\"bam\"}}").should.equal('{{ ID:foo [] HASH{bar=ID:baz, bat="bam"} }}\n');
+
+ ast_for("{{foo bat='bam'}}").should.equal('{{ ID:foo [] HASH{bat="bam"} }}\n');
+
+ ast_for("{{foo omg bar=baz bat=\"bam\"}}").should.equal('{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam"} }}\n');
+ ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should.equal('{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=INTEGER{1}} }}\n');
+ ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should.equal('{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=BOOLEAN{true}} }}\n');
+ ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should.equal('{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=BOOLEAN{false}} }}\n');
+ });
+
+ it('parses contents followed by a mustache', function() {
+ ast_for("foo bar {{baz}}").should.equal("CONTENT[ \'foo bar \' ]\n{{ ID:baz [] }}\n");
+ });
+
+ it('parses a partial', function() {
+ ast_for("{{> foo }}").should.equal("{{> PARTIAL:foo }}\n");
+ });
+
+ it('parses a partial with context', function() {
+ ast_for("{{> foo bar}}").should.equal("{{> PARTIAL:foo ID:bar }}\n");
+ });
+
+ it('parses a partial with a complex name', function() {
+ ast_for("{{> shared/partial?.bar}}").should.equal("{{> PARTIAL:shared/partial?.bar }}\n");
+ });
+
+ it('parses a comment', function() {
+ ast_for("{{! this is a comment }}").should.equal("{{! ' this is a comment ' }}\n");
+ });
+
+ it('parses a multi-line comment', function() {
+ ast_for("{{!\nthis is a multi-line comment\n}}").should.equal("{{! \'\nthis is a multi-line comment\n\' }}\n");
+ });
+
+ it('parses an inverse section', function() {
+ ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
+ });
+
+ it('parses an inverse (else-style) section', function() {
+ ast_for("{{#foo}} bar {{else}} baz {{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
+ });
+
+ it('parses empty blocks', function() {
+ ast_for("{{#foo}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n");
+ });
+
+ it('parses empty blocks with empty inverse section', function() {
+ ast_for("{{#foo}}{{^}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n");
+ });
+
+ it('parses empty blocks with empty inverse (else-style) section', function() {
+ ast_for("{{#foo}}{{else}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n");
+ });
+
+ it('parses non-empty blocks with empty inverse section', function() {
+ ast_for("{{#foo}} bar {{^}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
+ });
+
+ it('parses non-empty blocks with empty inverse (else-style) section', function() {
+ ast_for("{{#foo}} bar {{else}}{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
+ });
+
+ it('parses empty blocks with non-empty inverse section', function() {
+ ast_for("{{#foo}}{{^}} bar {{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
+ });
+
+ it('parses empty blocks with non-empty inverse (else-style) section', function() {
+ ast_for("{{#foo}}{{else}} bar {{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
+ });
+
+ it('parses a standalone inverse section', function() {
+ ast_for("{{^foo}}bar{{/foo}}").should.equal("BLOCK:\n {{ ID:foo [] }}\n {{^}}\n CONTENT[ 'bar' ]\n");
+ });
+
+ it("raises if there's a Parse error", function() {
+ (function() {
+ ast_for("{{foo}");
+ }).should.throw(/Parse error on line 1/);
+ (function() {
+ ast_for("{{foo &}}");
+ }).should.throw(/Parse error on line 1/);
+ (function() {
+ ast_for("{{#goodbyes}}{{/hellos}}");
+ }).should.throw(/goodbyes doesn't match hellos/);
+ });
+
+ it('knows how to report the correct line number in errors', function() {
+ (function() {
+ ast_for("hello\nmy\n{{foo}");
+ }).should.throw(/Parse error on line 3/);
+ (function() {
+ ast_for("hello\n\nmy\n\n{{foo}");
+ }).should.throw(/Parse error on line 5/);
+ });
+
+ it('knows how to report the correct line number in errors when the first character is a newline', function() {
+ (function() {
+ ast_for("\n\nhello\n\nmy\n\n{{foo}");
+ }).should.throw(/Parse error on line 7/);
+ });
+
+ describe('externally compiled AST', function() {
+ it('can pass through an already-compiled AST', function() {
+ ast_for(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")])).should.equal("CONTENT[ \'Hello\' ]\n");
+ });
+ });
+});