const { describe, it, expect } = require("@jest/globals"); const { evaluateJsExpression, internalExpressionCache: cache, createResponseParser } = require("../src/utils"); describe("utils", () => { describe("expression evaluation", () => { const context = { res: { data: { pets: ["bruno", "max"] } } }; beforeEach(() => cache.clear()); afterEach(() => cache.clear()); it("should evaluate expression", () => { let result; result = evaluateJsExpression("res.data.pets", context); expect(result).toEqual(["bruno", "max"]); result = evaluateJsExpression("res.data.pets[0].toUpperCase()", context); expect(result).toEqual("BRUNO"); }); it("should cache expression", () => { expect(cache.size).toBe(0); evaluateJsExpression("res.data.pets", context); expect(cache.size).toBe(1); }); it("should use cached expression", () => { const expr = "res.data.pets"; evaluateJsExpression(expr, context); const fn = cache.get(expr); expect(fn).toBeDefined(); evaluateJsExpression(expr, context); // cache should not be overwritten expect(cache.get(expr)).toBe(fn); }); it("should identify top level variables", () => { const expr = "res.data.pets[0].toUpperCase()"; evaluateJsExpression(expr, context); expect(cache.get(expr).toString()).toContain("let { res } = context;"); }); it("should not duplicate variables", () => { const expr = "res.data.pets[0] + res.data.pets[1]"; evaluateJsExpression(expr, context); expect(cache.get(expr).toString()).toContain("let { res } = context;"); }); it("should exclude js keywords like true false from vars", () => { const expr = "res.data.pets.length > 0 ? true : false"; evaluateJsExpression(expr, context); expect(cache.get(expr).toString()).toContain("let { res } = context;"); }); it("should exclude numbers from vars", () => { const expr = "res.data.pets.length + 10"; evaluateJsExpression(expr, context); expect(cache.get(expr).toString()).toContain("let { res } = context;"); }); it("should pick variables from complex expressions", () => { const expr = "res.data.pets.map(pet => pet.length)"; const result = evaluateJsExpression(expr, context); expect(result).toEqual([5, 3]); expect(cache.get(expr).toString()).toContain("let { res, pet } = context;"); }); it("should be ok picking extra vars from strings", () => { const expr = "'hello' + ' ' + res.data.pets[0]"; const result = evaluateJsExpression(expr, context); expect(result).toBe("hello bruno"); // extra var hello is harmless expect(cache.get(expr).toString()).toContain("let { hello, res } = context;"); }); it("should evaluate expressions referencing globals", () => { const startTime = new Date("2022-02-01").getTime(); const currentTime = new Date("2022-02-02").getTime(); jest.useFakeTimers({ now: currentTime }); const expr = "Math.max(Date.now(), startTime)"; const result = evaluateJsExpression(expr, { startTime }); expect(result).toBe(currentTime); expect(cache.get(expr).toString()).toContain("Math = Math ?? globalThis.Math;"); expect(cache.get(expr).toString()).toContain("Date = Date ?? globalThis.Date;"); }); it("should use global overridden in context", () => { const startTime = new Date("2022-02-01").getTime(); const currentTime = new Date("2022-02-02").getTime(); jest.useFakeTimers({ now: currentTime }); const context = { Date: { now: () => new Date("2022-01-31").getTime() }, startTime }; const expr = "Math.max(Date.now(), startTime)"; const result = evaluateJsExpression(expr, context); expect(result).toBe(startTime); }); }); describe("response parser", () => { const res = createResponseParser({ status: 200, data: { order: { items: [ { id: 1, amount: 10 }, { id: 2, amount: 20 } ] } } }); it("should default to bruno query", () => { const value = res("..items[?].amount[0]", i => i.amount > 10); expect(value).toBe(20); }); it("should allow json-query", () => { const value = res.jq("order.items[amount > 10].amount"); expect(value).toBe(20); }); }); });