From 0cf5657c43791817776477b727b30ad7f6275707 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 00:35:14 -0600 Subject: [PATCH 01/12] Use shouldThrow helper for test asserts --- spec/basic.js | 9 +++++---- spec/blocks.js | 4 ++-- spec/data.js | 14 +++++++------- spec/env/common.js | 18 ++++++++++++++++++ spec/helpers.js | 20 ++++++++++---------- spec/partials.js | 8 ++++---- spec/regressions.js | 14 +++++++------- 7 files changed, 53 insertions(+), 34 deletions(-) diff --git a/spec/basic.js b/spec/basic.js index ee154a165..245022c61 100644 --- a/spec/basic.js +++ b/spec/basic.js @@ -1,3 +1,4 @@ +/*global CompilerContext, Handlebars, beforeEach, shouldCompileTo */ global.handlebarsEnv = null; beforeEach(function() { @@ -160,9 +161,9 @@ describe("basic context", function() { it("this keyword nested inside path", function() { var string = "{{#hellos}}{{text/this/foo}}{{/hellos}}"; - (function() { + shouldThrow(function() { CompilerContext.compile(string); - }).should.throw(Error); + }, Error); }); it("this keyword in helpers", function() { @@ -181,8 +182,8 @@ describe("basic context", function() { it("this keyword nested inside helpers param", function() { var string = "{{#hellos}}{{foo text/this/foo}}{{/hellos}}"; - (function() { + shouldThrow(function() { CompilerContext.compile(string); - }).should.throw(Error); + }, Error); }); }); diff --git a/spec/blocks.js b/spec/blocks.js index 1880eb584..d72a44ec4 100644 --- a/spec/blocks.js +++ b/spec/blocks.js @@ -42,9 +42,9 @@ describe('blocks', function() { it("block with complex lookup using nested context", function() { var string = "{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}"; - (function() { + shouldThrow(function() { CompilerContext.compile(string); - }).should.throw(Error); + }, Error); }); it("block with deep nested complex lookup", function() { diff --git a/spec/data.js b/spec/data.js index cf9424e97..d84eae729 100644 --- a/spec/data.js +++ b/spec/data.js @@ -1,4 +1,4 @@ -/*global CompilerContext */ +/*global CompilerContext, Handlebars, handlebarsEnv, shouldThrow */ describe('data', function() { it("passing in data to a compiled function that expects data - works with helpers", function() { var template = CompilerContext.compile("{{hello}}", {data: true}); @@ -88,25 +88,25 @@ describe('data', function() { it("parameter data throws when using this scope references", function() { var string = "{{#goodbyes}}{{text}} cruel {{@./name}}! {{/goodbyes}}"; - (function() { + shouldThrow(function() { CompilerContext.compile(string); - }).should.throw(Error); + }, Error); }); it("parameter data throws when using parent scope references", function() { var string = "{{#goodbyes}}{{text}} cruel {{@../name}}! {{/goodbyes}}"; - (function() { + shouldThrow(function() { CompilerContext.compile(string); - }).should.throw(Error); + }, Error); }); it("parameter data throws when using complex scope references", function() { var string = "{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}"; - (function() { + shouldThrow(function() { CompilerContext.compile(string); - }).should.throw(Error); + }, Error); }); it("data is inherited downstream", function() { diff --git a/spec/env/common.js b/spec/env/common.js index 53ddd61ae..7dfe16ea0 100644 --- a/spec/env/common.js +++ b/spec/env/common.js @@ -26,3 +26,21 @@ global.compileWithPartials = function(string, hashOrArray, partials) { global.equals = global.equal = function(a, b, msg) { a.should.equal(b, msg || ''); }; + +global.shouldThrow = function(callback, type, msg) { + var failed; + try { + callback(); + failed = true; + } catch (err) { + if (type && !(err instanceof type)) { + throw new Error('Type failure'); + } + if (msg && !(msg.test ? msg.test(err.message) : msg === err.message)) { + throw new Error('Message failure'); + } + } + if (failed) { + throw new Error('It failed to throw'); + } +}; diff --git a/spec/helpers.js b/spec/helpers.js index c5ea574bc..b0eb91e65 100644 --- a/spec/helpers.js +++ b/spec/helpers.js @@ -1,4 +1,4 @@ -/*global CompilerContext, shouldCompileTo, shouldCompileToWithPartials */ +/*global CompilerContext, Handlebars, shouldCompileTo, shouldCompileToWithPartials, shouldThrow, handlebarsEnv */ describe('helpers', function() { it("helper with complex lookup$", function() { var string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}"; @@ -212,10 +212,10 @@ describe('helpers', function() { }); it("using a quote in the middle of a parameter raises an error", function() { - (function() { - var string = 'Message: {{hello wo"rld"}}'; + var string = 'Message: {{hello wo"rld"}}'; + shouldThrow(function() { CompilerContext.compile(string); - }).should.throw(Error); + }, Error); }); it("escaping a String is possible", function(){ @@ -351,10 +351,10 @@ describe('helpers', function() { describe("helperMissing", function() { it("if a context is not found, helperMissing is used", function() { - (function() { + shouldThrow(function() { var template = CompilerContext.compile("{{hello}} {{link_to world}}"); template({}); - }).should.throw(/Missing helper: 'link_to'/); + }, undefined, /Missing helper: 'link_to'/); }); it("if a context is not found, custom helperMissing is used", function() { @@ -417,9 +417,9 @@ describe('helpers', function() { equal(result, "bar", "'bar' should === '" + result); }); it("Unknown helper call in knownHelpers only mode should throw", function() { - (function() { + shouldThrow(function() { CompilerContext.compile("{{typeof hello}}", {knownHelpersOnly: true}); - }).should.throw(Error); + }, Error); }); }); @@ -491,7 +491,7 @@ describe('helpers', function() { cruel: function(world) { return "cruel " + world.toUpperCase(); - }, + } }; var context = { @@ -513,7 +513,7 @@ describe('helpers', function() { cruel: function(world) { return "cruel " + world.toUpperCase(); - }, + } }; var context = { diff --git a/spec/partials.js b/spec/partials.js index 7ff9d3c25..3f46c377a 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -32,17 +32,17 @@ describe('partials', function() { }); it("rendering undefined partial throws an exception", function() { - (function() { + shouldThrow(function() { var template = CompilerContext.compile("{{> whatever}}"); template(); - }).should.throw(Handlebars.Exception, 'The partial whatever could not be found'); + }, Handlebars.Exception, 'The partial whatever could not be found'); }); it("rendering template partial in vm mode throws an exception", function() { - (function() { + shouldThrow(function() { var template = CompilerContext.compile("{{> whatever}}"); template(); - }).should.throw(Handlebars.Exception, 'The partial whatever could not be found'); + }, Handlebars.Exception, 'The partial whatever could not be found'); }); it("rendering function partial in vm mode", function() { diff --git a/spec/regressions.js b/spec/regressions.js index eec5a490b..23fcbc0b6 100644 --- a/spec/regressions.js +++ b/spec/regressions.js @@ -1,4 +1,4 @@ -/*global CompilerContext, shouldCompileTo */ +/*global CompilerContext, Handlebars, shouldCompileTo, shouldThrow */ describe('Regressions', function() { it("GH-94: Cannot read property of undefined", function() { var data = {"books":[{"title":"The origin of species","author":{"name":"Charles Darwin"}},{"title":"Lazarillo de Tormes"}]}; @@ -85,12 +85,12 @@ describe('Regressions', function() { }); it('GH-437: Matching escaping', function() { - (function() { + shouldThrow(function() { CompilerContext.compile('{{{a}}'); - }).should.throw(Error); - (function() { + }, Error); + shouldThrow(function() { CompilerContext.compile('{{a}}}'); - }).should.throw(Error); + }, Error); }); it("Mustache man page", function() { @@ -106,9 +106,9 @@ describe('Regressions', function() { }); it("Passing falsy values to Handlebars.compile throws an error", function() { - (function() { + shouldThrow(function() { CompilerContext.compile(null); - }).should.throw("You must pass a string or Handlebars AST to Handlebars.precompile. You passed null"); + }, Error, 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null'); }); if (Handlebars.AST) { From 494232425033d7d3aade5b0eba641a93e68e9f89 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 00:48:28 -0600 Subject: [PATCH 02/12] Move away from should asserts to internal This is needed as neither Sinon nor Chai support in-browser testing under IE. --- package.json | 1 - spec/builtins.js | 2 +- spec/env/common.js | 10 +- spec/parser.js | 117 +++++++++++----------- spec/require.js | 8 +- spec/tokenizer.js | 237 ++++++++++++++++++++++----------------------- spec/utils.js | 44 +++++---- 7 files changed, 211 insertions(+), 208 deletions(-) diff --git a/package.json b/package.json index 2b1031028..99a62bd7a 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "mocha": "*", "mustache": "~0.7.2", "semver": "~2.1.0", - "should": "~1.2.2", "underscore": "~1.5.1" }, "main": "lib/index.js", diff --git a/spec/builtins.js b/spec/builtins.js index ef2d82971..766541a77 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -73,7 +73,7 @@ describe('builtin helpers', function() { var expected1 = "<b>#1</b>. goodbye! 2. GOODBYE! cruel world!"; var expected2 = "2. GOODBYE! <b>#1</b>. goodbye! cruel world!"; - (actual === expected1 || actual === expected2).should.equal(true, "each with object argument iterates over the contents when not empty"); + equals(actual === expected1 || actual === expected2, true, "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"); }); diff --git a/spec/env/common.js b/spec/env/common.js index 7dfe16ea0..53bf977da 100644 --- a/spec/env/common.js +++ b/spec/env/common.js @@ -1,12 +1,12 @@ -global.should = require('should'); - global.shouldCompileTo = function(string, hashOrArray, expected, message) { shouldCompileToWithPartials(string, hashOrArray, false, expected, message); }; global.shouldCompileToWithPartials = function(string, hashOrArray, partials, expected, message) { var result = compileWithPartials(string, hashOrArray, partials); - result.should.equal(expected, "'" + expected + "' should === '" + result + "': " + message); + if (result !== expected) { + throw new Error("'" + expected + "' should === '" + result + "': " + message); + } }; global.compileWithPartials = function(string, hashOrArray, partials) { @@ -24,7 +24,9 @@ global.compileWithPartials = function(string, hashOrArray, partials) { global.equals = global.equal = function(a, b, msg) { - a.should.equal(b, msg || ''); + if (a !== b) { + throw new Error("'" + b + "' should === '" + a + "'" + (msg ? ": " + msg : '')); + } }; global.shouldThrow = function(callback, type, msg) { diff --git a/spec/parser.js b/spec/parser.js index 3397105fd..70c16359e 100644 --- a/spec/parser.js +++ b/spec/parser.js @@ -1,3 +1,4 @@ +/*global Handlebars */ describe('parser', function() { if (!Handlebars.print) { return; @@ -9,165 +10,165 @@ describe('parser', function() { } 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"); + equals(ast_for('{{foo}}'), "{{ ID:foo [] }}\n"); + equals(ast_for('{{foo?}}'), "{{ ID:foo? [] }}\n"); + equals(ast_for('{{foo_}}'), "{{ ID:foo_ [] }}\n"); + equals(ast_for('{{foo-}}'), "{{ ID:foo- [] }}\n"); + equals(ast_for('{{foo:}}'), "{{ ID:foo: [] }}\n"); }); it('parses simple mustaches with data', function() { - ast_for("{{@foo}}").should.equal("{{ @ID:foo [] }}\n"); + equals(ast_for("{{@foo}}"), "{{ @ID:foo [] }}\n"); }); it('parses mustaches with paths', function() { - ast_for("{{foo/bar}}").should.equal("{{ PATH:foo/bar [] }}\n"); + equals(ast_for("{{foo/bar}}"), "{{ PATH:foo/bar [] }}\n"); }); it('parses mustaches with this/foo', function() { - ast_for("{{this/foo}}").should.equal("{{ ID:foo [] }}\n"); + equals(ast_for("{{this/foo}}"), "{{ ID:foo [] }}\n"); }); it('parses mustaches with - in a path', function() { - ast_for("{{foo-bar}}").should.equal("{{ ID:foo-bar [] }}\n"); + equals(ast_for("{{foo-bar}}"), "{{ ID:foo-bar [] }}\n"); }); it('parses mustaches with parameters', function() { - ast_for("{{foo bar}}").should.equal("{{ ID:foo [ID:bar] }}\n"); + equals(ast_for("{{foo bar}}"), "{{ 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'); + equals(ast_for("{{foo bar \"baz\" }}"), '{{ ID:foo [ID:bar, "baz"] }}\n'); }); it('parses mustaches with INTEGER parameters', function() { - ast_for("{{foo 1}}").should.equal("{{ ID:foo [INTEGER{1}] }}\n"); + equals(ast_for("{{foo 1}}"), "{{ 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"); + equals(ast_for("{{foo true}}"), "{{ ID:foo [BOOLEAN{true}] }}\n"); + equals(ast_for("{{foo false}}"), "{{ ID:foo [BOOLEAN{false}] }}\n"); }); it('parses mutaches with DATA parameters', function() { - ast_for("{{foo @bar}}").should.equal("{{ ID:foo [@ID:bar] }}\n"); + equals(ast_for("{{foo @bar}}"), "{{ 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"); + equals(ast_for("{{foo bar=baz}}"), "{{ ID:foo [] HASH{bar=ID:baz} }}\n"); + equals(ast_for("{{foo bar=1}}"), "{{ ID:foo [] HASH{bar=INTEGER{1}} }}\n"); + equals(ast_for("{{foo bar=true}}"), "{{ ID:foo [] HASH{bar=BOOLEAN{true}} }}\n"); + equals(ast_for("{{foo bar=false}}"), "{{ ID:foo [] HASH{bar=BOOLEAN{false}} }}\n"); + equals(ast_for("{{foo bar=@baz}}"), "{{ 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'); + equals(ast_for("{{foo bar=baz bat=bam}}"), "{{ ID:foo [] HASH{bar=ID:baz, bat=ID:bam} }}\n"); + equals(ast_for("{{foo bar=baz bat=\"bam\"}}"), '{{ ID:foo [] HASH{bar=ID:baz, bat="bam"} }}\n'); - ast_for("{{foo bat='bam'}}").should.equal('{{ ID:foo [] HASH{bat="bam"} }}\n'); + equals(ast_for("{{foo bat='bam'}}"), '{{ 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'); + equals(ast_for("{{foo omg bar=baz bat=\"bam\"}}"), '{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam"} }}\n'); + equals(ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}"), '{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=INTEGER{1}} }}\n'); + equals(ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}"), '{{ ID:foo [ID:omg] HASH{bar=ID:baz, bat="bam", baz=BOOLEAN{true}} }}\n'); + equals(ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}"), '{{ 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"); + equals(ast_for("foo bar {{baz}}"), "CONTENT[ \'foo bar \' ]\n{{ ID:baz [] }}\n"); }); it('parses a partial', function() { - ast_for("{{> foo }}").should.equal("{{> PARTIAL:foo }}\n"); + equals(ast_for("{{> foo }}"), "{{> PARTIAL:foo }}\n"); }); it('parses a partial with context', function() { - ast_for("{{> foo bar}}").should.equal("{{> PARTIAL:foo ID:bar }}\n"); + equals(ast_for("{{> foo bar}}"), "{{> 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"); + equals(ast_for("{{> shared/partial?.bar}}"), "{{> PARTIAL:shared/partial?.bar }}\n"); }); it('parses a comment', function() { - ast_for("{{! this is a comment }}").should.equal("{{! ' this is a comment ' }}\n"); + equals(ast_for("{{! this is a comment }}"), "{{! ' 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"); + equals(ast_for("{{!\nthis is a multi-line comment\n}}"), "{{! \'\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"); + equals(ast_for("{{#foo}} bar {{^}} baz {{/foo}}"), "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"); + equals(ast_for("{{#foo}} bar {{else}} baz {{/foo}}"), "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"); + equals(ast_for("{{#foo}}{{/foo}}"), "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"); + equals(ast_for("{{#foo}}{{^}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\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"); + equals(ast_for("{{#foo}}{{else}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\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"); + equals(ast_for("{{#foo}} bar {{^}}{{/foo}}"), "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"); + equals(ast_for("{{#foo}} bar {{else}}{{/foo}}"), "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"); + equals(ast_for("{{#foo}}{{^}} bar {{/foo}}"), "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"); + equals(ast_for("{{#foo}}{{else}} bar {{/foo}}"), "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"); + equals(ast_for("{{^foo}}bar{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n {{^}}\n CONTENT[ 'bar' ]\n"); }); it("raises if there's a Parse error", function() { - (function() { + shouldThrow(function() { ast_for("foo{{^}}bar"); - }).should.throw(/Parse error on line 1/); - (function() { + }, Error, /Parse error on line 1/); + shouldThrow(function() { ast_for("{{foo}"); - }).should.throw(/Parse error on line 1/); - (function() { + }, Error, /Parse error on line 1/); + shouldThrow(function() { ast_for("{{foo &}}"); - }).should.throw(/Parse error on line 1/); - (function() { + }, Error, /Parse error on line 1/); + shouldThrow(function() { ast_for("{{#goodbyes}}{{/hellos}}"); - }).should.throw(/goodbyes doesn't match hellos/); + }, Error, /goodbyes doesn't match hellos/); }); it('knows how to report the correct line number in errors', function() { - (function() { + shouldThrow(function() { ast_for("hello\nmy\n{{foo}"); - }).should.throw(/Parse error on line 3/); - (function() { + }, Error, /Parse error on line 3/); + shouldThrow(function() { ast_for("hello\n\nmy\n\n{{foo}"); - }).should.throw(/Parse error on line 5/); + }, Error, /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() { + shouldThrow(function() { ast_for("\n\nhello\n\nmy\n\n{{foo}"); - }).should.throw(/Parse error on line 7/); + }, Error, /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"); + equals(ast_for(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")])), "CONTENT[ \'Hello\' ]\n"); }); }); }); diff --git a/spec/require.js b/spec/require.js index d750d7d3d..11b7a0c93 100644 --- a/spec/require.js +++ b/spec/require.js @@ -2,22 +2,22 @@ if (typeof(require) !== 'undefined' && require.extensions[".handlebars"]) { describe('Require', function() { it('Load .handlebars files with require()', function() { var template = require("./artifacts/example_1"); - template.should.eql(require("./artifacts/example_1.handlebars")); + equal(template, require("./artifacts/example_1.handlebars")); var expected = 'foo\n'; var result = template({foo: "foo"}); - result.should.equal(expected); + equal(result, expected); }); it('Load .hbs files with require()', function() { var template = require("./artifacts/example_2"); - template.should.eql(require("./artifacts/example_2.hbs")); + equal(template, require("./artifacts/example_2.hbs")); var expected = 'Hello, World!\n'; var result = template({name: "World"}); - result.should.equal(expected); + equal(result, expected); }); }); } diff --git a/spec/tokenizer.js b/spec/tokenizer.js index de981e427..b6719400b 100644 --- a/spec/tokenizer.js +++ b/spec/tokenizer.js @@ -1,12 +1,11 @@ -var should = require('should'); - -should.Assertion.prototype.match_tokens = function(tokens) { - this.obj.forEach(function(value, index) { - value.name.should.equal(tokens[index]); +function shouldMatchTokens(result, tokens) { + result.forEach(function(value, index) { + equals(value.name, tokens[index]); }); }; -should.Assertion.prototype.be_token = function(name, text) { - this.obj.should.eql({name: name, text: text}); +function shouldBeToken(result, name, text) { + equals(result.name, name); + equals(result.text, text); }; describe('Tokenizer', function() { @@ -35,324 +34,324 @@ describe('Tokenizer', function() { it('tokenizes a simple mustache as "OPEN ID CLOSE"', function() { var result = tokenize("{{foo}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE']); - result[1].should.be_token("ID", "foo"); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']); + shouldBeToken(result[1], "ID", "foo"); }); it('supports unescaping with &', function() { var result = tokenize("{{&bar}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']); - result[0].should.be_token("OPEN", "{{&"); - result[1].should.be_token("ID", "bar"); + shouldBeToken(result[0], "OPEN", "{{&"); + shouldBeToken(result[1], "ID", "bar"); }); it('supports unescaping with {{{', function() { var result = tokenize("{{{bar}}}"); - result.should.match_tokens(['OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED']); + shouldMatchTokens(result, ['OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED']); - result[1].should.be_token("ID", "bar"); + shouldBeToken(result[1], "ID", "bar"); }); it('supports escaping delimiters', function() { var result = tokenize("{{foo}} \\{{bar}} {{baz}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); - result[3].should.be_token("CONTENT", " "); - result[4].should.be_token("CONTENT", "{{bar}} "); + shouldBeToken(result[3], "CONTENT", " "); + shouldBeToken(result[4], "CONTENT", "{{bar}} "); }); it('supports escaping multiple delimiters', function() { var result = tokenize("{{foo}} \\{{bar}} \\{{baz}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']); + shouldMatchTokens(result, ['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}}"); + shouldBeToken(result[3], "CONTENT", " "); + shouldBeToken(result[4], "CONTENT", "{{bar}} "); + shouldBeToken(result[5], "CONTENT", "{{baz}}"); }); it('supports escaping a triple stash', function() { var result = tokenize("{{foo}} \\{{{bar}}} {{baz}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); - result[4].should.be_token("CONTENT", "{{{bar}}} "); + shouldBeToken(result[4], "CONTENT", "{{{bar}}} "); }); it('supports escaping escape character', function() { var result = tokenize("{{foo}} \\\\{{bar}} {{baz}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); - result[3].should.be_token("CONTENT", " \\"); - result[5].should.be_token("ID", "bar"); + shouldBeToken(result[3], "CONTENT", " \\"); + shouldBeToken(result[5], "ID", "bar"); }); it('supports escaping multiple escape characters', function() { var result = tokenize("{{foo}} \\\\{{bar}} \\\\{{baz}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); - result[3].should.be_token("CONTENT", " \\"); - result[5].should.be_token("ID", "bar"); - result[7].should.be_token("CONTENT", " \\"); - result[9].should.be_token("ID", "baz"); + shouldBeToken(result[3], "CONTENT", " \\"); + shouldBeToken(result[5], "ID", "bar"); + shouldBeToken(result[7], "CONTENT", " \\"); + shouldBeToken(result[9], "ID", "baz"); }); it('supports mixed escaped delimiters and escaped escape characters', function() { var result = tokenize("{{foo}} \\\\{{bar}} \\{{baz}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']); - result[3].should.be_token("CONTENT", " \\"); - result[4].should.be_token("OPEN", "{{"); - result[5].should.be_token("ID", "bar"); - result[7].should.be_token("CONTENT", " "); - result[8].should.be_token("CONTENT", "{{baz}}"); + shouldBeToken(result[3], "CONTENT", " \\"); + shouldBeToken(result[4], "OPEN", "{{"); + shouldBeToken(result[5], "ID", "bar"); + shouldBeToken(result[7], "CONTENT", " "); + shouldBeToken(result[8], "CONTENT", "{{baz}}"); }); it('supports escaped escape character on a triple stash', function() { var result = tokenize("{{foo}} \\\\{{{bar}}} {{baz}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); - result[3].should.be_token("CONTENT", " \\"); - result[5].should.be_token("ID", "bar"); + shouldBeToken(result[3], "CONTENT", " \\"); + shouldBeToken(result[5], "ID", "bar"); }); it('tokenizes a simple path', function() { var result = tokenize("{{foo/bar}}"); - result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); }); it('allows dot notation', function() { var result = tokenize("{{foo.bar}}"); - result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); - tokenize("{{foo.bar.baz}}").should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(tokenize("{{foo.bar.baz}}"), ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); }); it('allows path literals with []', function() { var result = tokenize("{{foo.[bar]}}"); - result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); }); it('allows multiple path literals on a line with []', function() { var result = tokenize("{{foo.[bar]}}{{foo.[baz]}}"); - result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE', 'OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE', 'OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); }); it('tokenizes {{.}} as OPEN ID CLOSE', function() { var result = tokenize("{{.}}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']); }); it('tokenizes a path as "OPEN (ID SEP)* ID CLOSE"', function() { var result = tokenize("{{../foo/bar}}"); - result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); - result[1].should.be_token("ID", ".."); + shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldBeToken(result[1], "ID", ".."); }); it('tokenizes a path with .. as a parent path', function() { var result = tokenize("{{../foo.bar}}"); - result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); - result[1].should.be_token("ID", ".."); + shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldBeToken(result[1], "ID", ".."); }); it('tokenizes a path with this/foo as OPEN ID SEP ID CLOSE', function() { var result = tokenize("{{this/foo}}"); - result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); - result[1].should.be_token("ID", "this"); - result[3].should.be_token("ID", "foo"); + shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldBeToken(result[1], "ID", "this"); + shouldBeToken(result[3], "ID", "foo"); }); it('tokenizes a simple mustache with spaces as "OPEN ID CLOSE"', function() { var result = tokenize("{{ foo }}"); - result.should.match_tokens(['OPEN', 'ID', 'CLOSE']); - result[1].should.be_token("ID", "foo"); + shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']); + shouldBeToken(result[1], "ID", "foo"); }); it('tokenizes a simple mustache with line breaks as "OPEN ID ID CLOSE"', function() { var result = tokenize("{{ foo \n bar }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'CLOSE']); - result[1].should.be_token("ID", "foo"); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'CLOSE']); + shouldBeToken(result[1], "ID", "foo"); }); it('tokenizes raw content as "CONTENT"', function() { var result = tokenize("foo {{ bar }} baz"); - result.should.match_tokens(['CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT']); - result[0].should.be_token("CONTENT", "foo "); - result[4].should.be_token("CONTENT", " baz"); + shouldMatchTokens(result, ['CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT']); + shouldBeToken(result[0], "CONTENT", "foo "); + shouldBeToken(result[4], "CONTENT", " baz"); }); it('tokenizes a partial as "OPEN_PARTIAL ID CLOSE"', function() { var result = tokenize("{{> foo}}"); - result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']); }); it('tokenizes a partial with context as "OPEN_PARTIAL ID ID CLOSE"', function() { var result = tokenize("{{> foo bar }}"); - result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'ID', 'CLOSE']); }); it('tokenizes a partial without spaces as "OPEN_PARTIAL ID CLOSE"', function() { var result = tokenize("{{>foo}}"); - result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']); }); it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() { var result = tokenize("{{>foo }}"); - result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']); }); it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() { var result = tokenize("{{>foo/bar.baz }}"); - result.should.match_tokens(['OPEN_PARTIAL', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']); }); it('tokenizes a comment as "COMMENT"', function() { var result = tokenize("foo {{! this is a comment }} bar {{ baz }}"); - result.should.match_tokens(['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); - result[1].should.be_token("COMMENT", " this is a comment "); + shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldBeToken(result[1], "COMMENT", " this is a comment "); }); it('tokenizes a block comment as "COMMENT"', function() { var result = tokenize("foo {{!-- this is a {{comment}} --}} bar {{ baz }}"); - result.should.match_tokens(['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); - result[1].should.be_token("COMMENT", " this is a {{comment}} "); + shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldBeToken(result[1], "COMMENT", " this is a {{comment}} "); }); it('tokenizes a block comment with whitespace as "COMMENT"', function() { var result = tokenize("foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}"); - result.should.match_tokens(['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); - result[1].should.be_token("COMMENT", " this is a\n{{comment}}\n"); + shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + shouldBeToken(result[1], "COMMENT", " this is a\n{{comment}}\n"); }); it('tokenizes open and closing blocks as OPEN_BLOCK, ID, CLOSE ..., OPEN_ENDBLOCK ID CLOSE', function() { var result = tokenize("{{#foo}}content{{/foo}}"); - result.should.match_tokens(['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']); }); it('tokenizes inverse sections as "OPEN_INVERSE CLOSE"', function() { - tokenize("{{^}}").should.match_tokens(['OPEN_INVERSE', 'CLOSE']); - tokenize("{{else}}").should.match_tokens(['OPEN_INVERSE', 'CLOSE']); - tokenize("{{ else }}").should.match_tokens(['OPEN_INVERSE', 'CLOSE']); + shouldMatchTokens(tokenize("{{^}}"), ['OPEN_INVERSE', 'CLOSE']); + shouldMatchTokens(tokenize("{{else}}"), ['OPEN_INVERSE', 'CLOSE']); + shouldMatchTokens(tokenize("{{ else }}"), ['OPEN_INVERSE', 'CLOSE']); }); it('tokenizes inverse sections with ID as "OPEN_INVERSE ID CLOSE"', function() { var result = tokenize("{{^foo}}"); - result.should.match_tokens(['OPEN_INVERSE', 'ID', 'CLOSE']); - result[1].should.be_token("ID", "foo"); + shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']); + shouldBeToken(result[1], "ID", "foo"); }); it('tokenizes inverse sections with ID and spaces as "OPEN_INVERSE ID CLOSE"', function() { var result = tokenize("{{^ foo }}"); - result.should.match_tokens(['OPEN_INVERSE', 'ID', 'CLOSE']); - result[1].should.be_token("ID", "foo"); + shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']); + shouldBeToken(result[1], "ID", "foo"); }); it('tokenizes mustaches with params as "OPEN ID ID ID CLOSE"', function() { var result = tokenize("{{ foo bar baz }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'CLOSE']); - result[1].should.be_token("ID", "foo"); - result[2].should.be_token("ID", "bar"); - result[3].should.be_token("ID", "baz"); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'CLOSE']); + shouldBeToken(result[1], "ID", "foo"); + shouldBeToken(result[2], "ID", "bar"); + shouldBeToken(result[3], "ID", "baz"); }); it('tokenizes mustaches with String params as "OPEN ID ID STRING CLOSE"', function() { var result = tokenize("{{ foo bar \"baz\" }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']); - result[3].should.be_token("STRING", "baz"); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']); + shouldBeToken(result[3], "STRING", "baz"); }); it('tokenizes mustaches with String params using single quotes as "OPEN ID ID STRING CLOSE"', function() { var result = tokenize("{{ foo bar \'baz\' }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']); - result[3].should.be_token("STRING", "baz"); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']); + shouldBeToken(result[3], "STRING", "baz"); }); it('tokenizes String params with spaces inside as "STRING"', function() { var result = tokenize("{{ foo bar \"baz bat\" }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']); - result[3].should.be_token("STRING", "baz bat"); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']); + shouldBeToken(result[3], "STRING", "baz bat"); }); it('tokenizes String params with escapes quotes as STRING', function() { var result = tokenize('{{ foo "bar\\"baz" }}'); - result.should.match_tokens(['OPEN', 'ID', 'STRING', 'CLOSE']); - result[2].should.be_token("STRING", 'bar"baz'); + shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']); + shouldBeToken(result[2], "STRING", 'bar"baz'); }); it('tokenizes String params using single quotes with escapes quotes as STRING', function() { var result = tokenize("{{ foo 'bar\\'baz' }}"); - result.should.match_tokens(['OPEN', 'ID', 'STRING', 'CLOSE']); - result[2].should.be_token("STRING", "bar'baz"); + shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']); + shouldBeToken(result[2], "STRING", "bar'baz"); }); it('tokenizes numbers', function() { var result = tokenize('{{ foo 1 }}'); - result.should.match_tokens(['OPEN', 'ID', 'INTEGER', 'CLOSE']); - result[2].should.be_token("INTEGER", "1"); + shouldMatchTokens(result, ['OPEN', 'ID', 'INTEGER', 'CLOSE']); + shouldBeToken(result[2], "INTEGER", "1"); result = tokenize('{{ foo -1 }}'); - result.should.match_tokens(['OPEN', 'ID', 'INTEGER', 'CLOSE']); - result[2].should.be_token("INTEGER", "-1"); + shouldMatchTokens(result, ['OPEN', 'ID', 'INTEGER', 'CLOSE']); + shouldBeToken(result[2], "INTEGER", "-1"); }); it('tokenizes booleans', function() { var result = tokenize('{{ foo true }}'); - result.should.match_tokens(['OPEN', 'ID', 'BOOLEAN', 'CLOSE']); - result[2].should.be_token("BOOLEAN", "true"); + shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']); + shouldBeToken(result[2], "BOOLEAN", "true"); result = tokenize('{{ foo false }}'); - result.should.match_tokens(['OPEN', 'ID', 'BOOLEAN', 'CLOSE']); - result[2].should.be_token("BOOLEAN", "false"); + shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']); + shouldBeToken(result[2], "BOOLEAN", "false"); }); it('tokenizes hash arguments', function() { var result = tokenize("{{ foo bar=baz }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); result = tokenize("{{ foo bar baz=bat }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); result = tokenize("{{ foo bar baz=1 }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'INTEGER', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'INTEGER', 'CLOSE']); result = tokenize("{{ foo bar baz=true }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']); result = tokenize("{{ foo bar baz=false }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']); result = tokenize("{{ foo bar\n baz=bat }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']); result = tokenize("{{ foo bar baz=\"bat\" }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']); result = tokenize("{{ foo bar baz=\"bat\" bam=wot }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'ID', 'EQUALS', 'ID', 'CLOSE']); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'ID', 'EQUALS', 'ID', 'CLOSE']); result = tokenize("{{foo omg bar=baz bat=\"bam\"}}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']); - result[2].should.be_token("ID", "omg"); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']); + shouldBeToken(result[2], "ID", "omg"); }); it('tokenizes special @ identifiers', function() { var result = tokenize("{{ @foo }}"); - result.should.match_tokens(['OPEN', 'DATA', 'ID', 'CLOSE']); - result[2].should.be_token("ID", "foo"); + shouldMatchTokens(result, ['OPEN', 'DATA', 'ID', 'CLOSE']); + shouldBeToken(result[2], "ID", "foo"); result = tokenize("{{ foo @bar }}"); - result.should.match_tokens(['OPEN', 'ID', 'DATA', 'ID', 'CLOSE']); - result[3].should.be_token("ID", "bar"); + shouldMatchTokens(result, ['OPEN', 'ID', 'DATA', 'ID', 'CLOSE']); + shouldBeToken(result[3], "ID", "bar"); result = tokenize("{{ foo bar=@baz }}"); - result.should.match_tokens(['OPEN', 'ID', 'ID', 'EQUALS', 'DATA', 'ID', 'CLOSE']); - result[5].should.be_token("ID", "baz"); + shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'DATA', 'ID', 'CLOSE']); + shouldBeToken(result[5], "ID", "baz"); }); it('does not time out in a mustache with a single } followed by EOF', function() { - tokenize("{{foo}").should.match_tokens(['OPEN', 'ID']); + shouldMatchTokens(tokenize("{{foo}"), ['OPEN', 'ID']); }); it('does not time out in a mustache when invalid ID characters are used', function() { - tokenize("{{foo & }}").should.match_tokens(['OPEN', 'ID']); + shouldMatchTokens(tokenize("{{foo & }}"), ['OPEN', 'ID']); }); }); diff --git a/spec/utils.js b/spec/utils.js index 5eee69e06..390ad0533 100644 --- a/spec/utils.js +++ b/spec/utils.js @@ -1,11 +1,13 @@ -/*global shouldCompileTo */ +/*global Handlebars, shouldCompileTo */ describe('utils', function() { describe('#SafeString', function() { it("constructing a safestring from a string and checking its type", function() { var safe = new Handlebars.SafeString("testing 1, 2, 3"); - safe.should.be.instanceof(Handlebars.SafeString); - (safe == "testing 1, 2, 3").should.equal(true, "SafeString is equivalent to its underlying string"); + if (!(safe instanceof Handlebars.SafeString)) { + throw new Error('Must be instance of SafeString'); + } + equals(safe == 'testing 1, 2, 3', true, 'SafeString is equivalent to its underlying string'); }); it("it should not escape SafeString properties", function() { @@ -17,41 +19,41 @@ describe('utils', function() { describe('#escapeExpression', function() { it('shouhld escape html', function() { - Handlebars.Utils.escapeExpression('foo<&"\'>').should.equal('foo<&"'>'); + equals(Handlebars.Utils.escapeExpression('foo<&"\'>'), 'foo<&"'>'); }); it('should not escape SafeString', function() { var string = new Handlebars.SafeString('foo<&"\'>'); - Handlebars.Utils.escapeExpression(string).should.equal('foo<&"\'>'); + equals(Handlebars.Utils.escapeExpression(string), 'foo<&"\'>'); }); it('should handle falsy', function() { - Handlebars.Utils.escapeExpression('').should.equal(''); - Handlebars.Utils.escapeExpression(undefined).should.equal(''); - Handlebars.Utils.escapeExpression(null).should.equal(''); - Handlebars.Utils.escapeExpression(false).should.equal(''); + equals(Handlebars.Utils.escapeExpression(''), ''); + equals(Handlebars.Utils.escapeExpression(undefined), ''); + equals(Handlebars.Utils.escapeExpression(null), ''); + equals(Handlebars.Utils.escapeExpression(false), ''); - Handlebars.Utils.escapeExpression(0).should.equal('0'); + equals(Handlebars.Utils.escapeExpression(0), '0'); }); it('should handle empty objects', function() { - Handlebars.Utils.escapeExpression({}).should.equal({}.toString()); - Handlebars.Utils.escapeExpression([]).should.equal([].toString()); + equals(Handlebars.Utils.escapeExpression({}), {}.toString()); + equals(Handlebars.Utils.escapeExpression([]), [].toString()); }); }); describe('#isEmpty', function() { it('should not be empty', function() { - Handlebars.Utils.isEmpty(undefined).should.equal(true); - Handlebars.Utils.isEmpty(null).should.equal(true); - Handlebars.Utils.isEmpty(false).should.equal(true); - Handlebars.Utils.isEmpty('').should.equal(true); - Handlebars.Utils.isEmpty([]).should.equal(true); + equals(Handlebars.Utils.isEmpty(undefined), true); + equals(Handlebars.Utils.isEmpty(null), true); + equals(Handlebars.Utils.isEmpty(false), true); + equals(Handlebars.Utils.isEmpty(''), true); + equals(Handlebars.Utils.isEmpty([]), true); }); it('should be empty', function() { - Handlebars.Utils.isEmpty(0).should.equal(false); - Handlebars.Utils.isEmpty([1]).should.equal(false); - Handlebars.Utils.isEmpty('foo').should.equal(false); - Handlebars.Utils.isEmpty({bar: 1}).should.equal(false); + equals(Handlebars.Utils.isEmpty(0), false); + equals(Handlebars.Utils.isEmpty([1]), false); + equals(Handlebars.Utils.isEmpty('foo'), false); + equals(Handlebars.Utils.isEmpty({bar: 1}), false); }); }); }); From 96b42dedea9e7c610aaa6227f4366ef0e9e16f25 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 01:03:59 -0600 Subject: [PATCH 03/12] Fix tokenizer tests under IE --- spec/tokenizer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/tokenizer.js b/spec/tokenizer.js index b6719400b..373232229 100644 --- a/spec/tokenizer.js +++ b/spec/tokenizer.js @@ -1,12 +1,12 @@ function shouldMatchTokens(result, tokens) { - result.forEach(function(value, index) { - equals(value.name, tokens[index]); - }); -}; + for (var index = 0; index < result.length; index++) { + equals(result[index].name, tokens[index]); + } +} function shouldBeToken(result, name, text) { equals(result.name, name); equals(result.text, text); -}; +} describe('Tokenizer', function() { if (!Handlebars.Parser) { From 9ad8b86ece75192fd92d4e0242b786965a392594 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 01:32:01 -0600 Subject: [PATCH 04/12] Concat tests together for inbrowser exec --- .npmignore | 1 + Gruntfile.js | 11 ++++++++++- package.json | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index eeb1ceb5e..366b45408 100644 --- a/.npmignore +++ b/.npmignore @@ -18,5 +18,6 @@ dist/components/* spec/* src/* tasks/* +tmp/* publish/* vendor/* diff --git a/Gruntfile.js b/Gruntfile.js index 27e35c1b8..db7a0d3ae 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -14,7 +14,7 @@ module.exports = function(grunt) { ] }, - clean: ["dist", "lib/handlebars/compiler/parser.js"], + clean: ['tmp', 'dist', 'lib/handlebars/compiler/parser.js'], copy: { dist: { @@ -110,6 +110,13 @@ module.exports = function(grunt) { } }] } + }, + + concat: { + tests: { + src: ['spec/!(require).js'], + dest: 'tmp/tests.js' + } } }); @@ -124,11 +131,13 @@ module.exports = function(grunt) { this.registerTask('amd', ['packager:amd', 'requirejs']); this.registerTask('node', ['packager:cjs']); this.registerTask('globals', ['packager:global']); + this.registerTask('tests', ['concat:tests']); this.registerTask('release', 'Build final packages', ['amd', 'jshint', 'uglify', 'copy:dist', 'copy:components', 'copy:cdnjs']); // Load tasks from npm grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-requirejs'); grunt.loadNpmTasks('grunt-contrib-jshint'); diff --git a/package.json b/package.json index 99a62bd7a..1209532d3 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "grunt": "~0.4.1", "grunt-cli": "~0.1.10", "grunt-contrib-clean": "~0.4.1", + "grunt-contrib-concat": "~0.3.0", "grunt-contrib-copy": "~0.4.1", "grunt-contrib-jshint": "~0.7.2", "grunt-contrib-requirejs": "~0.4.1", From fe7bc0d4bdf9f4786726404e46f37ea5507a775c Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 02:17:32 -0600 Subject: [PATCH 05/12] Add sauce labs testing support --- .gitignore | 1 + .travis.yml | 16 +++++----------- Gruntfile.js | 36 ++++++++++++++++++++++++++++++++++++ package.json | 2 ++ spec/index.html | 42 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 spec/index.html diff --git a/.gitignore b/.gitignore index fc7d79ae6..0e32d042b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ node_modules *.sublime-project *.sublime-workspace npm-debug.log +sauce_connect.log* diff --git a/.travis.yml b/.travis.yml index 1e4e9651f..0182285cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ before_install: - npm install -g grunt-cli script: - - grunt --stack default metrics publish:latest + - grunt --stack default sauce metrics publish:latest email: on_failure: change @@ -15,13 +15,7 @@ email: env: global: - S3_BUCKET_NAME=builds.handlebarsjs.com - - secure: ! 'PJaukuvkBBsSDOqbIcNSSMgb96VVEaIt/eq9GPjXPeFbSd3hXgwhwVE62Lrq - - tJO8BaUfX+PzpiQjEl4D5/KBmvlFZ057Hqmy0zmPOT5mDZfJe8Ja5zyvTMb+ - - KkCWN/tjAp8kawHojE04pn6jIpPdwXFnAYwPhaHbATFrmdt9fdg=' - - secure: ! 'mBcGL2tnmiRujJdV/4fxrVd8E8wn6AW9IQKVcMv8tvOc7i5dOzZ39rpBKLuT - - MRXDtMV1LyLiuKYb1pHj1IyeadEahcLYFfGygF4LG7Yzp4NWHtRzQ7Q8LXaJ - - V7dXDboYCFkn2a8/Rtx1YSVh/sCONf5UoRC+MUIqrj4UiHN9r3s=' + - secure: PJaukuvkBBsSDOqbIcNSSMgb96VVEaIt/eq9GPjXPeFbSd3hXgwhwVE62LrqtJO8BaUfX+PzpiQjEl4D5/KBmvlFZ057Hqmy0zmPOT5mDZfJe8Ja5zyvTMb+KkCWN/tjAp8kawHojE04pn6jIpPdwXFnAYwPhaHbATFrmdt9fdg= + - secure: mBcGL2tnmiRujJdV/4fxrVd8E8wn6AW9IQKVcMv8tvOc7i5dOzZ39rpBKLuTMRXDtMV1LyLiuKYb1pHj1IyeadEahcLYFfGygF4LG7Yzp4NWHtRzQ7Q8LXaJV7dXDboYCFkn2a8/Rtx1YSVh/sCONf5UoRC+MUIqrj4UiHN9r3s= + - secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw= + - secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY= diff --git a/Gruntfile.js b/Gruntfile.js index db7a0d3ae..85ae34880 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -117,6 +117,39 @@ module.exports = function(grunt) { src: ['spec/!(require).js'], dest: 'tmp/tests.js' } + }, + + connect: { + server: { + options: { + base: '.', + hostname: '*', + port: 9999 + } + } + }, + 'saucelabs-mocha': { + all: { + options: { + build: process.env.TRAVIS_JOB_ID, + urls: ['http://localhost:9999/spec/'], + detailedError: true, + concurrency: 2, + browsers: [ + {browserName: 'chrome'}, + {browserName: 'firefox'}, + {browserName: 'firefox', version: '3.6'}, + {browserName: 'safari', version: 7, platform: 'OS X 10.9'}, + {browserName: 'safari', version: 6, platform: 'OS X 10.8'}, + {browserName: 'opera'}, + {browserName: 'internet explorer', version: 11, platform: 'Windows 8.1'}, + {browserName: 'internet explorer', version: 10, platform: 'Windows 8'}, + {browserName: 'internet explorer', version: 9, platform: 'Windows 7'}, + {browserName: 'internet explorer', version: 8, platform: 'XP'}, + {browserName: 'internet explorer', version: 7, platform: 'XP'} + ] + } + } } }); @@ -138,15 +171,18 @@ module.exports = function(grunt) { // Load tasks from npm grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-requirejs'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-saucelabs'); grunt.loadNpmTasks('es6-module-packager'); grunt.task.loadTasks('tasks'); grunt.registerTask('bench', ['metrics']); + grunt.registerTask('sauce', ['tests', 'connect', 'saucelabs-mocha']); grunt.registerTask('default', ['build', 'test', 'release']); }; diff --git a/package.json b/package.json index 1209532d3..6a1a730b2 100644 --- a/package.json +++ b/package.json @@ -36,10 +36,12 @@ "grunt-cli": "~0.1.10", "grunt-contrib-clean": "~0.4.1", "grunt-contrib-concat": "~0.3.0", + "grunt-contrib-connect": "~0.5.0", "grunt-contrib-copy": "~0.4.1", "grunt-contrib-jshint": "~0.7.2", "grunt-contrib-requirejs": "~0.4.1", "grunt-contrib-uglify": "~0.2.2", + "grunt-saucelabs": "~4.1.2", "es6-module-packager": "0.x", "jison": "~0.3.0", "keen.io": "0.0.3", diff --git a/spec/index.html b/spec/index.html new file mode 100644 index 000000000..6b4bcb13f --- /dev/null +++ b/spec/index.html @@ -0,0 +1,42 @@ + + + Mocha + + + + + + + + + + + + + +
+ + From e2a81fdd60e7698afc115ea1d21b605c0bcf6919 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 02:46:35 -0600 Subject: [PATCH 06/12] Add selenium badge --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 5937e4519..555024e89 100644 --- a/README.markdown +++ b/README.markdown @@ -1,5 +1,5 @@ -[![Build Status](https://travis-ci.org/wycats/handlebars.js.png?branch=master)](https://travis-ci.org/wycats/handlebars.js) - +[![Travis Build Status](https://travis-ci.org/wycats/handlebars.js.png?branch=master)](https://travis-ci.org/wycats/handlebars.js) +[![Selenium Test Status](https://saucelabs.com/buildstatus/handlebars)](https://saucelabs.com/u/handlebars) Handlebars.js ============= From ad3727b3fc21d03855e2f1be366d90b5d0017916 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 02:52:46 -0600 Subject: [PATCH 07/12] (Attempt to) Limit sauce tests to one travis build --- .travis.yml | 9 +++++++-- Gruntfile.js | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0182285cf..0677efbff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,5 +17,10 @@ env: - S3_BUCKET_NAME=builds.handlebarsjs.com - secure: PJaukuvkBBsSDOqbIcNSSMgb96VVEaIt/eq9GPjXPeFbSd3hXgwhwVE62LrqtJO8BaUfX+PzpiQjEl4D5/KBmvlFZ057Hqmy0zmPOT5mDZfJe8Ja5zyvTMb+KkCWN/tjAp8kawHojE04pn6jIpPdwXFnAYwPhaHbATFrmdt9fdg= - secure: mBcGL2tnmiRujJdV/4fxrVd8E8wn6AW9IQKVcMv8tvOc7i5dOzZ39rpBKLuTMRXDtMV1LyLiuKYb1pHj1IyeadEahcLYFfGygF4LG7Yzp4NWHtRzQ7Q8LXaJV7dXDboYCFkn2a8/Rtx1YSVh/sCONf5UoRC+MUIqrj4UiHN9r3s= - - secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw= - - secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY= + +matrix: + include: + - node_js: "0.10" + env: + - secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw= + - secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY= diff --git a/Gruntfile.js b/Gruntfile.js index 85ae34880..b01390f84 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -182,7 +182,7 @@ module.exports = function(grunt) { grunt.task.loadTasks('tasks'); grunt.registerTask('bench', ['metrics']); - grunt.registerTask('sauce', ['tests', 'connect', 'saucelabs-mocha']); + grunt.registerTask('sauce', process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : []); grunt.registerTask('default', ['build', 'test', 'release']); }; From 512daa31895b6acfbe21c0327ed444589b8fe191 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 02:54:48 -0600 Subject: [PATCH 08/12] Create matrix target for 0.10 only --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0677efbff..8eff8fe7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: node_js node_js: - "0.8" - - "0.10" before_install: - npm install -g grunt-cli @@ -24,3 +23,4 @@ matrix: env: - secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw= - secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY= + From efcedf2f24912b11965b4433f00c442c2bc56af2 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 03:12:03 -0600 Subject: [PATCH 09/12] Optimize travis build with publish option --- .travis.yml | 3 ++- Gruntfile.js | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8eff8fe7c..e98688e2c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ before_install: - npm install -g grunt-cli script: - - grunt --stack default sauce metrics publish:latest + - grunt --stack travis email: on_failure: change @@ -21,6 +21,7 @@ matrix: include: - node_js: "0.10" env: + - PUBLISH=true - secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw= - secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY= diff --git a/Gruntfile.js b/Gruntfile.js index b01390f84..be9022076 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -184,5 +184,7 @@ module.exports = function(grunt) { grunt.registerTask('bench', ['metrics']); grunt.registerTask('sauce', process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : []); + grunt.registerTask('travis', process.env.PUBLISH ? ['default', 'sauce', 'metrics', 'publish:latest'] : ['default']); + grunt.registerTask('default', ['build', 'test', 'release']); }; From 5b8b54b77470e53fd0dd1bb7c763d0b2f4a2ab7d Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 03:20:13 -0600 Subject: [PATCH 10/12] Add travis build caching --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index e98688e2c..902b7412c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,3 +25,6 @@ matrix: - secure: pLTzghtVll9yGKJI0AaB0uI8GypfWxLTaIB0ZL8//yN3nAEIKMhf/RRilYTsn/rKj2NUa7vt2edYILi3lttOUlCBOwTc9amiRms1W8Lwr/3IdWPeBLvLuH1zNJRm2lBAwU4LBSqaOwhGaxOQr6KHTnWudhNhgOucxpZfvfI/dFw= - secure: yERYCf7AwL11D9uMtacly/THGV8BlzsMmrt+iQVvGA3GaY6QMmfYqf6P6cCH98sH5etd1Y+1e6YrPeMjqI6lyRllT7FptoyOdHulazQe86VQN4sc0EpqMlH088kB7gGjTut9Z+X9ViooT5XEh9WA5jXEI9pXhQJNoIHkWPuwGuY= +cache: + directories: + - node_modules From 015eba6687796683119456179b584ddb2af0aab2 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 23 Dec 2013 11:57:11 -0600 Subject: [PATCH 11/12] Hide success messages when running in sauce This should help debug the firefox issues that only occur when executing against sauce. --- Gruntfile.js | 2 +- spec/index.html | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index be9022076..fadc36a68 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -132,7 +132,7 @@ module.exports = function(grunt) { all: { options: { build: process.env.TRAVIS_JOB_ID, - urls: ['http://localhost:9999/spec/'], + urls: ['http://localhost:9999/spec/?headless=true'], detailedError: true, concurrency: 2, browsers: [ diff --git a/spec/index.html b/spec/index.html index 6b4bcb13f..15e2321e5 100644 --- a/spec/index.html +++ b/spec/index.html @@ -3,7 +3,21 @@ Mocha + + + +