diff --git a/src/scope.js b/src/scope.js index 960f1029f..cf1a404c1 100644 --- a/src/scope.js +++ b/src/scope.js @@ -1,7 +1,6 @@ const _ = require('./util/underscore.js'); const lexical = require('./lexical.js'); const assert = require('./util/assert.js'); -const referenceError = /undefined variable|Cannot read property .* of undefined/; var Scope = { getAll: function() { @@ -16,13 +15,23 @@ var Scope = { try { return this.getPropertyByPath(this.scopes[i], str); } catch (e) { - if (!referenceError.test(e.message) || this.opts.strict_variables) { + if (/undefined variable/.test(e.message)) { + continue; + } + if (/Cannot read property/.test(e.message)) { + if (this.opts.strict_variables) { + e.message += ': ' + str; + throw e; + } else { + continue; + } + } else { e.message += ': ' + str; throw e; } } } - if(this.opts.strict_variables){ + if (this.opts.strict_variables) { throw new TypeError('undefined variable: ' + str); } }, diff --git a/src/util/error.js b/src/util/error.js index 1ed8ebe26..dcb02eeb5 100644 --- a/src/util/error.js +++ b/src/util/error.js @@ -14,7 +14,11 @@ TokenizationError.prototype.constructor = TokenizationError; function ParseError(message, input, line, e) { if(Error.captureStackTrace){ Error.captureStackTrace(this, this.constructor); + } else{ + this.stack = ""; } + this.stack += (this.stack ? "\nFrom " : "From ") + e.stack; + this.name = this.constructor.name; this.originalError = e; diff --git a/test/scope.js b/test/scope.js index 430abc69f..0db65bcf2 100644 --- a/test/scope.js +++ b/test/scope.js @@ -71,21 +71,6 @@ describe('scope', function() { }).to.throw(/unbalanced '/); }); - it('should throw undefined in strict mode', function() { - scope = Scope.factory(ctx, { - strict_variables: true - }); - - function fn() { - scope.get('notdefined'); - } - expect(fn).to.throw(/undefined variable: notdefined/); - }); - - it('should get all properties when arguments empty', function() { - expect(scope.getAll()).deep.equal(ctx); - }); - it('should access child property via dot syntax', function() { expect(scope.get('bar.zoo')).to.equal('coo'); expect(scope.get('bar.arr')).to.deep.equal(['a', 'b']); @@ -116,6 +101,34 @@ describe('scope', function() { }); }); + describe('strict_variables', function() { + var scope; + beforeEach(function(){ + scope = Scope.factory(ctx, { + strict_variables: true + }); + }); + it('should throw undefined in strict mode', function() { + function fn() { + scope.get('notdefined'); + } + expect(fn).to.throw(/undefined variable: notdefined/); + }); + it('should find variable in parent scope', function() { + scope.set('foo', 'foo'); + scope.push({ + 'bar': 'bar' + }); + expect(scope.get('foo')).to.equal('foo'); + }); + }); + + describe('.getAll()', function() { + it('should get all properties when arguments empty', function() { + expect(scope.getAll()).deep.equal(ctx); + }); + }); + describe('.push()', function() { it('should throw when trying to push non-object', function() { expect(function() { diff --git a/test/tags/include.js b/test/tags/include.js index 58a5e48bc..5dcb963a6 100644 --- a/test/tags/include.js +++ b/test/tags/include.js @@ -29,8 +29,10 @@ describe('tags/include', function() { mock({ '/illegal.html': '{%include%}', }); - return expect(liquid.renderFile('/illegal.html')).to. - be.rejectedWith(ParseError, /illegal token {%include%}/); + return liquid.renderFile('/illegal.html').catch(function(e){ + expect(e.name).to.equal('ParseError'); + expect(e.message).to.match(/illegal token {%include%}/); + }); }); it('should support include with relative path', function() { diff --git a/test/util/error.js b/test/util/error.js index 293b330f2..b643dc876 100644 --- a/test/util/error.js +++ b/test/util/error.js @@ -66,7 +66,8 @@ describe('error', function() { return engine.parseAndRender(src).catch(function(err) { expect(err.name).to.equal('ParseError'); expect(err.input).to.equal('{% -a %}'); - expect(err.line).to.equal(3); + expect(err.line).to.equal(3) + expect(err.stack).to.contain('From AssertionError: tag -a not found'); }); }); it('should throw correct error info for files', function() {