2021-12-23 14:17:20 +00:00
|
|
|
import evaluate, {
|
|
|
|
|
setupEvaluationEnvironment,
|
|
|
|
|
evaluateAsync,
|
|
|
|
|
isFunctionAsync,
|
|
|
|
|
} from "workers/evaluate";
|
2021-03-13 14:12:21 +00:00
|
|
|
import {
|
|
|
|
|
DataTree,
|
|
|
|
|
DataTreeWidget,
|
|
|
|
|
ENTITY_TYPE,
|
|
|
|
|
} from "entities/DataTree/dataTreeFactory";
|
2021-09-09 15:10:22 +00:00
|
|
|
import { RenderModes } from "constants/WidgetConstants";
|
2021-03-13 14:12:21 +00:00
|
|
|
|
2021-12-23 14:17:20 +00:00
|
|
|
describe("evaluateSync", () => {
|
2021-03-13 14:12:21 +00:00
|
|
|
const widget: DataTreeWidget = {
|
|
|
|
|
bottomRow: 0,
|
|
|
|
|
isLoading: false,
|
|
|
|
|
leftColumn: 0,
|
|
|
|
|
parentColumnSpace: 0,
|
|
|
|
|
parentRowSpace: 0,
|
|
|
|
|
renderMode: RenderModes.CANVAS,
|
|
|
|
|
rightColumn: 0,
|
|
|
|
|
topRow: 0,
|
2022-01-18 07:52:24 +00:00
|
|
|
type: "INPUT_WIDGET_V2",
|
2021-03-13 14:12:21 +00:00
|
|
|
version: 0,
|
|
|
|
|
widgetId: "",
|
|
|
|
|
widgetName: "",
|
|
|
|
|
text: "value",
|
|
|
|
|
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
|
|
|
|
bindingPaths: {},
|
|
|
|
|
triggerPaths: {},
|
2021-04-21 14:34:25 +00:00
|
|
|
validationPaths: {},
|
2021-06-21 11:09:51 +00:00
|
|
|
logBlackList: {},
|
2022-02-04 12:28:46 +00:00
|
|
|
overridingPropertyPaths: {},
|
|
|
|
|
privateWidgets: {},
|
|
|
|
|
propertyOverrideDependency: {},
|
2021-03-13 14:12:21 +00:00
|
|
|
};
|
|
|
|
|
const dataTree: DataTree = {
|
|
|
|
|
Input1: widget,
|
|
|
|
|
};
|
2021-10-11 12:55:03 +00:00
|
|
|
beforeAll(() => {
|
|
|
|
|
setupEvaluationEnvironment();
|
|
|
|
|
});
|
2021-03-13 14:12:21 +00:00
|
|
|
it("unescapes string before evaluation", () => {
|
|
|
|
|
const js = '\\"Hello!\\"';
|
2022-02-11 10:52:27 +00:00
|
|
|
const response = evaluate(js, {}, {}, false);
|
2021-03-13 14:12:21 +00:00
|
|
|
expect(response.result).toBe("Hello!");
|
|
|
|
|
});
|
2021-11-10 07:11:23 +00:00
|
|
|
it("evaluate string post unescape in v1", () => {
|
|
|
|
|
const js = '[1, 2, 3].join("\\\\n")';
|
2022-02-11 10:52:27 +00:00
|
|
|
const response = evaluate(js, {}, {}, false);
|
2021-11-10 07:11:23 +00:00
|
|
|
expect(response.result).toBe("1\n2\n3");
|
|
|
|
|
});
|
|
|
|
|
it("evaluate string without unescape in v2", () => {
|
|
|
|
|
self.evaluationVersion = 2;
|
|
|
|
|
const js = '[1, 2, 3].join("\\n")';
|
2022-02-11 10:52:27 +00:00
|
|
|
const response = evaluate(js, {}, {}, false);
|
2021-11-10 07:11:23 +00:00
|
|
|
expect(response.result).toBe("1\n2\n3");
|
|
|
|
|
});
|
2021-03-13 14:12:21 +00:00
|
|
|
it("throws error for undefined js", () => {
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
expect(() => evaluate(undefined, {})).toThrow(TypeError);
|
|
|
|
|
});
|
2021-06-21 11:09:51 +00:00
|
|
|
it("Returns for syntax errors", () => {
|
2022-02-11 10:52:27 +00:00
|
|
|
const response1 = evaluate("wrongJS", {}, {}, false);
|
2021-06-21 11:09:51 +00:00
|
|
|
expect(response1).toStrictEqual({
|
|
|
|
|
result: undefined,
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
2021-09-17 10:31:45 +00:00
|
|
|
ch: 1,
|
2021-08-26 04:45:17 +00:00
|
|
|
code: "W117",
|
2021-06-21 11:09:51 +00:00
|
|
|
errorMessage: "'wrongJS' is not defined.",
|
2021-07-15 04:59:04 +00:00
|
|
|
errorSegment: " const result = wrongJS",
|
2021-06-21 11:09:51 +00:00
|
|
|
errorType: "LINT",
|
2021-09-17 10:31:45 +00:00
|
|
|
line: 0,
|
2021-07-15 04:59:04 +00:00
|
|
|
raw: `
|
|
|
|
|
function closedFunction () {
|
|
|
|
|
const result = wrongJS
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2022-02-04 12:28:46 +00:00
|
|
|
closedFunction.call(THIS_CONTEXT)
|
2021-07-15 04:59:04 +00:00
|
|
|
`,
|
2021-09-17 10:31:45 +00:00
|
|
|
severity: "error",
|
2021-08-26 04:45:17 +00:00
|
|
|
originalBinding: "wrongJS",
|
|
|
|
|
variables: ["wrongJS", undefined, undefined, undefined],
|
2021-06-21 11:09:51 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
errorMessage: "ReferenceError: wrongJS is not defined",
|
|
|
|
|
errorType: "PARSE",
|
2021-07-15 04:59:04 +00:00
|
|
|
raw: `
|
|
|
|
|
function closedFunction () {
|
|
|
|
|
const result = wrongJS
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2022-02-04 12:28:46 +00:00
|
|
|
closedFunction.call(THIS_CONTEXT)
|
2021-07-15 04:59:04 +00:00
|
|
|
`,
|
2021-06-21 11:09:51 +00:00
|
|
|
severity: "error",
|
2021-08-26 04:45:17 +00:00
|
|
|
originalBinding: "wrongJS",
|
2021-06-21 11:09:51 +00:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
2022-02-11 10:52:27 +00:00
|
|
|
const response2 = evaluate("{}.map()", {}, {}, false);
|
2021-06-21 11:09:51 +00:00
|
|
|
expect(response2).toStrictEqual({
|
|
|
|
|
result: undefined,
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
|
|
|
|
errorMessage: "TypeError: {}.map is not a function",
|
|
|
|
|
errorType: "PARSE",
|
2021-07-15 04:59:04 +00:00
|
|
|
raw: `
|
|
|
|
|
function closedFunction () {
|
|
|
|
|
const result = {}.map()
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2022-02-04 12:28:46 +00:00
|
|
|
closedFunction.call(THIS_CONTEXT)
|
2021-07-15 04:59:04 +00:00
|
|
|
`,
|
2021-06-21 11:09:51 +00:00
|
|
|
severity: "error",
|
2021-08-26 04:45:17 +00:00
|
|
|
originalBinding: "{}.map()",
|
2021-06-21 11:09:51 +00:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
2021-03-13 14:12:21 +00:00
|
|
|
});
|
|
|
|
|
it("evaluates value from data tree", () => {
|
|
|
|
|
const js = "Input1.text";
|
2022-02-11 10:52:27 +00:00
|
|
|
const response = evaluate(js, dataTree, {}, false);
|
2021-03-13 14:12:21 +00:00
|
|
|
expect(response.result).toBe("value");
|
|
|
|
|
});
|
|
|
|
|
it("disallows unsafe function calls", () => {
|
|
|
|
|
const js = "setTimeout(() => {}, 100)";
|
2022-02-11 10:52:27 +00:00
|
|
|
const response = evaluate(js, dataTree, {}, false);
|
2021-06-21 11:09:51 +00:00
|
|
|
expect(response).toStrictEqual({
|
|
|
|
|
result: undefined,
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
|
|
|
|
errorMessage: "TypeError: setTimeout is not a function",
|
|
|
|
|
errorType: "PARSE",
|
2021-07-15 04:59:04 +00:00
|
|
|
raw: `
|
|
|
|
|
function closedFunction () {
|
|
|
|
|
const result = setTimeout(() => {}, 100)
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2022-02-04 12:28:46 +00:00
|
|
|
closedFunction.call(THIS_CONTEXT)
|
2021-07-15 04:59:04 +00:00
|
|
|
`,
|
2021-06-21 11:09:51 +00:00
|
|
|
severity: "error",
|
2021-08-26 04:45:17 +00:00
|
|
|
originalBinding: "setTimeout(() => {}, 100)",
|
2021-06-21 11:09:51 +00:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
2021-03-13 14:12:21 +00:00
|
|
|
});
|
|
|
|
|
it("has access to extra library functions", () => {
|
|
|
|
|
const js = "_.add(1,2)";
|
2022-02-11 10:52:27 +00:00
|
|
|
const response = evaluate(js, dataTree, {}, false);
|
2021-03-13 14:12:21 +00:00
|
|
|
expect(response.result).toBe(3);
|
|
|
|
|
});
|
|
|
|
|
it("evaluates functions with callback data", () => {
|
|
|
|
|
const js = "(arg1, arg2) => arg1.value + arg2";
|
|
|
|
|
const callbackData = [{ value: "test" }, "1"];
|
2022-02-11 10:52:27 +00:00
|
|
|
const response = evaluate(js, dataTree, {}, false, {}, callbackData);
|
2021-03-13 14:12:21 +00:00
|
|
|
expect(response.result).toBe("test1");
|
|
|
|
|
});
|
2021-12-21 14:30:19 +00:00
|
|
|
it("handles EXPRESSIONS with new lines", () => {
|
|
|
|
|
let js = "\n";
|
2022-02-11 10:52:27 +00:00
|
|
|
let response = evaluate(js, dataTree, {}, false);
|
2021-12-21 14:30:19 +00:00
|
|
|
expect(response.errors.length).toBe(0);
|
|
|
|
|
|
|
|
|
|
js = "\n\n\n";
|
2022-02-11 10:52:27 +00:00
|
|
|
response = evaluate(js, dataTree, {}, false);
|
2021-12-21 14:30:19 +00:00
|
|
|
expect(response.errors.length).toBe(0);
|
|
|
|
|
});
|
|
|
|
|
it("handles TRIGGERS with new lines", () => {
|
|
|
|
|
let js = "\n";
|
2022-02-11 10:52:27 +00:00
|
|
|
let response = evaluate(js, dataTree, {}, false, undefined, undefined);
|
2021-12-21 14:30:19 +00:00
|
|
|
expect(response.errors.length).toBe(0);
|
|
|
|
|
|
|
|
|
|
js = "\n\n\n";
|
2022-02-11 10:52:27 +00:00
|
|
|
response = evaluate(js, dataTree, {}, false, undefined, undefined);
|
2021-12-21 14:30:19 +00:00
|
|
|
expect(response.errors.length).toBe(0);
|
|
|
|
|
});
|
|
|
|
|
it("handles ANONYMOUS_FUNCTION with new lines", () => {
|
|
|
|
|
let js = "\n";
|
2022-02-11 10:52:27 +00:00
|
|
|
let response = evaluate(js, dataTree, {}, false, undefined, undefined);
|
2021-12-21 14:30:19 +00:00
|
|
|
expect(response.errors.length).toBe(0);
|
|
|
|
|
|
|
|
|
|
js = "\n\n\n";
|
2022-02-11 10:52:27 +00:00
|
|
|
response = evaluate(js, dataTree, {}, false, undefined, undefined);
|
2021-12-21 14:30:19 +00:00
|
|
|
expect(response.errors.length).toBe(0);
|
|
|
|
|
});
|
2022-02-04 12:28:46 +00:00
|
|
|
it("has access to this context", () => {
|
|
|
|
|
const js = "this.contextVariable";
|
|
|
|
|
const thisContext = { contextVariable: "test" };
|
2022-02-11 10:52:27 +00:00
|
|
|
const response = evaluate(js, dataTree, {}, false, { thisContext });
|
2022-02-04 12:28:46 +00:00
|
|
|
expect(response.result).toBe("test");
|
|
|
|
|
// there should not be any error when accessing "this" variables
|
|
|
|
|
expect(response.errors).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("has access to additional global context", () => {
|
|
|
|
|
const js = "contextVariable";
|
|
|
|
|
const globalContext = { contextVariable: "test" };
|
2022-02-11 10:52:27 +00:00
|
|
|
const response = evaluate(js, dataTree, {}, false, { globalContext });
|
2022-02-04 12:28:46 +00:00
|
|
|
expect(response.result).toBe("test");
|
|
|
|
|
expect(response.errors).toHaveLength(0);
|
|
|
|
|
});
|
2021-03-13 14:12:21 +00:00
|
|
|
});
|
2021-12-23 14:17:20 +00:00
|
|
|
|
|
|
|
|
describe("evaluateAsync", () => {
|
|
|
|
|
it("runs and completes", async () => {
|
|
|
|
|
const js = "(() => new Promise((resolve) => { resolve(123) }))()";
|
|
|
|
|
self.postMessage = jest.fn();
|
|
|
|
|
await evaluateAsync(js, {}, "TEST_REQUEST", {});
|
|
|
|
|
expect(self.postMessage).toBeCalledWith({
|
|
|
|
|
requestId: "TEST_REQUEST",
|
|
|
|
|
responseData: {
|
|
|
|
|
finished: true,
|
|
|
|
|
result: { errors: [], result: 123, triggers: [] },
|
|
|
|
|
},
|
|
|
|
|
type: "PROCESS_TRIGGER",
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
it("runs and returns errors", async () => {
|
|
|
|
|
jest.restoreAllMocks();
|
|
|
|
|
const js = "(() => new Promise((resolve) => { randomKeyword }))()";
|
|
|
|
|
self.postMessage = jest.fn();
|
|
|
|
|
await evaluateAsync(js, {}, "TEST_REQUEST_1", {});
|
|
|
|
|
expect(self.postMessage).toBeCalledWith({
|
|
|
|
|
requestId: "TEST_REQUEST_1",
|
|
|
|
|
responseData: {
|
|
|
|
|
finished: true,
|
|
|
|
|
result: {
|
|
|
|
|
errors: [
|
|
|
|
|
{
|
|
|
|
|
errorMessage: expect.stringContaining(
|
|
|
|
|
"randomKeyword is not defined",
|
|
|
|
|
),
|
|
|
|
|
errorType: "PARSE",
|
|
|
|
|
originalBinding: expect.stringContaining("Promise"),
|
|
|
|
|
raw: expect.stringContaining("Promise"),
|
|
|
|
|
severity: "error",
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
triggers: [],
|
|
|
|
|
result: undefined,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
type: "PROCESS_TRIGGER",
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("isFunctionAsync", () => {
|
|
|
|
|
it("identifies async functions", () => {
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
|
|
|
const cases: Array<{ script: Function | string; expected: boolean }> = [
|
|
|
|
|
{
|
|
|
|
|
script: () => {
|
|
|
|
|
return 1;
|
|
|
|
|
},
|
|
|
|
|
expected: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
script: () => {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
resolve(1);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
expected: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
script: "() => { showAlert('yo') }",
|
|
|
|
|
expected: true,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (const testCase of cases) {
|
|
|
|
|
let testFunc = testCase.script;
|
|
|
|
|
if (typeof testFunc === "string") {
|
|
|
|
|
testFunc = eval(testFunc);
|
|
|
|
|
}
|
2022-02-04 12:28:46 +00:00
|
|
|
const actual = isFunctionAsync(testFunc, {}, {});
|
2021-12-23 14:17:20 +00:00
|
|
|
expect(actual).toBe(testCase.expected);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|