feat: add find_index, has, and reject filters (#799)

* feat: add find_index, has, and reject filters

* Minor tweaks

* Change semantics of jekyllStyle, add more tests

* Some docs improvements
This commit is contained in:
Bruno Carvalho
2025-02-23 22:57:17 +08:00
committed by GitHub
parent b0facc71f7
commit 0deb93eeae
10 changed files with 595 additions and 23 deletions
+259 -2
View File
@@ -417,6 +417,32 @@ describe('filters/array', function () {
- Boring sneakers
`)
})
it('should support filter with undefined target', function () {
return test(`{% assign typed_products = products | where: "type", notdefined %}
Typed products:
{% for product in typed_products -%}
- {{ product.title }}
{% endfor %}`, { products }, `
Typed products:
- Vacuum
- Spatula
- Television
- Garlic press
`)
})
it('should support no target', function () {
return test(`{% assign typed_products = products | where: "type" %}
Typed products:
{% for product in typed_products -%}
- {{ product.title }}
{% endfor %}`, { products }, `
Typed products:
- Vacuum
- Spatula
- Television
- Garlic press
`)
})
it('should support nested property', async function () {
const authors = [
{ name: 'Alice', books: { year: 2019 } },
@@ -491,7 +517,7 @@ describe('filters/array', function () {
await test('{{objs | where: "foo", "FOO" | json}}', scope, '[]')
})
describe('jekyll style', () => {
it('should not match string with array', async () => {
it('should filter arrays by inclusion', async () => {
const scope = { objs: [{ foo: ['FOO', 'bar'] }] }
await test('{{objs | where: "foo", "FOO" | json}}', scope, '[{"foo":["FOO","bar"]}]', { jekyllWhere: true })
})
@@ -499,6 +525,14 @@ describe('filters/array', function () {
const scope = { pages: [{ tags: ['FOO'] }, { tags: [] }, { title: 'foo' }] }
await test('{{pages | where: "tags", empty | json}}', scope, '[{"tags":[]}]', { jekyllWhere: true })
})
it('should filter by undefined when target is omitted', async () => {
await test('{{products | where: "type" | map: "title" | join: ","}}', { products },
'Coffee mug,Limited edition sneakers,Boring sneakers', { jekyllWhere: true })
})
it('should filter plainly when target is undefined', async () => {
await test('{{products | where: "type", notdefined | map: "title" | join: ","}}', { products },
'Coffee mug,Limited edition sneakers,Boring sneakers', { jekyllWhere: true })
})
})
})
describe('where_exp', function () {
@@ -537,6 +571,89 @@ describe('filters/array', function () {
return test(tpl, scope, html)
})
})
describe('reject', 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 reject by property value', function () {
return test(`{% assign kitchen_products = products | reject: "type", "kitchen" %}
Kitchen products:
{% for product in kitchen_products -%}
- {{ product.title }}
{% endfor %}`, { products }, `
Kitchen products:
- Vacuum
- Television
- Coffee mug
- Limited edition sneakers
- Boring sneakers
`)
})
it('should support reject truthy property', function () {
return test(`{% assign unavailable_products = products | reject: "available" %}
Unavailable products:
{% for product in unavailable_products -%}
- {{ product.title }}
{% endfor %}`, { products }, `
Unavailable products:
- Vacuum
- Spatula
- Television
- Garlic press
- Limited edition sneakers
`)
})
it('should support reject by string property', function () {
return test(`{% assign untyped_products = products | reject: "type" %}
Untyped products:
{% for product in untyped_products -%}
- {{ product.title }}
{% endfor %}`, { products }, `
Untyped products:
- Coffee mug
- Limited edition sneakers
- Boring sneakers
`)
})
describe('jekyll style', () => {
it('should filter arrays by exclusion', async () => {
const scope = { objs: [{ foo: ['FOO', 'bar'] }, { foo: ['bar', 'baz'] }, { foo: ['FOO'] }] }
await test('{{objs | reject: "foo", "FOO" | json}}', scope, '[{"foo":["bar","baz"]}]', { jekyllWhere: true })
})
it('should filter by undefined when target is omitted', async () => {
await test('{{products | reject: "type" | map: "title" | join: ","}}', { products },
'Vacuum,Spatula,Television,Garlic press', { jekyllWhere: true })
})
})
})
describe('reject_exp', 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 reject by exp', function () {
return test(`{% assign kitchen_products = products | reject_exp: "item", "item.type != 'kitchen'" %}
Kitchen products:
{% for product in kitchen_products -%}
- {{ product.title }}
{% endfor %}`, { products }, `
Kitchen products:
- Spatula
- Garlic press
`)
})
})
describe('group_by', function () {
const members = [
{ graduation_year: 2003, name: 'Jay' },
@@ -616,12 +733,76 @@ describe('filters/array', function () {
JSON.stringify(expected))
})
})
describe('find', function () {
describe('has', function () {
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack', age: 13 }
]
it('should support has with no value', function () {
return test(
`{{ members | has: "age" | json }}, {{ members | has: "height" | json }}`,
{ members },
`true, false`)
})
it('should support has by property', function () {
return test(
`{{ members | has: "graduation_year", 2014 | json }}`,
{ members },
`true`)
})
it('should return false if not found', function () {
return test(
`{{ members | has: "graduation_year", 2018 | json }}`,
{ members },
`false`)
})
describe('jekyll style', () => {
it('should select array by inclusion', async () => {
const scope = { objs: [{ foo: ['FOO', 'bar'] }] }
await test('{{objs | has: "foo", "FOO" | json}}', scope, 'true', { jekyllWhere: true })
})
it('should support empty as target', async () => {
const scope = { pages: [{ tags: ['FOO'] }, { tags: [] }, { title: 'foo' }] }
await test('{{pages | has: "tags", empty | json}}', scope, 'true', { jekyllWhere: true })
})
it('should search plainly when target is undefined', async () => {
await test('{{members | has: "age", notdefined}}', { members }, 'true', { jekyllWhere: true })
await test('{{members | has: "name", notdefined}}', { members }, 'false', { jekyllWhere: true })
})
})
})
describe('has_exp', function () {
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack' }
]
it('should support has by expression', function () {
return test(
`{{ members | has_exp: "item", "item.graduation_year == 2014" | json }}`,
{ members },
`true`)
})
it('should return false if not found', function () {
return test(
`{{ members | has_exp: "item", "item.graduation_year == 2018" | json }}`,
{ members },
`false`)
})
})
describe('find', function () {
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack', age: 13 }
]
it('should support find with no value', function () {
return test(
`{{ members | find: "age" | json }}`,
{ members },
`{"graduation_year":2014,"name":"Jack","age":13}`)
})
it('should support find by property', function () {
return test(
`{{ members | find: "graduation_year", 2014 | json }}`,
@@ -634,6 +815,24 @@ describe('filters/array', function () {
{ members },
``)
})
describe('jekyll style', () => {
it('should select array by inclusion', async () => {
const scope = { objs: [{ foo: ['FOO', 'bar'] }] }
await test('{{objs | find: "foo", "FOO" | json}}', scope, '{"foo":["FOO","bar"]}', { jekyllWhere: true })
})
it('should support empty as target', async () => {
const scope = { pages: [{ tags: ['FOO'] }, { tags: [] }, { title: 'foo' }] }
await test('{{pages | find: "tags", empty | json}}', scope, '{"tags":[]}', { jekyllWhere: true })
})
it('should search plainly when target is undefined', async () => {
await test(
'{{members | find: "age", notdefined | json}}',
{ members },
'{"graduation_year":2013,"name":"Jay"}',
{ jekyllWhere: true })
await test('{{members | find: "name", notdefined | json}}', { members }, '', { jekyllWhere: true })
})
})
})
describe('find_exp', function () {
const members = [
@@ -654,4 +853,62 @@ describe('filters/array', function () {
``)
})
})
describe('find_index', function () {
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack', age: 13 }
]
it('should support find_index with no value', function () {
return test(
`{{ members | find_index: "age" | json }}`,
{ members },
`2`)
})
it('should support find_index by property', function () {
return test(
`{{ members | find_index: "graduation_year", 2014 | json }}`,
{ members },
`1`)
})
it('should render none if not found', function () {
return test(
`{{ members | find_index: "graduation_year", 2018 | json }}`,
{ members },
``)
})
describe('jekyll style', () => {
it('should select array by inclusion', async () => {
const scope = { objs: [{ foo: ['FOO', 'bar'] }] }
await test('{{objs | find_index: "foo", "FOO" | json}}', scope, '0', { jekyllWhere: true })
})
it('should support empty as target', async () => {
const scope = { pages: [{ tags: ['FOO'] }, { tags: [] }, { title: 'foo' }] }
await test('{{pages | find_index: "tags", empty | json}}', scope, '1', { jekyllWhere: true })
})
it('should search plainly when target is undefined', async () => {
await test('{{members | find_index: "age", notdefined | json}}', { members }, '0', { jekyllWhere: true })
await test('{{members | find_index: "name", notdefined | json}}', { members }, '', { jekyllWhere: true })
})
})
})
describe('find_index_exp', function () {
const members = [
{ graduation_year: 2013, name: 'Jay' },
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2014, name: 'Jack' }
]
it('should support find_index by expression', function () {
return test(
`{{ members | find_index_exp: "item", "item.graduation_year == 2014" | json }}`,
{ members },
`1`)
})
it('should render none if not found', function () {
return test(
`{{ members | find_index_exp: "item", "item.graduation_year == 2018" | json }}`,
{ members },
``)
})
})
})