diff --git a/src/render/operator.ts b/src/render/operator.ts index 0e0a25378..b97291bc0 100644 --- a/src/render/operator.ts +++ b/src/render/operator.ts @@ -1,6 +1,6 @@ import { isComparable } from '../drop/comparable' import { Context } from '../context/context' -import { isFunction } from '../util/underscore' +import { isFunction, toValue } from '../util/underscore' import { isTruthy } from '../render/boolean' export interface Operators { @@ -39,6 +39,8 @@ export const defaultOperators: Operators = { return l <= r }, 'contains': (l: any, r: any) => { + l = toValue(l) + r = toValue(r) return l && isFunction(l.indexOf) ? l.indexOf(r) > -1 : false }, 'and': (l: any, r: any, ctx: Context) => isTruthy(l, ctx) && isTruthy(r, ctx), diff --git a/test/e2e/issues.ts b/test/e2e/issues.ts index 1734400d4..c56941947 100644 --- a/test/e2e/issues.ts +++ b/test/e2e/issues.ts @@ -1,4 +1,4 @@ -import { Liquid } from '../..' +import { Liquid, Drop } from '../..' import { expect, use } from 'chai' import * as chaiAsPromised from 'chai-as-promised' import * as sinon from 'sinon' @@ -232,4 +232,13 @@ describe('Issues', function () { const html = await engine.parseAndRender(`{% assign a = "x,y,z" | split: ',' -%}{{ a[-1] }} {{ a[-3] }} {{ a[-8] }}`) expect(html).to.equal('z x ') }) + it('#492 contains operator does not support Drop', async () => { + class TemplateDrop extends Drop { + valueOf () { return 'product' } + } + const engine = new Liquid() + const ctx = { template: new TemplateDrop() } + const html = await engine.parseAndRender(`{% if template contains "product" %}contains{%endif%}`, ctx) + expect(html).to.equal('contains') + }) }) diff --git a/test/unit/render/expression.ts b/test/unit/render/expression.ts index 6f497aae3..405feb4e0 100644 --- a/test/unit/render/expression.ts +++ b/test/unit/render/expression.ts @@ -1,5 +1,6 @@ import { Tokenizer } from '../../../src/parser/tokenizer' import { expect } from 'chai' +import { Drop } from '../../../src/drop/drop' import { Context } from '../../../src/context/context' import { toThenable } from '../../../src/util/async' import { defaultOperators } from '../../../src/render/operator' @@ -130,6 +131,13 @@ describe('Expression', function () { it('should support < or contains', async function () { expect(await toThenable(create('1 < 2 or x contains "x"').evaluate(ctx, false))).to.equal(true) }) + it('should support Drops for "x contains "x""', async () => { + class TemplateDrop extends Drop { + valueOf () { return 'X' } + } + const ctx = new Context({ x: 'XXX', X: new TemplateDrop() }) + expect(await toThenable(create('x contains X').evaluate(ctx, false))).to.equal(true) + }) it('should support value and !=', async function () { const ctx = new Context({ empty: '' }) expect(await toThenable(create('empty and empty != ""').evaluate(ctx, false))).to.equal(false)