UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

278 lines (227 loc) 8.94 kB
const { compileExpression, useDotAccessOperator, useOptionalChaining, useDotAccessOperatorAndOptionalChaining, } = require("../dist/cjs/filtrex"); const { describe, it } = require("mocha"); const chai = require("chai"); const assertArrays = require("chai-arrays"); chai.use(assertArrays); const { expect } = chai; const eval = (str, obj) => compileExpression(str)(obj); describe("Various other things", () => { it("in / not in", () => { // value in array expect(eval("5 in (1, 2, 3, 4)")).equals(false); expect(eval("3 in (1, 2, 3, 4)")).equals(true); expect(eval("5 not in (1, 2, 3, 4)")).equals(true); expect(eval("3 not in (1, 2, 3, 4)")).equals(false); // array in array expect(eval("(1, 2) in (1, 2, 3)")).equals(true); expect(eval("(1, 2) in (2, 3, 1)")).equals(true); expect(eval("(3, 4) in (1, 2, 3)")).equals(false); expect(eval("(1, 2) not in (1, 2, 3)")).equals(false); expect(eval("(1, 2) not in (2, 3, 1)")).equals(false); expect(eval("(3, 4) not in (1, 2, 3)")).equals(true); // other edge cases expect(eval("(1, 2) in 1")).equals(false); expect(eval("1 in 1")).equals(true); expect(eval("(1, 2) not in 1")).equals(true); expect(eval("1 not in 1")).equals(false); }); it("string support", () => { expect(eval('foo == "hello"', { foo: "hello" })).equals(true); expect(eval('foo == "hello"', { foo: "bye" })).equals(false); expect(eval('foo != "hello"', { foo: "hello" })).equals(false); expect(eval('foo != "hello"', { foo: "bye" })).equals(true); expect(eval('foo in ("aa", "bb")', { foo: "aa" })).equals(true); expect(eval('foo in ("aa", "bb")', { foo: "cc" })).equals(false); expect(eval('foo not in ("aa", "bb")', { foo: "aa" })).equals(false); expect(eval('foo not in ("aa", "bb")', { foo: "cc" })).equals(true); expect(eval(`"\n"`)).equals("\n"); expect(eval(`"\u0000"`)).equals("\u0000"); expect(eval(`"\uD800"`)).equals("\uD800"); }); it("regexp support", () => { expect(eval('foo ~= "^[hH]ello"', { foo: "hello" })).equals(true); expect(eval('foo ~= "^[hH]ello"', { foo: "bye" })).equals(false); }); it("array support", () => { const arr = eval('(42, "fifty", pi)', { pi: Math.PI }); expect(arr).is.array(); expect(arr).to.be.equalTo([42, "fifty", Math.PI]); }); it("ternary operator", () => { expect(eval("if 1 > 2 then 3 else 4")).equals(4); expect(eval("if 1 < 2 then 3 else 4")).equals(3); expect( eval( "if 1 < 2 then if 3 < 4 then 42 else 420 else if 5 < 6 then 69 else -1/12", ), ).equals(42); expect( eval( "if 1 < 2 then if 3 > 4 then 42 else 420 else if 5 < 6 then 69 else -1/12", ), ).equals(420); expect( eval( "if 1 > 2 then if 3 < 4 then 42 else 420 else if 5 < 6 then 69 else -1/12", ), ).equals(69); expect( eval( "if 1 > 2 then if 3 < 4 then 42 else 420 else if 5 > 6 then 69 else -1/12", ), ).equals(-1 / 12); }); it("kitchensink", () => { var kitchenSink = compileExpression( "if 4 > lowNumber * 2 and (max(a, b) < 20 or foo) then 1.1 else 9.4", ); expect(kitchenSink({ lowNumber: 1.5, a: 10, b: 12, foo: false })).equals( 1.1, ); expect(kitchenSink({ lowNumber: 3.5, a: 10, b: 12, foo: false })).equals( 9.4, ); }); it("custom functions", () => { let triple = (x) => x * 3; let options = { extraFunctions: { triple } }; expect(compileExpression("triple(v)", options)({ v: 7 })).equals(21); }); it("custom property function basics", () => { expect( compileExpression("a", { customProp: (name) => name === "a" })(), ).equals(true); expect( compileExpression("a + bb + ccc", { customProp: (name) => name.length, })(), ).equals(6); expect( compileExpression("a + b * c", { customProp: (name, get) => get(name) })({ a: 1, b: 2, c: 3, }), ).equals(7); expect( compileExpression("a", { customProp: (name, get) => get(name) })({ a: true, }), ).equals(true); let object = { a: 2 }; expect( compileExpression("a", { customProp: (_, __, obj) => obj === object })( object, ), ).equals(true); }); it("custom property function text search", () => { let textToSearch = "able was i ere I saw elba\nthe Rain in spain falls MAINLY on the plain"; let doesTextMatch = (name) => textToSearch.indexOf(name) !== -1; let evalProp = (exp) => compileExpression(exp, { customProp: doesTextMatch })(); expect(evalProp("able and was and i")).equals(true); expect(evalProp("able and was and dog")).equals(false); expect(evalProp("able or dog")).equals(true); expect(evalProp("able")).equals(true); expect(evalProp("Rain and (missing or MAINLY)")).equals(true); expect(evalProp("NotThere or missing or falls and plain")).equals(true); }); it("custom property function proxy", () => { let prefixedName = (str, sub) => str.substr(0, sub.length) === sub && str.substr(sub.length); let tripleName = (str) => prefixedName(str, "triple_"); let proxy = (name, get) => tripleName(name) ? 3 * get(tripleName(name)) : get(name); let evalProp = (exp) => compileExpression(exp, { customProp: proxy })({ a: 1, b: 2, c: 3 }); expect(evalProp("a")).equals(1); expect(evalProp("triple_a")).equals(3); expect(evalProp("a + triple_b * c")).equals(19); }); it("throws when using old API", () => { let extraFunctions = { myCustomFunc: (n) => n * n }; let customProp = () => "foo"; expect(() => compileExpression("", extraFunctions)).throws(); expect(() => compileExpression("", {}, customProp)).throws(); expect(() => compileExpression("", extraFunctions, customProp)).throws(); }); it("doesn't recognise non-callable values as extra functions", () => { let options = { extraFunctions: { sqrt: undefined, a: 42, b: {} } }; let eval = (str) => compileExpression(str, options)(); expect(eval("a()")).is.instanceOf(ReferenceError); expect(eval("b()")).is.instanceOf(ReferenceError); let err = eval("sqrt(4)"); expect(err).is.instanceOf(ReferenceError); expect(err.message).equals("Unknown function: sqrt()"); }); it('gives the correct precedence to "in" and "not in"', () => { expect(eval("4 + 3 in (7, 8)")).equals(true); expect(eval("4 + 3 in (6, 8)")).equals(false); expect(eval("4 + 3 not in (7, 8)")).equals(false); expect(eval("4 + 3 not in (6, 8)")).equals(true); }); it("constants basics", () => { const options = { constants: { pi: Math.PI, true: true, false: false } }; expect(compileExpression("2 * pi * radius", options)({ radius: 6 })).equals( 2 * Math.PI * 6, ); expect( compileExpression("not true == false and not false == true", options)(), ).equals(true); expect(compileExpression("pi", options)({ pi: 3 })).equals(Math.PI); expect(compileExpression(`'pi'`, options)({ pi: 3 })).equals(3); const options2 = { constants: { a: "a_const " } }; const data = { a: "a_data ", b: "b_data " }; const expr = `'a' + a + 'b' + b`; expect(compileExpression(expr, options2)(data)).equals( "a_data a_const b_data b_data ", ); }); it("deprecated syntax still works", () => { expect(eval("10 % 2")).equals(0); expect(eval("11 % 2")).equals(1); expect(eval("-1 % 2")).equals(1); expect(eval("-0.1 % 5")).equals(4.9); expect(eval("1 < 2 ? 3 < 4 ? 42 : 420 : 5 < 6 ? 69 : -1/12")).equals(42); expect(eval("1 < 2 ? 3 > 4 ? 42 : 420 : 5 < 6 ? 69 : -1/12")).equals(420); expect(eval("1 > 2 ? 3 < 4 ? 42 : 420 : 5 < 6 ? 69 : -1/12")).equals(69); expect(eval("1 > 2 ? 3 < 4 ? 42 : 420 : 5 > 6 ? 69 : -1/12")).equals( -1 / 12, ); }); it("useDotAccessOperator works", () => { const expr = "foo.bar"; const fn = compileExpression(expr, { customProp: useDotAccessOperator, }); expect(fn({ foo: { bar: 42 } })).equals(42); }); it("useOptionalChaining work", () => { const expr = "bar of foo"; const fn = compileExpression(expr, { customProp: useOptionalChaining, }); expect(fn({ foo: null })).equals(null); expect(fn({ foo: { bar: 42 } })).equals(42); }); it("useDotAccessOperatorAndOptionalChaining works", () => { const expr1 = "foo.bar"; const expr2 = "bar of foo"; const options = { customProp: useDotAccessOperatorAndOptionalChaining, }; const fn1 = compileExpression(expr1, options); const fn2 = compileExpression(expr2, options); expect(fn1({ foo: null })).equals(null); expect(fn1({ foo: { bar: 42 } })).equals(42); expect(fn2({ foo: null })).equals(null); expect(fn2({ foo: { bar: 42 } })).equals(42); }); });