Merge branch 'js-binding-drawer' of github.com:Budibase/budibase into feature/query-transformers
This commit is contained in:
commit
80dfd5bd6f
|
@ -27,19 +27,39 @@ const getContextValue = (path, context) => {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Node polyfill for base64 encoding
|
||||||
|
const btoa = plainText => {
|
||||||
|
return Buffer.from(plainText, "utf-8").toString("base64")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node polyfill for base64 decoding
|
||||||
|
const atob = base64 => {
|
||||||
|
return Buffer.from(base64, "base64").toString("utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
// Evaluates JS code against a certain context
|
// Evaluates JS code against a certain context
|
||||||
module.exports.processJS = (handlebars, context) => {
|
module.exports.processJS = (handlebars, context) => {
|
||||||
|
// Do not evaluate JS in a node environment
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
return "JS bindings are not executed in a Node environment"
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Wrap JS in a function and immediately invoke it.
|
// Wrap JS in a function and immediately invoke it.
|
||||||
// This is required to allow the final `return` statement to be valid.
|
// This is required to allow the final `return` statement to be valid.
|
||||||
const js = `function run(){${atob(handlebars)}};run();`
|
const js = `function run(){${atob(handlebars)}};run();`
|
||||||
|
|
||||||
// Our $ context function gets a value from context
|
// Our $ context function gets a value from context
|
||||||
const sandboxContext = { $: path => getContextValue(path, context) }
|
const sandboxContext = {
|
||||||
|
$: path => getContextValue(path, context),
|
||||||
|
alert: undefined,
|
||||||
|
setInterval: undefined,
|
||||||
|
setTimeout: undefined,
|
||||||
|
}
|
||||||
|
|
||||||
// Create a sandbox with out context and run the JS
|
// Create a sandbox with out context and run the JS
|
||||||
vm.createContext(sandboxContext)
|
vm.createContext(sandboxContext)
|
||||||
return vm.runInNewContext(js, sandboxContext)
|
return vm.runInNewContext(js, sandboxContext, { timeout: 1000 })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return "Error while executing JS"
|
return "Error while executing JS"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
const { processStringSync, encodeJSBinding } = require("../src/index.cjs")
|
||||||
|
|
||||||
|
const processJS = (js, context) => {
|
||||||
|
return processStringSync(encodeJSBinding(js), context)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Test the JavaScript helper in Node", () => {
|
||||||
|
it("should not execute JS in Node", () => {
|
||||||
|
const output = processJS(`return 1`)
|
||||||
|
expect(output).toBe("JS bindings are not executed in a Node environment")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Test the JavaScript helper", () => {
|
||||||
|
// JS bindings do not get evaluated on the server for safety.
|
||||||
|
// Since we want to run SJ for tests, we fake a window object to make
|
||||||
|
// it think that we're in the browser
|
||||||
|
beforeEach(() => {
|
||||||
|
window = {}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should execute a simple expression", () => {
|
||||||
|
const output = processJS(`return 1 + 2`)
|
||||||
|
expect(output).toBe("3")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to use primitive bindings", () => {
|
||||||
|
const output = processJS(`return $("foo")`, {
|
||||||
|
foo: "bar",
|
||||||
|
})
|
||||||
|
expect(output).toBe("bar")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to use an object binding", () => {
|
||||||
|
const output = processJS(`return $("foo").bar`, {
|
||||||
|
foo: {
|
||||||
|
bar: "baz",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(output).toBe("baz")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to use a complex object binding", () => {
|
||||||
|
const output = processJS(`return $("foo").bar[0].baz`, {
|
||||||
|
foo: {
|
||||||
|
bar: [
|
||||||
|
{
|
||||||
|
baz: "shazbat",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(output).toBe("shazbat")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to use a deep binding", () => {
|
||||||
|
const output = processJS(`return $("foo.bar.baz")`, {
|
||||||
|
foo: {
|
||||||
|
bar: {
|
||||||
|
baz: "shazbat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(output).toBe("shazbat")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to use a deep array binding", () => {
|
||||||
|
const output = processJS(`return $("foo.0.bar")`, {
|
||||||
|
foo: [
|
||||||
|
{
|
||||||
|
bar: "baz",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
expect(output).toBe("baz")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle errors", () => {
|
||||||
|
const output = processJS(`throw "Error"`)
|
||||||
|
expect(output).toBe("Error while executing JS")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should timeout after one second", () => {
|
||||||
|
const output = processJS(`while (true) {}`)
|
||||||
|
expect(output).toBe("Error while executing JS")
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue