diff --git a/README.md b/README.md index 2eee7ff53..61fb0c061 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,8 @@ Documentation: - [x] default [Document](https://shopify.github.io/liquid/filters/default) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] - [x] divided_by [Document](https://shopify.github.io/liquid/filters/divided_by) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] - [x] downcase [Document](https://shopify.github.io/liquid/filters/downcase) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] -- [ ] escape [Document](https://shopify.github.io/liquid/filters/escape) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] -- [ ] escape_once [Document](https://shopify.github.io/liquid/filters/escape_once) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] +- [x] escape [Document](https://shopify.github.io/liquid/filters/escape) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] +- [x] escape_once [Document](https://shopify.github.io/liquid/filters/escape_once) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] - [ ] first [Document](https://shopify.github.io/liquid/filters/first) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] - [ ] floor [Document](https://shopify.github.io/liquid/filters/floor) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] - [ ] join [Document](https://shopify.github.io/liquid/filters/join) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft] diff --git a/filters.js b/filters.js index 361dfbb7e..0b383490e 100644 --- a/filters.js +++ b/filters.js @@ -10,4 +10,6 @@ module.exports = function(liquid) { liquid.registerFilter('default', (v, arg) => arg || v); liquid.registerFilter('divided_by', (v, arg) => Math.floor(v/arg)); liquid.registerFilter('downcase', v => v.toLowerCase()); + liquid.registerFilter('escape', v => _.escape(v)); + liquid.registerFilter('escape_once', v => _.escape(_.unescape(v))); }; diff --git a/scope.js b/scope.js index 0d2ce73ea..49f17155d 100644 --- a/scope.js +++ b/scope.js @@ -1,5 +1,6 @@ const _ = require('lodash'); const lexical = require('./lexical.js'); +const error = require('./error.js'); var scope = { get: function(str) { @@ -23,6 +24,7 @@ var scope = { return this; }, push: function(ctx) { + if(!ctx) error(`trying to push ${ctx} into scopes`); return this.scopes.push(ctx); }, pop: function() { @@ -32,6 +34,6 @@ var scope = { exports.factory = function(_ctx) { var ctx = Object.create(scope); - ctx.scopes = [_ctx]; + ctx.scopes = _ctx ? [_ctx] : []; return ctx; }; diff --git a/test/filters.js b/test/filters.js index 7bdf9d901..1c1b183f9 100644 --- a/test/filters.js +++ b/test/filters.js @@ -1,53 +1,66 @@ const chai = require("chai"); const expect = chai.expect; -var liquid = require('..')(); +var liquid = require('..')(), ctx; -describe('filters', function() { - var ctx = { +function test(src, dst) { + ctx = { + date: new Date(), foo: 'bar', arr: [-2, 'a'] }; + expect(liquid.render(src, ctx)).to.equal(dst); +} +describe('filters', function() { it('should support abs', function() { - expect(liquid.render('{{ -3 | abs }}')).to.equal('3'); - expect(liquid.render('{{ arr[0] | abs }}', ctx)).to.equal('2'); + test('{{ -3 | abs }}', '3'); + test('{{ arr[0] | abs }}', '2'); }); it('should support append', function() { - expect(liquid.render('{{ -3 | append: "abc" }}')).to.equal('-3abc'); - expect(liquid.render('{{ "a" | append: foo }}', ctx)).to.equal('abar'); + test('{{ -3 | append: "abc" }}', '-3abc'); + test('{{ "a" | append: foo }}', 'abar'); }); it('should support capitalize', function() { - expect(liquid.render('{{ "i am good" | capitalize }}')).to.equal('I am good'); + test('{{ "i am good" | capitalize }}', 'I am good'); }); it('should support ceil', function() { - expect(liquid.render('{{ 1.2 | ceil }}')).to.equal('2'); - expect(liquid.render('{{ 2.0 | ceil }}')).to.equal('2'); - expect(liquid.render('{{ "3.5" | ceil }}')).to.equal('4'); - expect(liquid.render('{{ 183.357 | ceil }}')).to.equal('184'); + test('{{ 1.2 | ceil }}', '2'); + test('{{ 2.0 | ceil }}', '2'); + test('{{ "3.5" | ceil }}', '4'); + test('{{ 183.357 | ceil }}', '184'); }); it('should support date', function() { - var d = ctx.date = new Date(); - str = d.toDateString(); - expect(liquid.render('{{ date | date:"%a %b %d %Y"}}', ctx)).to.equal(str); + str = ctx.date.toDateString(); + test('{{ date | date:"%a %b %d %Y"}}', str); }); it('should support default', function() { - expect(liquid.render('{{false |default: "a"}}')).to.equal('a'); + test('{{false |default: "a"}}', 'a'); }); it('should support divided_by', function() { - expect(liquid.render('{{4 | divided_by: 2}}')).to.equal('2'); - expect(liquid.render('{{16 | divided_by: 4}}')).to.equal('4'); - expect(liquid.render('{{5 | divided_by: 3}}')).to.equal('1'); + test('{{4 | divided_by: 2}}', '2'); + test('{{16 | divided_by: 4}}', '4'); + test('{{5 | divided_by: 3}}', '1'); }); it('should support downcase', function() { - expect(liquid.render('{{ "Parker Moore" | downcase }}')).to.equal('parker moore'); - expect(liquid.render('{{ "apple" | downcase }}')).to.equal('apple'); + test('{{ "Parker Moore" | downcase }}', 'parker moore'); + test('{{ "apple" | downcase }}', 'apple'); + }); + it('should support escape', function() { + test('{{ "Have you read \'James & the Giant Peach\'?" | escape }}', + 'Have you read 'James & the Giant Peach'?'); + test('{{ "Tetsuro Takara" | escape }}', 'Tetsuro Takara'); + }); + it('should support escape_once', function() { + test('{{ "1 < 2 & 3" | escape_once }}', '1 < 2 & 3'); + test('{{ "1 < 2 & 3" | escape_once }}', '1 < 2 & 3'); }); }); + diff --git a/test/tags.js b/test/tags.js index 111cd8e8d..39e86b5ff 100644 --- a/test/tags.js +++ b/test/tags.js @@ -4,6 +4,16 @@ const expect = chai.expect; var liquid = require('..')(), ctx; +function test(src, dst) { + expect(liquid.render(src, ctx)).to.equal(dst); +} + +function testThrow (src, pattern) { + expect(function() { + liquid.render(src, ctx); + }).to.throw(pattern); +} + describe('tags', function() { beforeEach(function() { ctx = { @@ -16,51 +26,47 @@ describe('tags', function() { }; }); it('should support assign', function() { - expect(liquid.render('{% assign foo="bar"%}{{foo}}', ctx)).to.equal('bar'); + test('{% assign foo="bar"%}{{foo}}', 'bar'); }); it('should support case', function() { - expect(function() { - liquid.render('{% case "foo"%}'); - }).to.throw(/case "foo" not closed/); - expect(liquid.render('{% case "foo"%}' + + testThrow('{% case "foo"%}', /case "foo" not closed/); + test('{% case "foo"%}' + '{% when "foo" %}foo{% when "bar"%}bar' + - '{%endcase%}', ctx)).to.equal('foo'); - expect(liquid.render('{% case empty %}' + + '{%endcase%}', 'foo'); + test('{% case empty %}' + '{% when "foo" %}foo{% when ""%}bar' + - '{%endcase%}')).to.equal('bar'); - expect(liquid.render('{% case false %}' + + '{%endcase%}', 'bar'); + test('{% case false %}' + '{% when "foo" %}foo{% when ""%}bar' + - '{%endcase%}')).to.equal(''); - expect(liquid.render('{% case "a" %}' + + '{%endcase%}', ''); + test('{% case "a" %}' + '{% when "b" %}b{% when "c"%}c{%else %}d' + - '{%endcase%}')).to.equal('d'); + '{%endcase%}', 'd'); }); it('should support if', function() { - expect(liquid.render('{% if 2==3 %}yes{%else%}no{%endif%}', ctx)).to.equal('no'); - expect(liquid.render('{% if 1==2 and one2 %}yes{%endunless%}', ctx)).to.equal('yes'); + test('{% unless 1 %}yes{%else%}no{%endunless%}', 'no'); + test('{% unless 1>2 %}yes{%endunless%}', 'yes'); }); it('should support capture', function() { - expect(liquid.render('{% capture f %}{{"a" | capitalize}}{%endcapture%}{{f}}', ctx)).to.equal('A'); - expect(function() { - liquid.render('{% capture = %}{%endcapture%}', ctx); - }).to.throw(/= not valid identifier/); + test('{% capture f %}{{"a" | capitalize}}{%endcapture%}{{f}}', 'A'); + testThrow('{% capture = %}{%endcapture%}', /= not valid identifier/); }); it('should support increment', function() { - expect(liquid.render('{% increment foo %}{%increment foo%}{{foo}}', ctx)).to.equal('2'); - expect(liquid.render('{% increment one %}{{one}}', ctx)).to.equal('2'); + test('{% increment foo %}{%increment foo%}{{foo}}', '2'); + test('{% increment one %}{{one}}', '2'); }); it('should support decrement', function() { - expect(liquid.render('{% decrement foo %}{%decrement foo%}{{foo}}', ctx)).to.equal('-2'); - expect(liquid.render('{% decrement one %}{{one}}', ctx)).to.equal('0'); + test('{% decrement foo %}{%decrement foo%}{{foo}}', '-2'); + test('{% decrement one %}{{one}}', '0'); }); });