chore(TypeScript): refactor objects into classes

fix: `Nil`(null, undefined) now renders as empty string
change: `parser.parseValue()` renamed to `parser.parseOutput`
change: registered tags/filters become static and shared across different liquid instances
This commit is contained in:
harttle
2019-02-17 04:55:30 +08:00
parent b51b0dabca
commit 677e8511e6
134 changed files with 3327 additions and 3858 deletions
-11
View File
@@ -1,11 +0,0 @@
{
"rules": {
"no-unused-expressions": "off"
},
"env": {
"mocha": true
},
"plugins": [
"mocha"
]
}
+14
View File
@@ -0,0 +1,14 @@
var chai = require('chai')
var Liquid = require('../..')
var expect = chai.expect
chai.use(require('chai-as-promised'))
describe('.evalValue()', function () {
var engine
beforeEach(() => engine = new Liquid())
it('should throw when scope undefined', function () {
expect(() => engine.evalValue('{{"foo"}}')).to.throw(/scope undefined/)
})
})
+8 -8
View File
@@ -1,14 +1,14 @@
const chai = require('chai')
const request = require('supertest')
const express = require('express')
const mock = require('mock-fs')
const Liquid = require('../../dist/liquid.common.js')
const chaiAsPromised = require('chai-as-promised')
import * as chai from 'chai'
import * as request from 'supertest'
import * as express from 'express'
import * as mock from 'mock-fs'
import Liquid from '../../dist/liquid.common.js'
import * as chaiAsPromised from 'chai-as-promised'
const expect = chai.expect
chai.use(chaiAsPromised)
describe('engine#express()', function () {
describe('express()', function () {
var app, engine
beforeEach(function () {
@@ -84,7 +84,7 @@ describe('engine#express()', function () {
.expect('bar')
.expect(200, done)
})
it('should respect express views (Undefined) when lookup', function (done) {
it('should respect express views (undefined) when lookup', function (done) {
const files = {}
files[process.cwd() + '/views/include.html'] = '{% include file %}'
files[process.cwd() + '/views/bar.html'] = 'bar'
@@ -12,15 +12,15 @@ describe('.parseAndRender()', function () {
strict_filters: true
})
})
it('should value object', function () {
it('should stringify object', function () {
var ctx = { obj: { foo: 'bar' } }
return expect(engine.parseAndRender('{{obj}}', ctx)).to.eventually.equal('{"foo":"bar"}')
})
it('should value array', function () {
it('should stringify array ', function () {
var ctx = { arr: [-2, 'a'] }
return expect(engine.parseAndRender('{{arr}}', ctx)).to.eventually.equal('[-2,"a"]')
})
it('should value undefined to empty', function () {
it('should render undefined as empty', function () {
return expect(engine.parseAndRender('foo{{zzz}}bar', {})).to.eventually.equal('foobar')
})
it('should render as null when filter undefined', function () {
@@ -1,8 +1,10 @@
const chai = require('chai')
const Liquid = require('../..')
import * as chai from 'chai'
import Liquid from '../..'
import * as chaiAsPromised from 'chai-as-promised'
const liquid = new Liquid()
const expect = chai.expect
chai.use(require('chai-as-promised'))
chai.use(chaiAsPromised)
const cases = [
{
+19 -17
View File
@@ -1,28 +1,30 @@
var Liquid = require('../../dist/liquid.js')
var sinon = require('sinon')
var chai = require('chai')
var expect = chai.expect
chai.use(require('chai-as-promised'))
import Liquid from '../../dist/liquid.js'
import { createFakeServer, useFakeXMLHttpRequest } from 'sinon'
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import { JSDOM } from 'jsdom'
const expect = chai.expect
chai.use(chaiAsPromised)
describe('xhr', () => {
if (process.version.match(/^v(\d+)/)[1] < 8) {
if (+process.version.match(/^v(\d+)/)[1] < 8) {
console.info('jsdom not supported, skipping xhr...')
return
}
var JSDOM = require('jsdom').JSDOM
var server, engine
let server, engine
beforeEach(() => {
server = sinon.createFakeServer()
server = createFakeServer()
server.autoRespond = true
server.respondWith('GET', 'https://example.com/views/hello.html',
[200, { 'Content-Type': 'text/plain' }, 'hello {{name}}'])
var dom = new JSDOM('', {
let dom = new JSDOM('', {
url: 'https://example.com/foo/bar.html',
contentType: 'text/html',
includeNodeLocations: true
})
global.XMLHttpRequest = sinon.useFakeXMLHttpRequest()
global.document = dom.window.document
});
(global as any).XMLHttpRequest = useFakeXMLHttpRequest();
(global as any).document = dom.window.document;
engine = new Liquid({
root: 'https://example.com/views/',
extname: '.html'
@@ -30,8 +32,8 @@ describe('xhr', () => {
})
afterEach(() => {
server.restore()
delete global.XMLHttpRequest
delete global.document
delete (global as any).XMLHttpRequest
delete (global as any).document
})
describe('#renderFile()', () => {
it('should support without extname', () => {
@@ -70,8 +72,8 @@ describe('xhr', () => {
.catch(function (e) {
expect(e.message).to.equal('An error occurred whilst receiving the response.')
done()
})
global.XMLHttpRequest.onCreate = function (request) {
});
(global as any).XMLHttpRequest.onCreate = function (request) {
setTimeout(() => request.error())
}
})
-2
View File
@@ -1,2 +0,0 @@
--require ts-node/register
--recursive
+27 -28
View File
@@ -1,96 +1,95 @@
import * as chai from 'chai'
import * as sinon from 'sinon'
import * as sinonChai from 'sinon-chai'
import Filter from '../../src/filter'
import { factory as scopeFactory } from '../../src/scope'
import Filter from 'src/template/filter'
import Scope from 'src/scope/scope'
chai.use(sinonChai)
const expect = chai.expect
const filter = Filter()
describe('filter', function () {
let scope
beforeEach(function () {
filter.clear()
scope = scopeFactory()
Filter.clear()
scope = new Scope()
})
it('should return default filter when not registered', function () {
const result = filter.construct('foo')
const result = new Filter('foo')
expect(result.name).to.equal('foo')
})
it('should throw when filter name illegal', function () {
expect(function () {
filter.construct('/')
new Filter('/')
}).to.throw(/illegal filter/)
})
it('should parse argument syntax', function () {
filter.register('foo', x => x)
const f = filter.construct('foo: a, "b"')
Filter.register('foo', x => x)
const f = new Filter('foo: a, "b"')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal(['a', '"b"'])
})
it('should register a simple filter', function () {
filter.register('upcase', x => x.toUpperCase())
expect(filter.construct('upcase').render('foo', scope)).to.equal('FOO')
Filter.register('upcase', x => x.toUpperCase())
expect(new Filter('upcase').render('foo', scope)).to.equal('FOO')
})
it('should register a argumented filter', function () {
filter.register('add', (a, b) => a + b)
expect(filter.construct('add: 2').render(3, scope)).to.equal(5)
Filter.register('add', (a, b) => a + b)
expect(new Filter('add: 2').render(3, scope)).to.equal(5)
})
it('should register a multi-argumented filter', function () {
filter.register('add', (a, b, c) => a + b + c)
expect(filter.construct('add: 2, "c"').render(3, scope)).to.equal('5c')
Filter.register('add', (a, b, c) => a + b + c)
expect(new Filter('add: 2, "c"').render(3, scope)).to.equal('5c')
})
it('should call filter with corrct arguments', function () {
const spy = sinon.spy()
filter.register('foo', spy)
filter.construct('foo: 33').render('foo', scope)
Filter.register('foo', spy)
new Filter('foo: 33').render('foo', scope)
expect(spy).to.have.been.calledWith('foo', 33)
})
it('should support arguments as named key/values', function () {
filter.register('foo', x => x)
const f = filter.construct('foo: key1: "literal1", key2: value2')
Filter.register('foo', x => x)
const f = new Filter('foo: key1: "literal1", key2: value2')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal([ '\'key1\'', '"literal1"', '\'key2\'', 'value2' ])
})
it('should support arguments as named key/values with inline literals', function () {
filter.register('foo', x => x)
const f = filter.construct('foo: "test0", key1: "literal1", key2: value2')
Filter.register('foo', x => x)
const f = new Filter('foo: "test0", key1: "literal1", key2: value2')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal([ '"test0"', '\'key1\'', '"literal1"', '\'key2\'', 'value2' ])
})
it('should support arguments as named key/values with inline values', function () {
filter.register('foo', x => x)
const f = filter.construct('foo: test0, key1: "literal1", key2: value2')
Filter.register('foo', x => x)
const f = new Filter('foo: test0, key1: "literal1", key2: value2')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal([ 'test0', '\'key1\'', '"literal1"', '\'key2\'', 'value2' ])
})
it('should support argument values named same as keys', function () {
filter.register('foo', x => x)
const f = filter.construct('foo: a: a')
Filter.register('foo', x => x)
const f = new Filter('foo: a: a')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal(['\'a\'', 'a'])
})
it('should support argument literals named same as keys', function () {
filter.register('foo', x => x)
const f = filter.construct('foo: a: "a"')
Filter.register('foo', x => x)
const f = new Filter('foo: a: "a"')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal(['\'a\'', '"a"'])
})
it('should not throw undefined filter by default', function () {
expect(filter.construct('undefined').render('foo', scope)).to.equal('foo')
expect(new Filter('undefined').render('foo', scope)).to.equal('foo')
})
})
@@ -1,9 +1,8 @@
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import Liquid from '../../src/index'
import Liquid from '../../src/liquid'
chai.use(chaiAsPromised)
const liquid = new Liquid()
const expect = chai.expect
const ctx = {
@@ -20,12 +19,14 @@ const ctx = {
category: 'bar'
}]
}
let liquid
function test (src, dst) {
return expect(liquid.parseAndRender(src, ctx)).to.eventually.equal(dst)
}
describe('filters', function () {
before(() => liquid = new Liquid())
describe('abs', function () {
it('should return 3 for -3', () => test('{{ -3 | abs }}', '3'))
it('should return 2 for arr[0]', () => test('{{ arr[0] | abs }}', '2'))
@@ -416,8 +417,10 @@ describe('filters', function () {
})
describe('obj_test', function () {
liquid.registerFilter('obj_test', function () {
return Array.prototype.slice.call(arguments).join(',')
before(() => {
liquid.registerFilter('obj_test', function () {
return Array.prototype.slice.call(arguments).join(',')
})
})
it('should support object', () => test(`{{ "a" | obj_test: k1: "v1", k2: foo }}`, 'a,k1,v1,k2,bar'))
it('should support mixed object', () => test(`{{ "a" | obj_test: "something", k1: "v1", k2: foo }}`, 'a,something,k1,v1,k2,bar'))
@@ -1,7 +1,7 @@
const chai = require('chai')
const expect = chai.expect
import * as chai from 'chai'
const lexical = require('../../src/lexical.js')
const expect = chai.expect
const lexical = require('../../src/parser/lexical')
describe('lexical', function () {
it('should test filter syntax', function () {
+2 -4
View File
@@ -1,4 +1,4 @@
import Liquid from '../../src/index'
import Liquid from '../../src/liquid'
import * as mock from 'mock-fs'
import * as chai from 'chai'
@@ -7,9 +7,7 @@ const expect = chai.expect
describe('Liquid', function () {
describe('#constructor()', function () {
it('should throw on illegal root', function () {
expect(() => {
new Liquid({root: {}}) // eslint-disable-line
}).to.throw(/illegal root/)
expect(() => new (Liquid as any)({root: {}})).to.throw(/illegal root/)
})
})
describe('#plugin()', function () {
@@ -1,12 +1,12 @@
import * as chai from 'chai'
import * as mock from 'mock-fs'
import Liquid from '../../../src'
import Liquid from '../../../src/liquid'
import * as chaiAsPromised from 'chai-as-promised'
const expect = chai.expect
chai.use(chaiAsPromised)
describe('cache options', function () {
describe('LiquidOptions#cache', function () {
let engine
beforeEach(function () {
engine = new Liquid({
@@ -1,10 +1,10 @@
import Liquid from '../../../src'
import Liquid from '../../../src/liquid'
import * as chai from 'chai'
const expect = chai.expect
chai.use(require('chai-as-promised'))
describe('strict options', function () {
describe('LiquidOptions#strict_*', function () {
let engine
const ctx = {}
beforeEach(function () {
@@ -1,11 +1,11 @@
import * as chai from 'chai'
import Liquid from '../../../src'
import Liquid from '../../../src/liquid'
import * as chaiAsPromised from 'chai-as-promised'
const expect = chai.expect
chai.use(chaiAsPromised)
describe('trimming', function () {
describe('LiquidOptions#trimming', function () {
const ctx = { name: 'harttle' }
describe('tag trimming', function () {
+56
View File
@@ -0,0 +1,56 @@
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import * as sinonChai from 'sinon-chai'
import * as sinon from 'sinon'
import Scope from '../../src/scope/scope'
import Output from '../../src/template/output'
import Filter from 'src/template/filter'
chai.use(sinonChai)
chai.use(chaiAsPromised)
const expect = chai.expect
describe('Output', function () {
beforeEach(function () {
Filter.clear()
})
it('should respect to .to_liquid() method', function () {
const scope = new Scope({
bar: { to_liquid: x => 'custom' }
})
return expect(new Output({value: 'bar'}).render(scope)).to.eventually.equal('custom')
})
it('should stringify objects', function () {
const scope = new Scope({
foo: { obj: { arr: ['a', 2] } }
})
return expect(new Output({value: 'foo'}).render(scope)).to.eventually.equal('{"obj":{"arr":["a",2]}}')
})
it('should skip circular property', function () {
const ctx = { foo: { num: 2 }, bar: 'bar' } as any
ctx.foo.circular = ctx
const scope = new Scope(ctx)
return expect(new Output({value: 'foo'}).render(scope)).to.eventually.equal('{"num":2,"circular":{"bar":"bar"}}')
})
it('should skip function property', function () {
const scope = new Scope({ obj: { foo: 'foo', bar: x => x } })
return expect(new Output({value: 'obj'}).render(scope)).to.eventually.equal('{"foo":"foo"}')
})
it('should respect to .toString()', async () => {
const scope = new Scope({ obj: { toString: () => 'FOO' } })
const str = await new Output({value: 'obj'}).render(scope)
return expect(str).to.equal('FOO')
})
it('should respect to .to_s()', async () => {
const scope = new Scope({ obj: { to_s: () => 'FOO' } })
const str = await new Output({value: 'obj'}).render(scope)
return expect(str).to.equal('FOO')
})
it('should respect to .liquid_method_missing()', async () => {
const scope = new Scope({ obj: { liquid_method_missing: x => x.toUpperCase() } })
const str = await new Output({value: 'obj.foo'}).render(scope)
return expect(str).to.equal('FOO')
})
})
-49
View File
@@ -1,49 +0,0 @@
import * as chai from 'chai'
import * as sinonChai from 'sinon-chai'
import Filter from '../../src/filter'
import Tag from '../../src/tag'
import Template from '../../src/parser'
const expect = chai.expect
const filter = Filter()
const tag = Tag()
chai.use(sinonChai)
describe('template', function () {
let template
const add = (l, r) => l + r
beforeEach(function () {
filter.clear()
filter.register('add', add)
tag.clear()
template = Template(tag, filter)
})
it('should throw when value string illegal', function () {
expect(function () {
template.parseValue('/')
}).to.throw(/illegal value string/)
})
it('should parse value string', function () {
const tpl = template.parseValue('foo')
expect(tpl.type).to.equal('value')
expect(tpl.initial).to.equal('foo')
expect(tpl.filters).to.deep.equal([])
})
it('should parse value string with a simple filter', function () {
const tpl = template.parseValue('foo | add: 3, "foo"')
expect(tpl.initial).to.equal('foo')
expect(tpl.filters.length).to.equal(1)
expect(tpl.filters[0].filter).to.equal(add)
})
it('should parse value string with filters', function () {
const tpl = template.parseValue('foo | add: "|" | add')
expect(tpl.initial).to.equal('foo')
expect(tpl.filters.length).to.equal(2)
})
})
-120
View File
@@ -1,120 +0,0 @@
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import * as sinonChai from 'sinon-chai'
import * as sinon from 'sinon'
import Tag from '../../src/tag'
import Scope from '../../src/scope'
import Filter from '../../src/filter'
import Render from '../../src/render'
import parser from '../../src/parser'
chai.use(sinonChai)
chai.use(chaiAsPromised)
const expect = chai.expect
const tag = Tag()
const filter = Filter()
const Template = parser(tag, filter)
let render
describe('render', function () {
beforeEach(function () {
filter.clear()
tag.clear()
render = Render()
})
describe('.renderTemplates()', function () {
it('should throw when scope undefined', function () {
expect(render.renderTemplates([])).to.be.rejectedWith(/scope undefined/)
})
it('should render html', function () {
const scope = new Scope()
return expect(render.renderTemplates([{ type: 'html', value: '<p>' }], scope)).to.eventually.equal('<p>')
})
})
describe('.renderValue()', function () {
it('should respect to .to_liquid() method', function () {
const scope = new Scope({
bar: { to_liquid: x => 'custom' }
})
const tpl = Template.parseValue('bar')
return expect(render.renderValue(tpl, scope)).to.eventually.equal('custom')
})
it('should stringify objects', function () {
const scope = new Scope({
foo: { obj: { arr: ['a', 2] } }
})
const tpl = Template.parseValue('foo')
return expect(render.renderValue(tpl, scope)).to.eventually.equal('{"obj":{"arr":["a",2]}}')
})
it('should skip circular property', function () {
const ctx = { foo: { num: 2 }, bar: 'bar' }
ctx.foo.circular = ctx
const scope = new Scope(ctx)
const tpl = Template.parseValue('foo')
return expect(render.renderValue(tpl, scope)).to.eventually.equal('{"num":2,"circular":{"bar":"bar"}}')
})
it('should skip function property', function () {
const scope = new Scope({ obj: { foo: 'foo', bar: x => x } })
const tpl = Template.parseValue('obj')
return expect(render.renderValue(tpl, scope)).to.eventually.equal('{"foo":"foo"}')
})
it('should respect to .toString()', async () => {
const scope = new Scope({ obj: { toString: () => 'FOO' } })
const tpl = Template.parseValue('obj')
const str = await render.renderValue(tpl, scope)
return expect(str).to.equal('FOO')
})
it('should respect to .to_s()', async () => {
const scope = new Scope({ obj: { to_s: () => 'FOO' } })
const tpl = Template.parseValue('obj')
const str = await render.renderValue(tpl, scope)
return expect(str).to.equal('FOO')
})
it('should respect to .liquid_method_missing()', async () => {
const scope = new Scope({ obj: { liquid_method_missing: x => x.toUpperCase() } })
const tpl = Template.parseValue('obj.foo')
const str = await render.renderValue(tpl, scope)
return expect(str).to.equal('FOO')
})
})
describe('.evalValue()', function () {
it('should throw when scope undefined', function () {
expect(function () {
render.evalValue()
}).to.throw(/scope undefined/)
})
it('should eval value', function () {
filter.register('date', (l, r) => l + r)
filter.register('time', (l, r) => l + 3 * r)
const tpl = Template.parseValue('foo.bar[0] | date: "b" | time:2')
const scope = new Scope({
foo: { bar: ['a'] }
})
expect(render.evalValue(tpl, scope)).to.equal('ab6')
})
it('should reserve type', function () {
filter.register('arr', () => [1])
const tpl = Template.parseValue('"x" | arr')
expect(render.evalValue(tpl, new Scope())).to.deep.equal([1])
})
it('should eval filter with correct arguments', function () {
const date = sinon.stub().returns('y')
const time = sinon.spy()
filter.register('date', date)
filter.register('time', time)
const tpl = Template.parseValue('foo.bar | date: "b" | time:2')
const scope = new Scope({
foo: { bar: 'bar' }
})
render.evalValue(tpl, scope)
expect(date).to.have.been.calledWith('bar', 'b')
expect(time).to.have.been.calledWith('y', 2)
})
})
})
+38
View File
@@ -0,0 +1,38 @@
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import * as sinonChai from 'sinon-chai'
import * as sinon from 'sinon'
import Scope from '../../src/scope/scope'
import Token from 'src/parser/token'
import Tag from 'src/template/tag/tag'
import Filter from 'src/template/filter'
import Render from '../../src/render/render'
import Parser from '../../src/parser/parser'
import HTML from 'src/template/html'
chai.use(sinonChai)
chai.use(chaiAsPromised)
const expect = chai.expect
const parser = new Parser(null)
let render
describe('render', function () {
beforeEach(function () {
Filter.clear()
Tag.clear()
render = new Render()
})
describe('.renderTemplates()', function () {
it('should throw when scope undefined', function () {
expect(render.renderTemplates([])).to.be.rejectedWith(/scope undefined/)
})
it('should render html', function () {
const scope = new Scope()
const token = { type: 'html', value: '<p>' } as Token
return expect(render.renderTemplates([new HTML(token)], scope)).to.eventually.equal('<p>')
})
})
})
+1 -1
View File
@@ -1,5 +1,5 @@
import * as chai from 'chai'
import Scope from '../../src/scope'
import Scope from 'src/scope/scope'
const expect = chai.expect
+6 -6
View File
@@ -1,6 +1,6 @@
import Scope from '../../src/scope'
import Scope from 'src/scope/scope'
import { expect } from 'chai'
import { evalExp, evalValue, isTruthy } from '../../src/syntax'
import { evalExp, evalValue, isTruthy } from 'src/render/syntax'
describe('expression', function () {
let scope
@@ -19,8 +19,8 @@ describe('expression', function () {
describe('.evalValue()', function () {
it('should eval literals', function () {
expect(evalValue('2.3')).to.equal(2.3)
expect(evalValue('"foo"')).to.equal('foo')
expect(evalValue('2.3', scope)).to.equal(2.3)
expect(evalValue('"foo"', scope)).to.equal('foo')
})
it('should eval variables', function () {
@@ -31,7 +31,7 @@ describe('expression', function () {
})
it('should throw if not valid', function () {
const fn = () => evalValue('===')
const fn = () => evalValue('===', scope)
expect(fn).to.throw("cannot eval '===' as value")
})
})
@@ -53,7 +53,7 @@ describe('expression', function () {
describe('.evalExp()', function () {
it('should throw when scope undefined', function () {
expect(function () {
evalExp('')
(evalExp as any)('')
}).to.throw(/scope undefined/)
})
+17 -15
View File
@@ -1,12 +1,14 @@
import * as chai from 'chai'
import Tag from '../../src/tag'
import Scope from '../../src/scope'
import Tag from 'src/template/tag/tag'
import TagToken from 'src/parser/tag-token'
import Scope from 'src/scope/scope'
import * as sinon from 'sinon'
import * as sinonChai from 'sinon-chai'
import Liquid from 'src/liquid'
chai.use(sinonChai)
const expect = chai.expect
const tag = Tag()
const liquid = new Liquid();
describe('tag', function () {
let scope
@@ -18,22 +20,22 @@ describe('tag', function () {
coo: 'uoo'
}
})
tag.clear()
Tag.clear()
})
it('should throw when not registered', function () {
expect(function () {
tag.construct({
new Tag({ // eslint-disable-line
type: 'tag',
value: 'foo',
name: 'foo'
}, [])
}, [], liquid)
}).to.throw(/tag foo not found/)
})
it('should register simple tag', function () {
expect(function () {
tag.register('foo', {
Tag.register('foo', {
render: x => 'bar'
})
}).not.throw()
@@ -41,7 +43,7 @@ describe('tag', function () {
it('should call tag.render', async function () {
const spy = sinon.spy()
tag.register('foo', {
Tag.register('foo', {
render: spy
})
const token = {
@@ -49,7 +51,7 @@ describe('tag', function () {
value: 'foo',
name: 'foo'
}
await tag.construct(token, []).render(scope, {})
await new Tag(token, [], liquid).render(scope)
expect(spy).to.have.been.called
})
@@ -57,7 +59,7 @@ describe('tag', function () {
let spy, token
beforeEach(function () {
spy = sinon.spy()
tag.register('foo', {
Tag.register('foo', {
render: spy
})
token = {
@@ -68,29 +70,29 @@ describe('tag', function () {
}
})
it('should call tag.render with scope', async function () {
await tag.construct(token, []).render(scope, {})
await new Tag(token, [], liquid).render(scope)
expect(spy).to.have.been.calledWithMatch(scope)
})
it('should resolve identifier hash', async function () {
await tag.construct(token, []).render(scope, {})
await new Tag(token, [], liquid).render(scope)
expect(spy).to.have.been.calledWithMatch({}, {
aa: 'bar'
})
})
it('should accept space between key/value', async function () {
await tag.construct(token, []).render(scope, {})
await new Tag(token, [], liquid).render(scope)
expect(spy).to.have.been.calledWithMatch({}, {
bb: 2
})
})
it('should resolve number value hash', async function () {
await tag.construct(token, []).render(scope, {})
await new Tag(token, [], liquid).render(scope)
expect(spy).to.have.been.calledWithMatch(scope, {
cc: 2.3
})
})
it('should resolve property access hash', async function () {
await tag.construct(token, []).render(scope, {})
await new Tag(token, [], liquid).render(scope)
expect(spy).to.have.been.calledWithMatch(scope, {
dd: 'uoo'
})
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
import * as sinonChai from 'chai-as-promised'
@@ -1,4 +1,4 @@
import Liquid from '../../../src/index'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,4 +1,4 @@
import Liquid from '../../../src/index'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as mock from 'mock-fs'
import * as chai from 'chai'
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as mock from 'mock-fs'
import * as chai from 'chai'
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
@@ -1,11 +1,12 @@
import Liquid from '../../../src'
import Liquid from 'src/liquid'
import * as chai from 'chai'
const expect = chai.expect
chai.use(require('chai-as-promised'))
describe('tags/unless', function () {
const liquid = new Liquid()
let liquid
before(() => { liquid = new Liquid() })
it('should render else when predicate yields true', function () {
// 0 is truthy
@@ -1,10 +1,10 @@
import { resolve } from '../../src/template-browser'
import { resolve } from '../../src/parser/template-browser'
import * as chai from 'chai'
const expect = chai.expect
describe('template-browser', function () {
if (process.version.match(/^v(\d+)/)[1] < 8) {
if (+process.version.match(/^v(\d+)/)[1] < 8) {
console.info('jsdom not supported, skipping template-browser...')
return
}
@@ -14,11 +14,11 @@ describe('template-browser', function () {
url: 'https://example.com/foo/bar/',
contentType: 'text/html',
includeNodeLocations: true
})
global.document = dom.window.document
});
(global as any).document = dom.window.document
})
afterEach(function () {
delete global.document
delete (global as any).document
})
describe('resolve', function () {
it('should support relative root', function () {
@@ -1,4 +1,4 @@
import { resolve } from '../../src/template'
import { resolve } from '../../src/parser/template'
import * as mock from 'mock-fs'
import * as chai from 'chai'
import * as path from 'path'
@@ -1,6 +1,8 @@
const chai = require('chai')
const parse = require('../../src/tokenizer.js').parse
const expect = chai.expect
import { expect } from 'chai'
import { parse } from 'src/parser/tokenizer'
import TagToken from 'src/parser/tag-token'
import OutputToken from 'src/parser/output-token'
import HTMLToken from 'src/parser/html-token'
describe('tokenizer', function () {
describe('parse', function () {
@@ -10,19 +12,14 @@ describe('tokenizer', function () {
expect(tokens.length).to.equal(1)
expect(tokens[0].value).to.equal(html)
expect(tokens[0].type).to.equal('html')
})
it('should throw when non-string passed in', function () {
expect(function () {
parse({})
}).to.throw('illegal input')
expect(tokens[0]).instanceOf(HTMLToken)
})
it('should handle tag syntax', function () {
const html = '<p>{% for p in a[1]%}</p>'
const tokens = parse(html)
expect(tokens.length).to.equal(3)
expect(tokens[1].type).to.equal('tag')
expect(tokens[1]).instanceOf(TagToken)
expect(tokens[1].value).to.equal('for p in a[1]')
})
it('should handle value syntax', function () {
@@ -30,7 +27,7 @@ describe('tokenizer', function () {
const tokens = parse(html)
expect(tokens.length).to.equal(3)
expect(tokens[1].type).to.equal('value')
expect(tokens[1]).instanceOf(OutputToken)
expect(tokens[1].value).to.equal('foo | date: "%Y-%m-%d"')
})
it('should handle consecutive value and tags', function () {
@@ -38,8 +35,8 @@ describe('tokenizer', function () {
const tokens = parse(html)
expect(tokens.length).to.equal(4)
expect(tokens[0].type).to.equal('value')
expect(tokens[3].type).to.equal('tag')
expect(tokens[0]).instanceOf(OutputToken)
expect(tokens[2]).instanceOf(TagToken)
expect(tokens[1].value).to.equal('bar')
expect(tokens[2].value).to.equal('foo')
@@ -48,16 +45,16 @@ describe('tokenizer', function () {
const html = '{%foo%}\n{%bar %} \n {%alice%}'
const tokens = parse(html)
expect(tokens.length).to.equal(5)
expect(tokens[1].type).to.equal('html')
expect(tokens[1]).instanceOf(HTMLToken)
expect(tokens[1].raw).to.equal('\n')
expect(tokens[3].type).to.equal('html')
expect(tokens[3]).instanceOf(HTMLToken)
expect(tokens[3].raw).to.equal(' \n ')
})
it('should handle multiple lines tag', function () {
const html = '{%foo\na:a\nb:1.23\n%}'
const tokens = parse(html)
expect(tokens.length).to.equal(1)
expect(tokens[0].type).to.equal('tag')
expect(tokens[0]).instanceOf(TagToken)
expect(tokens[0].args).to.equal('a:a\nb:1.23')
expect(tokens[0].raw).to.equal('{%foo\na:a\nb:1.23\n%}')
})
@@ -65,7 +62,7 @@ describe('tokenizer', function () {
const html = '{{foo\n|date:\n"%Y-%m-%d"\n}}'
const tokens = parse(html)
expect(tokens.length).to.equal(1)
expect(tokens[0].type).to.equal('value')
expect(tokens[0]).instanceOf(OutputToken)
expect(tokens[0].raw).to.equal('{{foo\n|date:\n"%Y-%m-%d"\n}}')
})
})
@@ -1,4 +1,4 @@
import Liquid from '../../../src'
import Liquid from '../../../src/liquid'
import * as mock from 'mock-fs'
import * as chai from 'chai'
import * as path from 'path'
@@ -4,7 +4,7 @@ const expect = chai.expect
chai.use(require('chai-as-promised'))
chai.use(require('sinon-chai'))
const P = require('../../../src/util/promise.js')
const P = require('../../../src/util/promise')
describe('util/promise', function () {
describe('.anySeries()', function () {
@@ -126,7 +126,7 @@ describe('util/strftime', function () {
})
function mockUTC () {
const p = Date.prototype
const p = Date.prototype as any
p._getHours = p.getHours
p.getHours = p.getUTCHours
@@ -139,7 +139,7 @@ function mockUTC () {
}
function restoreUTC () {
const p = Date.prototype
const p = Date.prototype as any
p.getHours = p._getHours
p.getDays = p._getDays
p.getTimezoneOffset = p._getTimezoneOffset
@@ -21,7 +21,7 @@ describe('util/underscore', function () {
expect(_.isError(new RenderError(new Error(), tpl))).to.be.true
})
it('should return true for RenderBreakError', function () {
expect(_.isError(new RenderBreakError())).to.be.true
expect(_.isError(new RenderBreakError(''))).to.be.true
})
})
describe('.isString()', function () {
@@ -45,11 +45,11 @@ describe('util/underscore', function () {
it('should recursively call toLiquid()', function () {
expect(_.stringify({ toLiquid: () => ({ toLiquid: () => 'foo' }) })).to.equal('foo')
})
it('should return "null" for null', function () {
expect(_.stringify(null)).to.equal('null')
it('should return "" for null', function () {
expect(_.stringify(null)).to.equal('')
})
it('should return "undefined" for undefined', function () {
expect(_.stringify(undefined)).to.equal('undefined')
it('should return "" for undefined', function () {
expect(_.stringify(undefined)).to.equal('')
})
it('should return regex string for RegExp', function () {
const reg = /foo/g
@@ -98,7 +98,7 @@ describe('util/underscore', function () {
it('should return a range of integers', function () {
expect(_.range(3, 5)).to.deep.equal([3, 4])
})
it('should treat start as 0 if omitted', function () {
it('should start from 0 if begin omitted', function () {
expect(_.range(3)).to.deep.equal([0, 1, 2])
})
})
+72
View File
@@ -0,0 +1,72 @@
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import * as sinonChai from 'sinon-chai'
import * as sinon from 'sinon'
import Scope from '../../src/scope/scope'
import Filter from 'src/template/filter'
import Value from 'src/template/value'
chai.use(sinonChai)
chai.use(chaiAsPromised)
const expect = chai.expect
const add = (l, r) => l + r
describe('Value', function () {
beforeEach(() => Filter.clear())
it('should throw when value string illegal', function () {
expect(function () {
new Value('/')
}).to.throw(/illegal value string/)
})
it('should parse value string', function () {
const tpl: any = new Value('foo')
expect(tpl.initial).to.equal('foo')
expect(tpl.filters).to.deep.equal([])
})
it('should parse value string with a simple filter', function () {
Filter.register('add', add)
const tpl: any = new Value('foo | add: 3, "foo"')
expect(tpl.initial).to.equal('foo')
expect(tpl.filters.length).to.equal(1)
expect(tpl.filters[0].impl).to.equal(add)
})
it('should parse value string with filters', function () {
const tpl: any = new Value('foo | add: "|" | add')
expect(tpl.initial).to.equal('foo')
expect(tpl.filters.length).to.equal(2)
})
it('should eval value', function () {
Filter.register('date', (l, r) => l + r)
Filter.register('time', (l, r) => l + 3 * r)
const tpl = new Value('foo.bar[0] | date: "b" | time:2')
const scope = new Scope({
foo: { bar: ['a'] }
})
expect(tpl.value(scope)).to.equal('ab6')
})
it('should reserve type', function () {
Filter.register('arr', () => [1])
const tpl = new Value('"x" | arr')
expect(tpl.value(new Scope())).to.deep.equal([1])
})
it('should eval filter with correct arguments', function () {
const date = sinon.stub().returns('y')
const time = sinon.spy()
Filter.register('date', date)
Filter.register('time', time)
const tpl = new Value('foo.bar | date: "b" | time:2')
const scope = new Scope({
foo: { bar: 'bar' }
})
tpl.value(scope)
expect(date).to.have.been.calledWith('bar', 'b')
expect(time).to.have.been.calledWith('y', 2)
})
})
+5 -5
View File
@@ -1,4 +1,4 @@
import { read } from '../../src/template-browser'
import { read } from 'src/parser/template-browser'
import * as sinon from 'sinon'
import * as chai from 'chai'
@@ -7,7 +7,7 @@ chai.use(require('chai-as-promised'))
const expect = chai.expect
describe('template-browser', () => {
if (process.version.match(/^v(\d+)/)[1] < 8) {
if (+process.version.match(/^v(\d+)/)[1] < 8) {
console.info('jsdom not supported, skipping xhr...')
return
}
@@ -16,12 +16,12 @@ describe('template-browser', () => {
server = sinon.createFakeServer()
server.autoRespond = true
server.respondWith('GET', 'https://example.com/views/hello.html',
[200, { 'Content-Type': 'text/plain' }, 'hello {{name}}'])
global.XMLHttpRequest = sinon.useFakeXMLHttpRequest()
[200, { 'Content-Type': 'text/plain' }, 'hello {{name}}']);
(global as any).XMLHttpRequest = sinon.useFakeXMLHttpRequest()
})
afterEach(() => {
server.restore()
delete global.XMLHttpRequest
delete (global as any).XMLHttpRequest
})
describe('#read()', () => {
it('should get corresponding text', () => {