mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-14 20:00:39 -07:00
fix: coerce to Array in map and where filter
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { isArray, last as arrayLast } from '../../util/underscore'
|
||||
import { toArray } from '../../util/collection'
|
||||
import { isTruthy } from '../../render/boolean'
|
||||
import { FilterImpl } from '../../template/filter/filter-impl'
|
||||
|
||||
@@ -10,11 +11,11 @@ export const sort = <T>(v: T[], arg: (lhs: T, rhs: T) => number) => v.sort(arg)
|
||||
export const size = (v: string | any[]) => (v && v.length) || 0
|
||||
|
||||
export function map<T1, T2> (arr: {[key: string]: T1}[], arg: string): T1[] {
|
||||
return arr.map(v => v[arg])
|
||||
return toArray(arr).map(v => v[arg])
|
||||
}
|
||||
|
||||
export function concat<T1, T2> (v: T1[], arg: T2[] | T2): (T1 | T2)[] {
|
||||
return Array.prototype.concat.call(v, arg)
|
||||
return toArray(v).concat(arg)
|
||||
}
|
||||
|
||||
export function slice<T> (v: T[], begin: number, length = 1): T[] {
|
||||
@@ -23,8 +24,8 @@ export function slice<T> (v: T[], begin: number, length = 1): T[] {
|
||||
}
|
||||
|
||||
export function where<T extends object> (this: FilterImpl, arr: T[], property: string, expected?: any): T[] {
|
||||
return arr.filter(obj => {
|
||||
const value = this.context.getFromScope(obj, property.split('.'))
|
||||
return toArray(arr).filter(obj => {
|
||||
const value = this.context.getFromScope(obj, String(property).split('.'))
|
||||
return expected === undefined ? isTruthy(value) : value === expected
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { assert, Tokenizer, evalToken, Emitter, TagToken, TopLevelToken, Context, Template, TagImplOptions, ParseStream } from '../../types'
|
||||
import { toCollection } from '../../util/collection'
|
||||
import { toEnumerable } from '../../util/collection'
|
||||
import { ForloopDrop } from '../../drop/forloop-drop'
|
||||
import { Hash } from '../../template/tag/hash'
|
||||
|
||||
@@ -36,7 +36,7 @@ export default {
|
||||
},
|
||||
render: function * (ctx: Context, emitter: Emitter) {
|
||||
const r = this.liquid.renderer
|
||||
let collection = toCollection(evalToken(this.collection, ctx))
|
||||
let collection = toEnumerable(evalToken(this.collection, ctx))
|
||||
|
||||
if (!collection.length) {
|
||||
yield r.renderTemplates(this.elseTemplates, ctx, emitter)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { assert } from '../../util/assert'
|
||||
import { ForloopDrop } from '../../drop/forloop-drop'
|
||||
import { toCollection } from '../../util/collection'
|
||||
import { toEnumerable } from '../../util/collection'
|
||||
import { evalQuotedToken, TypeGuards, Tokenizer, evalToken, Hash, Emitter, TagToken, Context, TagImplOptions } from '../../types'
|
||||
|
||||
export default {
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
if (this['for']) {
|
||||
const { value, alias } = this['for']
|
||||
let collection = evalToken(value, ctx)
|
||||
collection = toCollection(collection)
|
||||
collection = toEnumerable(collection)
|
||||
scope['forloop'] = new ForloopDrop(collection.length)
|
||||
for (const item of collection) {
|
||||
scope[alias] = item
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { toCollection } from '../../util/collection'
|
||||
import { toEnumerable } from '../../util/collection'
|
||||
import { assert, evalToken, Emitter, Hash, TagToken, TopLevelToken, Context, Template, TagImplOptions, ParseStream } from '../../types'
|
||||
import { TablerowloopDrop } from '../../drop/tablerowloop-drop'
|
||||
import { Tokenizer } from '../../parser/tokenizer'
|
||||
@@ -30,7 +30,7 @@ export default {
|
||||
},
|
||||
|
||||
render: function * (ctx: Context, emitter: Emitter) {
|
||||
let collection = toCollection(evalToken(this.collection, ctx))
|
||||
let collection = toEnumerable(evalToken(this.collection, ctx))
|
||||
const hash = yield this.hash.render(ctx)
|
||||
const offset = hash.offset || 0
|
||||
const limit = (hash.limit === undefined) ? collection.length : hash.limit
|
||||
|
||||
@@ -16,7 +16,6 @@ export class Value {
|
||||
const tokenizer = new Tokenizer(str)
|
||||
this.initial = tokenizer.readValue()
|
||||
this.filters = tokenizer.readFilters().map(({ name, args }) => new Filter(name, this.filterMap.get(name), args))
|
||||
tokenizer.skipBlank()
|
||||
}
|
||||
public * value (ctx: Context) {
|
||||
let val = yield evalToken(this.initial, ctx)
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { isString, isObject, isArray } from './underscore'
|
||||
|
||||
export function toCollection (val: any) {
|
||||
export function toEnumerable (val: any) {
|
||||
if (isArray(val)) return val
|
||||
if (isString(val) && val.length > 0) return [val]
|
||||
if (isObject(val)) return Object.keys(val).map((key) => [key, val[key]])
|
||||
return []
|
||||
}
|
||||
|
||||
export function toArray (val: any) {
|
||||
if (isArray(val)) return val
|
||||
return [ val ]
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { test } from '../../../stub/render'
|
||||
import { Liquid } from '../../../../src/liquid'
|
||||
import { test, render } from '../../../stub/render'
|
||||
import { expect, use } from 'chai'
|
||||
import * as chaiAsPromised from 'chai-as-promised'
|
||||
use(chaiAsPromised)
|
||||
|
||||
describe('filters/array', function () {
|
||||
let liquid: Liquid
|
||||
beforeEach(function () {
|
||||
liquid = new Liquid()
|
||||
})
|
||||
describe('index', function () {
|
||||
it('should support index', function () {
|
||||
const src = '{% assign beatles = "John, Paul, George, Ringo" | split: ", " %}' +
|
||||
@@ -30,7 +25,7 @@ describe('filters/array', function () {
|
||||
it('should throw when comma missing', async () => {
|
||||
const src = '{% assign beatles = "John, Paul, George, Ringo" | split: ", " %}' +
|
||||
'{{ beatles | join " and " }}'
|
||||
return expect(liquid.parseAndRender(src)).to.be.rejectedWith('unexpected token at "\\" and \\"", line:1, col:65')
|
||||
return expect(render(src)).to.be.rejectedWith('unexpected token at "\\" and \\"", line:1, col:65')
|
||||
})
|
||||
})
|
||||
it('should support split/last', function () {
|
||||
@@ -38,101 +33,70 @@ describe('filters/array', function () {
|
||||
'{{ my_array|last }}'
|
||||
return test(src, 'tiger')
|
||||
})
|
||||
it('should support map', function () {
|
||||
return test('{{posts | map: "category"}}', 'foo,bar')
|
||||
describe('map', () => {
|
||||
it('should support map', function () {
|
||||
const posts = [{ category: 'foo' }, { category: 'bar' }]
|
||||
return test('{{posts | map: "category"}}', { posts }, 'foo,bar')
|
||||
})
|
||||
it('should normalize non-array input', function () {
|
||||
const post = { category: 'foo' }
|
||||
return test('{{post | map: "category"}}', { post }, 'foo')
|
||||
})
|
||||
})
|
||||
describe('reverse', function () {
|
||||
it('should support reverse', async function () {
|
||||
const html = await liquid.parseAndRender('{{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}')
|
||||
expect(html).to.equal('.moT rojaM ot lortnoc dnuorG')
|
||||
})
|
||||
it('should be pure', async function () {
|
||||
it('should support reverse', () => test(
|
||||
'{{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}',
|
||||
'.moT rojaM ot lortnoc dnuorG'
|
||||
))
|
||||
it('should be pure', async () => {
|
||||
const scope = { arr: ['a', 'b', 'c'] }
|
||||
await liquid.parseAndRender('{{ arr | reverse | join: "" }}', scope)
|
||||
const html = await liquid.parseAndRender('{{ arr | join: "" }}', scope)
|
||||
await render('{{ arr | reverse | join: "" }}', scope)
|
||||
const html = await render('{{ arr | join: "" }}', scope)
|
||||
expect(html).to.equal('abc')
|
||||
})
|
||||
})
|
||||
describe('size', function () {
|
||||
it('should return string length', async () => {
|
||||
const html = await liquid.parseAndRender('{{ "Ground control to Major Tom." | size }}')
|
||||
expect(html).to.equal('28')
|
||||
})
|
||||
it('should return array size', async () => {
|
||||
const html = await liquid.parseAndRender(
|
||||
'{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}{{ my_array | size }}')
|
||||
expect(html).to.equal('4')
|
||||
})
|
||||
it('should be respected with <string>.size notation', async () => {
|
||||
const html = await liquid.parseAndRender('{% assign my_string = "Ground control to Major Tom." %}{{ my_string.size }}')
|
||||
expect(html).to.equal('28')
|
||||
})
|
||||
it('should be respected with <array>.size notation', async () => {
|
||||
const html = await liquid.parseAndRender('{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}{{ my_array.size }}')
|
||||
expect(html).to.equal('4')
|
||||
})
|
||||
it('should return 0 for false', async () => {
|
||||
const html = await liquid.parseAndRender('{{ false | size }}')
|
||||
expect(html).to.equal('0')
|
||||
})
|
||||
it('should return 0 for nil', async () => {
|
||||
const html = await liquid.parseAndRender('{{ nil | size }}')
|
||||
expect(html).to.equal('0')
|
||||
})
|
||||
it('should return 0 for undefined', async () => {
|
||||
const html = await liquid.parseAndRender('{{ foo | size }}')
|
||||
expect(html).to.equal('0')
|
||||
})
|
||||
it('should return string length', () => test(
|
||||
'{{ "Ground control to Major Tom." | size }}',
|
||||
'28'
|
||||
))
|
||||
it('should return array size', () => test(
|
||||
'{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}{{ my_array | size }}',
|
||||
'4'
|
||||
))
|
||||
it('should be respected with <string>.size notation', () => test(
|
||||
'{% assign my_string = "Ground control to Major Tom." %}{{ my_string.size }}',
|
||||
'28'
|
||||
))
|
||||
it('should be respected with <array>.size notation', () => test(
|
||||
'{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}{{ my_array.size }}',
|
||||
'4'
|
||||
))
|
||||
it('should return 0 for false', () => test('{{ false | size }}', '0'))
|
||||
it('should return 0 for nil', () => test('{{ nil | size }}', '0'))
|
||||
it('should return 0 for undefined', () => test('{{ foo | size }}', '0'))
|
||||
})
|
||||
describe('first', function () {
|
||||
it('should support first', async () => {
|
||||
const html = await liquid.parseAndRender(
|
||||
'{{arr | first}}',
|
||||
{ arr: [ 'zebra', 'tiger' ] }
|
||||
)
|
||||
expect(html).to.equal('zebra')
|
||||
})
|
||||
it('should return empty for nil', async () => {
|
||||
const html = await liquid.parseAndRender('{{nil | first}}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should return empty for undefined', async () => {
|
||||
const html = await liquid.parseAndRender('{{foo | first}}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should return empty for false', async () => {
|
||||
const html = await liquid.parseAndRender('{{false | first}}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should return empty for string', async () => {
|
||||
const html = await liquid.parseAndRender('{{"zebra" | first}}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should support first', () => test(
|
||||
'{{arr | first}}',
|
||||
{ arr: [ 'zebra', 'tiger' ] },
|
||||
'zebra'
|
||||
))
|
||||
it('should return empty for nil', () => test('{{nil | first}}', ''))
|
||||
it('should return empty for undefined', () => test('{{foo | first}}', ''))
|
||||
it('should return empty for false', () => test('{{false | first}}', ''))
|
||||
it('should return empty for string', () => test('{{"zebra" | first}}', ''))
|
||||
})
|
||||
describe('last', function () {
|
||||
it('should support last', async () => {
|
||||
const html = await liquid.parseAndRender(
|
||||
'{{arr | last}}',
|
||||
{ arr: [ 'zebra', 'tiger' ] }
|
||||
)
|
||||
expect(html).to.equal('tiger')
|
||||
})
|
||||
it('should return empty for nil', async () => {
|
||||
const html = await liquid.parseAndRender('{{nil | last}}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should return empty for undefined', async () => {
|
||||
const html = await liquid.parseAndRender('{{foo | last}}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should return empty for false', async () => {
|
||||
const html = await liquid.parseAndRender('{{false | last}}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should return empty for string', async () => {
|
||||
const html = await liquid.parseAndRender('{{"zebra" | last}}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should support last', () => test(
|
||||
'{{arr | last}}',
|
||||
{ arr: [ 'zebra', 'tiger' ] },
|
||||
'tiger'
|
||||
))
|
||||
it('should return empty for nil', () => test('{{nil | last}}', ''))
|
||||
it('should return empty for undefined', () => test('{{foo | last}}', ''))
|
||||
it('should return empty for false', () => test('{{false | last}}', ''))
|
||||
it('should return empty for string', () => test('{{"zebra" | last}}', ''))
|
||||
})
|
||||
describe('slice', function () {
|
||||
it('should slice first char by 0', () => test('{{ "Liquid" | slice: 0 }}', 'L'))
|
||||
@@ -161,44 +125,60 @@ describe('filters/array', function () {
|
||||
})
|
||||
})
|
||||
describe('where', function () {
|
||||
const products = [
|
||||
{ title: 'Vacuum', type: 'living room' },
|
||||
{ title: 'Spatula', type: 'kitchen' },
|
||||
{ title: 'Television', type: 'living room' },
|
||||
{ title: 'Garlic press', type: 'kitchen' },
|
||||
{ title: 'Coffee mug', available: true },
|
||||
{ title: 'Limited edition sneakers', available: false },
|
||||
{ title: 'Boring sneakers', available: true }
|
||||
]
|
||||
it('should support filter by property value', function () {
|
||||
return test(`{% assign kitchen_products = products | where: "type", "kitchen" %}
|
||||
Kitchen products:
|
||||
{% for product in kitchen_products -%}
|
||||
- {{ product.title }}
|
||||
{% endfor %}`, `
|
||||
Kitchen products:
|
||||
- Spatula
|
||||
- Garlic press
|
||||
`)
|
||||
Kitchen products:
|
||||
{% for product in kitchen_products -%}
|
||||
- {{ product.title }}
|
||||
{% endfor %}`, { products }, `
|
||||
Kitchen products:
|
||||
- Spatula
|
||||
- Garlic press
|
||||
`)
|
||||
})
|
||||
it('should support filter truthy property', function () {
|
||||
return test(`{% assign available_products = products | where: "available" %}
|
||||
Available products:
|
||||
{% for product in available_products -%}
|
||||
- {{ product.title }}
|
||||
{% endfor %}`, `
|
||||
Available products:
|
||||
- Coffee mug
|
||||
- Boring sneakers
|
||||
`)
|
||||
Available products:
|
||||
{% for product in available_products -%}
|
||||
- {{ product.title }}
|
||||
{% endfor %}`, { products }, `
|
||||
Available products:
|
||||
- Coffee mug
|
||||
- Boring sneakers
|
||||
`)
|
||||
})
|
||||
it('should support nested property', async function () {
|
||||
const authors = [
|
||||
{ name: 'Alice', books: { year: 2019 } },
|
||||
{ name: 'Bob', books: { year: 2018 } }
|
||||
]
|
||||
const html = await liquid.parseAndRender(
|
||||
return test(
|
||||
`{% assign recentAuthors = authors | where: 'books.year', 2019 %}
|
||||
Recent Authors:
|
||||
{%- for author in recentAuthors %}
|
||||
- {{author.name}}
|
||||
{%- endfor %}`,
|
||||
{ authors }
|
||||
)
|
||||
expect(html).to.equal(`
|
||||
Recent Authors:
|
||||
- Alice`)
|
||||
Recent Authors:
|
||||
{%- for author in recentAuthors %}
|
||||
- {{author.name}}
|
||||
{%- endfor %}`,
|
||||
{ authors }, `
|
||||
Recent Authors:
|
||||
- Alice`)
|
||||
})
|
||||
it('should apply to string', async () => {
|
||||
await test('{{"abc" | where: 1, "b" }}', 'abc')
|
||||
await test('{{"abc" | where: 1, "a" }}', '')
|
||||
})
|
||||
it('should normalize non-array input', async () => {
|
||||
const scope = { obj: { foo: 'FOO' } }
|
||||
await test('{{obj | where: "foo", "FOO" }}', scope, '[object Object]')
|
||||
await test('{{obj | where: "foo", "BAR" }}', scope, '')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { test, ctx } from '../../../stub/render'
|
||||
import { expect } from 'chai'
|
||||
import { Liquid } from '../../../../src/liquid'
|
||||
import { test } from '../../../stub/render'
|
||||
|
||||
describe('filters/date', function () {
|
||||
let liquid: Liquid
|
||||
beforeEach(function () {
|
||||
liquid = new Liquid()
|
||||
})
|
||||
it('should support date: %a %b %d %Y', function () {
|
||||
const str = ctx.date.toDateString()
|
||||
return test('{{ date | date:"%a %b %d %Y"}}', str)
|
||||
const date = new Date()
|
||||
return test('{{ date | date:"%a %b %d %Y"}}', { date }, date.toDateString())
|
||||
})
|
||||
it('should create a new Date when given "now"', function () {
|
||||
return test('{{ "now" | date: "%Y"}}', (new Date()).getFullYear().toString())
|
||||
@@ -23,27 +17,21 @@ describe('filters/date', function () {
|
||||
it('should render string as string if not valid', function () {
|
||||
return test('{{ "foo" | date: "%Y"}}', 'foo')
|
||||
})
|
||||
it('should render object as string if not valid', function () {
|
||||
return test('{{ obj | date: "%Y"}}', '[object Object]')
|
||||
it('should render object as string', function () {
|
||||
return test('{{ obj | date: "%Y"}}', { obj: {} }, '[object Object]')
|
||||
})
|
||||
it('should create from number', async function () {
|
||||
const time = new Date('2017-03-07T12:00:00').getTime() / 1000
|
||||
const src = '{{ time | date: "%Y-%m-%dT%H:%M:%S" }}'
|
||||
const ctx = { time }
|
||||
const dst = '2017-03-07T12:00:00'
|
||||
expect(await liquid.parseAndRender(src, ctx)).to.equal(dst)
|
||||
return test('{{ time | date: "%Y-%m-%dT%H:%M:%S" }}', { time }, '2017-03-07T12:00:00')
|
||||
})
|
||||
it('should create from number-like string', async function () {
|
||||
const time = String(new Date('2017-03-07T12:00:00').getTime() / 1000)
|
||||
const src = '{{ time | date: "%Y-%m-%dT%H:%M:%S" }}'
|
||||
const ctx = { time }
|
||||
const dst = '2017-03-07T12:00:00'
|
||||
expect(await liquid.parseAndRender(src, ctx)).to.equal(dst)
|
||||
return test('{{ time | date: "%Y-%m-%dT%H:%M:%S" }}', { time }, '2017-03-07T12:00:00')
|
||||
})
|
||||
it('should support manipulation', async function () {
|
||||
const src = '{{ date | date: "%s" | minus : 604800 | date: "%Y-%m-%dT%H:%M:%S"}}'
|
||||
const ctx = { date: new Date('2017-03-07T12:00:00') }
|
||||
const dst = '2017-02-28T12:00:00'
|
||||
expect(await liquid.parseAndRender(src, ctx)).to.equal(dst)
|
||||
return test('{{ date | date: "%s" | minus : 604800 | date: "%Y-%m-%dT%H:%M:%S"}}',
|
||||
{ date: new Date('2017-03-07T12:00:00') },
|
||||
'2017-02-28T12:00:00'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('filters/html', function () {
|
||||
return test('{{ "Tetsuro Takara" | escape }}', 'Tetsuro Takara')
|
||||
})
|
||||
it('should escape function', function () {
|
||||
return test('{{ func | escape }}', 'function () { }')
|
||||
return test('{{ func | escape }}', { func: function () {} }, 'function () { }')
|
||||
})
|
||||
it('should escape undefined', function () {
|
||||
return test('{{ nonExistent.value | escape }}', '')
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { expect } from 'chai'
|
||||
import { test, liquid } from '../../../stub/render'
|
||||
import { Liquid } from '../../../../src/liquid'
|
||||
|
||||
describe('filters/math', function () {
|
||||
const l = 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'))
|
||||
it('should return 2 for arr[0]', () => test('{{ arr[0] | abs }}', { arr: [-2, 'a'] }, '2'))
|
||||
it('should return convert string', () => test('{{ "-3" | abs }}', '3'))
|
||||
})
|
||||
describe('at_least', function () {
|
||||
@@ -60,10 +57,7 @@ describe('filters/math', function () {
|
||||
() => test('{{ 183.357 | plus: 12 }}', '195.357'))
|
||||
it('should convert first arg as number', () => test('{{ "4" | plus: 2 }}', '6'))
|
||||
it('should convert both args as number', () => test('{{ "4" | plus: "2" }}', '6'))
|
||||
it('should support variable', async () => {
|
||||
const html = await l.parseAndRender('{{ 4 | plus: b }}', { b: 2 })
|
||||
expect(html).to.equal('6')
|
||||
})
|
||||
it('should support variable', () => test('{{ 4 | plus: b }}', { b: 2 }, '6'))
|
||||
})
|
||||
|
||||
describe('sort_natural', function () {
|
||||
@@ -73,55 +67,36 @@ describe('filters/math', function () {
|
||||
'giraffe, octopus, Sally Snake, zebra'
|
||||
)
|
||||
})
|
||||
it('should sort with specified property', async () => {
|
||||
const src = '{{ students | sort_natural: "name" | map: "name" | join }}'
|
||||
const students = [{ name: 'bob' }, { name: 'alice' }, { name: 'carol' }]
|
||||
const html = await l.parseAndRender(src, { students })
|
||||
expect(html).to.equal('alice bob carol')
|
||||
})
|
||||
it('should be stable', async () => {
|
||||
const src = '{{ students | sort_natural: "age" | map: "name" | join }}'
|
||||
const students = [
|
||||
{ name: 'bob', age: 1 },
|
||||
{ name: 'alice', age: 1 },
|
||||
{ name: 'carol', age: 1 }
|
||||
]
|
||||
const html = await l.parseAndRender(src, { students })
|
||||
expect(html).to.equal('bob alice carol')
|
||||
})
|
||||
it('should be stable when it comes to undefined props', async () => {
|
||||
const src = '{{ students | sort_natural: "age" | map: "name" | join }}'
|
||||
const students = [
|
||||
{ name: 'bob' },
|
||||
{ name: 'alice', age: 2 },
|
||||
{ name: 'amber' },
|
||||
{ name: 'watson' },
|
||||
{ name: 'michael' },
|
||||
{ name: 'charlie' }
|
||||
]
|
||||
const html = await l.parseAndRender(src, { students })
|
||||
expect(html).to.equal('alice bob amber watson michael charlie')
|
||||
})
|
||||
it('should tolerate undefined props', async () => {
|
||||
const src = '{{ students | sort_natural: "age" | map: "name" | join }}'
|
||||
const students = [
|
||||
{ name: 'bob' },
|
||||
{ name: 'alice', age: 2 },
|
||||
{ name: 'carol' }
|
||||
]
|
||||
const html = await l.parseAndRender(src, { students })
|
||||
expect(html).to.equal('alice bob carol')
|
||||
})
|
||||
it('should tolerate non array', async () => {
|
||||
const src = '{{ students | sort_natural: "age" | map: "name" | join }}'
|
||||
const html = await l.parseAndRender(src, { students: {} })
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should tolerate falsy input', async () => {
|
||||
const src = '{{ students | sort_natural: "age" | map: "name" | join }}'
|
||||
const html = await l.parseAndRender(src, { students: undefined })
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should sort with specified property', () => test(
|
||||
'{{ students | sort_natural: "name" | map: "name" | join }}',
|
||||
{ students: [{ name: 'bob' }, { name: 'alice' }, { name: 'carol' }] },
|
||||
'alice bob carol'
|
||||
))
|
||||
it('should be stable', () => test(
|
||||
'{{ students | sort_natural: "age" | map: "name" | join }}',
|
||||
{ students: [{ name: 'bob', age: 1 }, { name: 'alice', age: 1 }, { name: 'carol', age: 1 }] },
|
||||
'bob alice carol'
|
||||
))
|
||||
it('should be stable when it comes to undefined props', () => test(
|
||||
'{{ students | sort_natural: "age" | map: "name" | join }}',
|
||||
{ students: [{ name: 'bob' }, { name: 'alice', age: 2 }, { name: 'amber' }, { name: 'watson' }, { name: 'michael' }, { name: 'charlie' }] },
|
||||
'alice bob amber watson michael charlie'
|
||||
))
|
||||
it('should tolerate undefined props', () => test(
|
||||
'{{ students | sort_natural: "age" | map: "name" | join }}',
|
||||
{ students: [{ name: 'bob' }, { name: 'alice', age: 2 }, { name: 'carol' }] },
|
||||
'alice bob carol'
|
||||
))
|
||||
it('should tolerate non array', () => test(
|
||||
'{{ students | sort_natural: "age" | map: "name" | join }}',
|
||||
{ students: {} },
|
||||
''
|
||||
))
|
||||
it('should tolerate falsy input', () => test(
|
||||
'{{ students | sort_natural: "age" | map: "name" | join }}',
|
||||
{ students: undefined },
|
||||
''
|
||||
))
|
||||
})
|
||||
describe('round', function () {
|
||||
it('should return "1" for 1.2', () => test('{{1.2|round}}', '1'))
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import { test } from '../../../stub/render'
|
||||
import { Liquid } from '../../../../src/liquid'
|
||||
import { expect, use } from 'chai'
|
||||
import * as chaiAsPromised from 'chai-as-promised'
|
||||
|
||||
use(chaiAsPromised)
|
||||
|
||||
describe('filters/string', function () {
|
||||
let liquid: Liquid
|
||||
beforeEach(function () {
|
||||
liquid = new Liquid()
|
||||
})
|
||||
describe('append', function () {
|
||||
it('should return "-3abc" for -3, "abc"',
|
||||
() => test('{{ -3 | append: "abc" }}', '-3abc'))
|
||||
it('should return "abar" for "a", foo', () => test('{{ "a" | append: foo }}', 'abar'))
|
||||
it('should return "abar" for "a", foo', () => test('{{ "a" | append: foo }}', { foo: 'bar' }, 'abar'))
|
||||
it('should throw if second argument undefined', () => {
|
||||
return expect(test('{{ "abc" | append: undefinedVar }}', 'abc')).to.be.rejectedWith(/2 arguments/)
|
||||
})
|
||||
@@ -25,7 +20,7 @@ describe('filters/string', function () {
|
||||
describe('prepend', function () {
|
||||
it('should return "-3abc" for -3, "abc"',
|
||||
() => test('{{ -3 | prepend: "abc" }}', 'abc-3'))
|
||||
it('should return "abar" for "a", foo', () => test('{{ "a" | prepend: foo }}', 'bara'))
|
||||
it('should return "abar" for "a", foo', () => test('{{ "a" | prepend: foo }}', { foo: 'bar' }, 'bara'))
|
||||
it('should throw if second argument undefined', () => {
|
||||
return expect(test('{{ "abc" | prepend: undefinedVar }}', 'abc')).to.be.rejectedWith(/2 arguments/)
|
||||
})
|
||||
@@ -35,18 +30,9 @@ describe('filters/string', function () {
|
||||
it('should return "falseabc" for "abc", false', () => test('{{ "abc" | prepend: false }}', 'falseabc'))
|
||||
})
|
||||
describe('capitalize', function () {
|
||||
it('should capitalize first', async () => {
|
||||
const html = await liquid.parseAndRender('{{ "i am good" | capitalize }}')
|
||||
expect(html).to.equal('I am good')
|
||||
})
|
||||
it('should return empty for nil', async () => {
|
||||
const html = await liquid.parseAndRender('{{ nil | capitalize }}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should return empty for undefined', async () => {
|
||||
const html = await liquid.parseAndRender('{{ foo | capitalize }}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should capitalize first', () => test('{{ "i am good" | capitalize }}', 'I am good'))
|
||||
it('should return empty for nil', () => test('{{ nil | capitalize }}', ''))
|
||||
it('should return empty for undefined', async () => test('{{ foo | capitalize }}', ''))
|
||||
})
|
||||
describe('concat', function () {
|
||||
it('should concat arrays', () => test(`
|
||||
@@ -84,18 +70,13 @@ describe('filters/string', function () {
|
||||
`))
|
||||
})
|
||||
describe('downcase', function () {
|
||||
it('should return "parker moore" for "Parker Moore"', async () => {
|
||||
const html = await liquid.parseAndRender('{{ "Parker Moore" | downcase }}')
|
||||
expect(html).to.equal('parker moore')
|
||||
})
|
||||
it('should return "apple" for "apple"', async () => {
|
||||
const html = await liquid.parseAndRender('{{ "apple" | downcase }}')
|
||||
expect(html).to.equal('apple')
|
||||
})
|
||||
it('should return empty for undefined', async () => {
|
||||
const html = await liquid.parseAndRender('{{ foo | downcase }}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should return "parker moore" for "Parker Moore"', () =>
|
||||
test('{{ "Parker Moore" | downcase }}', 'parker moore')
|
||||
)
|
||||
it('should return "apple" for "apple"', () =>
|
||||
test('{{ "apple" | downcase }}', 'apple')
|
||||
)
|
||||
it('should return empty for undefined', () => test('{{ foo | downcase }}', ''))
|
||||
})
|
||||
describe('split', function () {
|
||||
it('should support split/first', function () {
|
||||
@@ -105,14 +86,8 @@ describe('filters/string', function () {
|
||||
})
|
||||
})
|
||||
describe('upcase', function () {
|
||||
it('should support upcase', async () => {
|
||||
const html = await liquid.parseAndRender('{{ "Parker Moore" | upcase }}')
|
||||
expect(html).to.equal('PARKER MOORE')
|
||||
})
|
||||
it('should return empty for undefined', async () => {
|
||||
const html = await liquid.parseAndRender('{{ foo | upcase }}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should support upcase', () => test('{{ "Parker Moore" | upcase }}', 'PARKER MOORE'))
|
||||
it('should return empty for undefined', () => test('{{ foo | upcase }}', ''))
|
||||
})
|
||||
it('should support lstrip', function () {
|
||||
const src = '{{ " So much room for activities! " | lstrip }}'
|
||||
@@ -124,24 +99,15 @@ describe('filters/string', function () {
|
||||
'liquidmarkup.com/index.html')
|
||||
})
|
||||
describe('remove', function () {
|
||||
it('should support remove', async () => {
|
||||
const html = await liquid.parseAndRender('{{ "I strained to see the train through the rain" | remove: "rain" }}')
|
||||
expect(html).to.equal('I sted to see the t through the ')
|
||||
})
|
||||
it('should return empty for undefined', async () => {
|
||||
const html = await liquid.parseAndRender('{{ foo | remove: "rain" }}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should support remove', () => test(
|
||||
'{{ "I strained to see the train through the rain" | remove: "rain" }}',
|
||||
'I sted to see the t through the '
|
||||
))
|
||||
it('should return empty for undefined', () => test('{{ foo | remove: "rain" }}', ''))
|
||||
})
|
||||
describe('remove_first', function () {
|
||||
it('should support remove_first', async () => {
|
||||
const html = await liquid.parseAndRender('{{ "I strained to see the train through the rain" | remove_first: "rain" }}')
|
||||
expect(html).to.equal('I sted to see the train through the rain')
|
||||
})
|
||||
it('should return empty for undefined', async () => {
|
||||
const html = await liquid.parseAndRender('{{ foo | remove_first: "r" }}')
|
||||
expect(html).to.equal('')
|
||||
})
|
||||
it('should support remove_first', () => test('{{ "I strained to see the train through the rain" | remove_first: "rain" }}', 'I sted to see the train through the rain'))
|
||||
it('should return empty for undefined', () => test('{{ foo | remove_first: "r" }}', ''))
|
||||
})
|
||||
it('should support replace', function () {
|
||||
return test('{{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}',
|
||||
|
||||
+8
-19
@@ -3,25 +3,14 @@ import { expect } from 'chai'
|
||||
|
||||
export const liquid = new Liquid()
|
||||
|
||||
export const ctx = {
|
||||
date: new Date(),
|
||||
foo: 'bar',
|
||||
arr: [-2, 'a'],
|
||||
obj: { foo: 'bar' },
|
||||
func: function () {}, // eslint-disable-line
|
||||
posts: [{ category: 'foo' }, { category: 'bar' }],
|
||||
products: [
|
||||
{ title: 'Vacuum', type: 'living room' },
|
||||
{ title: 'Spatula', type: 'kitchen' },
|
||||
{ title: 'Television', type: 'living room' },
|
||||
{ title: 'Garlic press', type: 'kitchen' },
|
||||
{ title: 'Coffee mug', available: true },
|
||||
{ title: 'Limited edition sneakers', available: false },
|
||||
{ title: 'Boring sneakers', available: true }
|
||||
]
|
||||
export function render (src: string, ctx?: object) {
|
||||
return liquid.parseAndRender(src, ctx)
|
||||
}
|
||||
|
||||
export async function test (src: string, dst: string) {
|
||||
const html = await liquid.parseAndRender(src, ctx)
|
||||
return expect(html).to.equal(dst)
|
||||
export async function test (src: string, ctx: object | string, dst?: string) {
|
||||
if (dst === undefined) {
|
||||
dst = ctx as string
|
||||
ctx = {}
|
||||
}
|
||||
return expect(await render(src, ctx as object)).to.equal(dst)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user