diff --git a/packages/server/package.json b/packages/server/package.json index 99763109f3..ec37ed0605 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -55,9 +55,8 @@ "!src/db/views/*.js", "!src/api/routes/tests/**/*.js", "!src/api/controllers/deploy/**/*.js", - "!src/api/controllers/static/templates/**/*", - "!src/api/controllers/static/selfhost/**/*", - "!src/*.js" + "!src/*.js", + "!src/api/controllers/static/**/*" ], "coverageReporters": [ "lcov", diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 9d11219506..5654c14c17 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -3,8 +3,8 @@ const { getAllTableRows, clearAllAutomations, } = require("./utilities/TestFunctions") -const { basicAutomation } = require("./utilities/structures") const setup = require("./utilities") +const { basicAutomation } = setup.structures const MAX_RETRIES = 4 diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js index ee1a1c47f5..c1448894b1 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.js +++ b/packages/server/src/api/routes/tests/datasource.spec.js @@ -1,6 +1,6 @@ -let {basicDatasource} = require("./utilities/structures") -let {checkBuilderEndpoint} = require("./utilities/TestFunctions") let setup = require("./utilities") +let { basicDatasource } = setup.structures +let { checkBuilderEndpoint } = require("./utilities/TestFunctions") describe("/datasources", () => { let request = setup.getRequest() diff --git a/packages/server/src/api/routes/tests/layout.spec.js b/packages/server/src/api/routes/tests/layout.spec.js index 6b21554d71..4842b2cc8e 100644 --- a/packages/server/src/api/routes/tests/layout.spec.js +++ b/packages/server/src/api/routes/tests/layout.spec.js @@ -1,6 +1,6 @@ const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const setup = require("./utilities") -const { basicLayout } = require("./utilities/structures") +const { basicLayout } = setup.structures describe("/layouts", () => { let request = setup.getRequest() diff --git a/packages/server/src/api/routes/tests/permissions.spec.js b/packages/server/src/api/routes/tests/permissions.spec.js index b24fac57c0..aab5567881 100644 --- a/packages/server/src/api/routes/tests/permissions.spec.js +++ b/packages/server/src/api/routes/tests/permissions.spec.js @@ -1,6 +1,6 @@ const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const setup = require("./utilities") -const { basicRow } = require("./utilities/structures") +const { basicRow } = setup.structures const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index aa0e5428c5..87938c6a37 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -1,9 +1,9 @@ // mock out postgres for this jest.mock("pg") -const { checkBuilderEndpoint } = require("./utilities/TestFunctions") -const { basicQuery, basicDatasource } = require("./utilities/structures") const setup = require("./utilities") +const { checkBuilderEndpoint } = require("./utilities/TestFunctions") +const { basicQuery, basicDatasource } = setup.structures describe("/queries", () => { let request = setup.getRequest() diff --git a/packages/server/src/api/routes/tests/role.spec.js b/packages/server/src/api/routes/tests/role.spec.js index 9bb38b295a..062450cf63 100644 --- a/packages/server/src/api/routes/tests/role.spec.js +++ b/packages/server/src/api/routes/tests/role.spec.js @@ -2,8 +2,8 @@ const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_PERMISSION_IDS, } = require("../../../utilities/security/permissions") -const { basicRole } = require("./utilities/structures") const setup = require("./utilities") +const { basicRole } = setup.structures describe("/roles", () => { let request = setup.getRequest() diff --git a/packages/server/src/api/routes/tests/routing.spec.js b/packages/server/src/api/routes/tests/routing.spec.js index 70d1632bf3..beb1659b2a 100644 --- a/packages/server/src/api/routes/tests/routing.spec.js +++ b/packages/server/src/api/routes/tests/routing.spec.js @@ -1,5 +1,5 @@ const setup = require("./utilities") -const { basicScreen } = require("./utilities/structures") +const { basicScreen } = setup.structures const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index 1442e4eb75..f597db0cc0 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -1,7 +1,6 @@ const { outputProcessing } = require("../../../utilities/rowProcessor") -const env = require("../../../environment") -const { basicRow } = require("./utilities/structures") const setup = require("./utilities") +const { basicRow } = setup.structures describe("/rows", () => { let request = setup.getRequest() diff --git a/packages/server/src/api/routes/tests/screen.spec.js b/packages/server/src/api/routes/tests/screen.spec.js index ae30afd29c..5533bc5e59 100644 --- a/packages/server/src/api/routes/tests/screen.spec.js +++ b/packages/server/src/api/routes/tests/screen.spec.js @@ -1,6 +1,6 @@ const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const setup = require("./utilities") -const { basicScreen } = require("./utilities/structures") +const { basicScreen } = setup.structures describe("/screens", () => { let request = setup.getRequest() diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index 5e7ec9e9d4..808f1a2622 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -1,7 +1,7 @@ const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { checkPermissionsEndpoint } = require("./utilities/TestFunctions") -const { basicUser } = require("./utilities/structures") const setup = require("./utilities") +const { basicUser } = setup.structures describe("/users", () => { let request = setup.getRequest() diff --git a/packages/server/src/api/routes/tests/utilities/controllers.js b/packages/server/src/api/routes/tests/utilities/controllers.js deleted file mode 100644 index a4eb9ac9de..0000000000 --- a/packages/server/src/api/routes/tests/utilities/controllers.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - table: require("../../../controllers/table"), - row: require("../../../controllers/row"), - role: require("../../../controllers/role"), - perms: require("../../../controllers/permission"), - view: require("../../../controllers/view"), - app: require("../../../controllers/application"), - user: require("../../../controllers/user"), - automation: require("../../../controllers/automation"), - datasource: require("../../../controllers/datasource"), - query: require("../../../controllers/query"), - screen: require("../../../controllers/screen"), - webhook: require("../../../controllers/webhook"), - layout: require("../../../controllers/layout"), -} diff --git a/packages/server/src/api/routes/tests/utilities/index.js b/packages/server/src/api/routes/tests/utilities/index.js index 7126f141e2..ed5c98cc48 100644 --- a/packages/server/src/api/routes/tests/utilities/index.js +++ b/packages/server/src/api/routes/tests/utilities/index.js @@ -1,4 +1,5 @@ -const TestConfig = require("./TestConfiguration") +const TestConfig = require("../../../../tests/utilities/TestConfiguration") +const structures = require("../../../../tests/utilities/structures") const env = require("../../../../environment") exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms)) @@ -51,3 +52,5 @@ exports.switchToCloudForFunction = async func => { throw error } } + +exports.structures = structures diff --git a/packages/server/src/api/routes/tests/webhook.spec.js b/packages/server/src/api/routes/tests/webhook.spec.js index 2bf5445a09..7fb7a26fc1 100644 --- a/packages/server/src/api/routes/tests/webhook.spec.js +++ b/packages/server/src/api/routes/tests/webhook.spec.js @@ -1,6 +1,6 @@ const setup = require("./utilities") const { checkBuilderEndpoint } = require("./utilities/TestFunctions") -const { basicWebhook, basicAutomation } = require("./utilities/structures") +const { basicWebhook, basicAutomation } = setup.structures describe("/webhooks", () => { let request = setup.getRequest() diff --git a/packages/server/src/automations/actions.js b/packages/server/src/automations/actions.js index ea88c2d1d6..37126c7ed4 100644 --- a/packages/server/src/automations/actions.js +++ b/packages/server/src/automations/actions.js @@ -37,10 +37,12 @@ let AUTOMATION_BUCKET = env.AUTOMATION_BUCKET let AUTOMATION_DIRECTORY = env.AUTOMATION_DIRECTORY let MANIFEST = null +/* instanbul ignore next */ function buildBundleName(pkgName, version) { return `${pkgName}@${version}.min.js` } +/* instanbul ignore next */ async function downloadPackage(name, version, bundleName) { await download( `${AUTOMATION_BUCKET}/${name}/${version}/${bundleName}`, @@ -96,5 +98,6 @@ module.exports.init = async function() { return MANIFEST } +// definitions will have downloaded ones added to it, while builtin won't module.exports.DEFINITIONS = BUILTIN_DEFINITIONS module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS diff --git a/packages/server/src/automations/steps/createRow.js b/packages/server/src/automations/steps/createRow.js index aeb75958f6..ef136e1131 100644 --- a/packages/server/src/automations/steps/createRow.js +++ b/packages/server/src/automations/steps/createRow.js @@ -59,15 +59,14 @@ module.exports.definition = { } module.exports.run = async function({ inputs, appId, apiKey, emitter }) { - // TODO: better logging of when actions are missed due to missing parameters if (inputs.row == null || inputs.row.tableId == null) { - return + return { + success: false, + response: { + message: "Invalid inputs", + }, + } } - inputs.row = await automationUtils.cleanUpRow( - appId, - inputs.row.tableId, - inputs.row - ) // have to clean up the row, remove the table from it const ctx = { params: { @@ -81,6 +80,11 @@ module.exports.run = async function({ inputs, appId, apiKey, emitter }) { } try { + inputs.row = await automationUtils.cleanUpRow( + appId, + inputs.row.tableId, + inputs.row + ) if (env.CLOUD) { await usage.update(apiKey, usage.Properties.ROW, 1) } diff --git a/packages/server/src/automations/steps/filter.js b/packages/server/src/automations/steps/filter.js index 4286cd44e8..586e424cc4 100644 --- a/packages/server/src/automations/steps/filter.js +++ b/packages/server/src/automations/steps/filter.js @@ -12,6 +12,9 @@ const PrettyLogicConditions = { [LogicConditions.LESS_THAN]: "Less than", } +module.exports.LogicConditions = LogicConditions +module.exports.PrettyLogicConditions = PrettyLogicConditions + module.exports.definition = { name: "Filter", tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}", @@ -64,7 +67,7 @@ module.exports.run = async function filter({ inputs }) { value = Date.parse(value) field = Date.parse(field) } - let success + let success = false if (typeof field !== "object" && typeof value !== "object") { switch (condition) { case LogicConditions.EQUAL: @@ -79,8 +82,6 @@ module.exports.run = async function filter({ inputs }) { case LogicConditions.LESS_THAN: success = field < value break - default: - return } } else { success = false diff --git a/packages/server/src/automations/tests/createRow.spec.js b/packages/server/src/automations/tests/createRow.spec.js new file mode 100644 index 0000000000..0be2803e47 --- /dev/null +++ b/packages/server/src/automations/tests/createRow.spec.js @@ -0,0 +1,57 @@ +const usageQuota = require("../../utilities/usageQuota") +const env = require("../../environment") +const setup = require("./utilities") + +jest.mock("../../utilities/usageQuota") + +describe("test the create row action", () => { + let table, row + let config = setup.getConfig() + + beforeEach(async () => { + await config.init() + table = await config.createTable() + row = { + tableId: table._id, + name: "test", + description: "test", + } + }) + + afterAll(setup.afterAll) + + it("should be able to run the action", async () => { + const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, { + row, + }) + expect(res.id).toBeDefined() + expect(res.revision).toBeDefined() + const gottenRow = await config.getRow(table._id, res.id) + expect(gottenRow.name).toEqual("test") + expect(gottenRow.description).toEqual("test") + }) + + it("should return an error (not throw) when bad info provided", async () => { + const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, { + row: { + tableId: "invalid", + invalid: "invalid", + } + }) + expect(res.success).toEqual(false) + }) + + it("check usage quota attempts", async () => { + env.CLOUD = true + await setup.runStep(setup.actions.CREATE_ROW.stepId, { + row + }) + expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "rows", 1) + env.CLOUD = false + }) + + it("should check invalid inputs return an error", async () => { + const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {}) + expect(res.success).toEqual(false) + }) +}) diff --git a/packages/server/src/automations/tests/createUser.spec.js b/packages/server/src/automations/tests/createUser.spec.js new file mode 100644 index 0000000000..5f65e260a9 --- /dev/null +++ b/packages/server/src/automations/tests/createUser.spec.js @@ -0,0 +1,43 @@ +const usageQuota = require("../../utilities/usageQuota") +const env = require("../../environment") +const setup = require("./utilities") +const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles") +const { ViewNames } = require("../../db/utils") + +jest.mock("../../utilities/usageQuota") + +describe("test the create user action", () => { + let config = setup.getConfig() + let user + + beforeEach(async () => { + await config.init() + user = { + email: "test@test.com", + password: "password", + roleId: BUILTIN_ROLE_IDS.POWER + } + }) + + afterAll(setup.afterAll) + + it("should be able to run the action", async () => { + const res = await setup.runStep(setup.actions.CREATE_USER.stepId, user) + expect(res.id).toBeDefined() + expect(res.revision).toBeDefined() + const userDoc = await config.getRow(ViewNames.USERS, res.id) + expect(userDoc.email).toEqual(user.email) + }) + + it("should return an error if no inputs provided", async () => { + const res = await setup.runStep(setup.actions.CREATE_USER.stepId, {}) + expect(res.success).toEqual(false) + }) + + it("check usage quota attempts", async () => { + env.CLOUD = true + await setup.runStep(setup.actions.CREATE_USER.stepId, user) + expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "users", 1) + env.CLOUD = false + }) +}) diff --git a/packages/server/src/automations/tests/delay.spec.js b/packages/server/src/automations/tests/delay.spec.js new file mode 100644 index 0000000000..8e9a725d01 --- /dev/null +++ b/packages/server/src/automations/tests/delay.spec.js @@ -0,0 +1,12 @@ +const setup = require("./utilities") + +describe("test the delay action", () => { + it("should be able to run the delay", async () => { + const time = 100 + const before = Date.now() + await setup.runStep(setup.logic.DELAY.stepId, { time: time }) + const now = Date.now() + // divide by two just so that test will always pass as long as there was some sort of delay + expect(now - before).toBeGreaterThanOrEqual(time / 2) + }) +}) \ No newline at end of file diff --git a/packages/server/src/automations/tests/filter.spec.js b/packages/server/src/automations/tests/filter.spec.js new file mode 100644 index 0000000000..8ddd94f7e4 --- /dev/null +++ b/packages/server/src/automations/tests/filter.spec.js @@ -0,0 +1,48 @@ +const setup = require("./utilities") +const { LogicConditions } = require("../steps/filter") + +describe("test the delay action", () => { + async function checkFilter(field, condition, value, pass = true) { + let res = await setup.runStep(setup.logic.FILTER.stepId, + { field, condition, value } + ) + expect(res.success).toEqual(pass) + } + + it("should be able test equality", async () => { + await checkFilter("hello", LogicConditions.EQUAL, "hello", true) + await checkFilter("hello", LogicConditions.EQUAL, "no", false) + }) + + it("should be able to test greater than", async () => { + await checkFilter(10, LogicConditions.GREATER_THAN, 5, true) + await checkFilter(10, LogicConditions.GREATER_THAN, 15, false) + }) + + it("should be able to test less than", async () => { + await checkFilter(5, LogicConditions.LESS_THAN, 10, true) + await checkFilter(15, LogicConditions.LESS_THAN, 10, false) + }) + + it("should be able to in-equality", async () => { + await checkFilter("hello", LogicConditions.NOT_EQUAL, "no", true) + await checkFilter(10, LogicConditions.NOT_EQUAL, 10, false) + }) + + it("check number coercion", async () => { + await checkFilter("10", LogicConditions.GREATER_THAN, "5", true) + }) + + it("check date coercion", async () => { + await checkFilter( + (new Date()).toISOString(), + LogicConditions.GREATER_THAN, + (new Date(-10000)).toISOString(), + true + ) + }) + + it("check objects always false", async () => { + await checkFilter({}, LogicConditions.EQUAL, {}, false) + }) +}) \ No newline at end of file diff --git a/packages/server/src/automations/tests/utilities/index.js b/packages/server/src/automations/tests/utilities/index.js new file mode 100644 index 0000000000..ad149d6bde --- /dev/null +++ b/packages/server/src/automations/tests/utilities/index.js @@ -0,0 +1,43 @@ +const TestConfig = require("../../../tests/utilities/TestConfiguration") +const actions = require("../../actions") +const logic = require("../../logic") +const emitter = require("../../../events/index") + +let config + +exports.getConfig = () => { + if (!config) { + config = new TestConfig(false) + } + return config +} + +exports.afterAll = () => { + config.end() +} + +exports.runStep = async function runStep(stepId, inputs) { + let step + if ( + Object.values(exports.actions) + .map(action => action.stepId) + .includes(stepId) + ) { + step = await actions.getAction(stepId) + } else { + step = logic.getLogic(stepId) + } + expect(step).toBeDefined() + return step({ + inputs, + appId: config ? config.getAppId() : null, + // don't really need an API key, mocked out usage quota, not being tested here + apiKey: exports.apiKey, + emitter, + }) +} + +exports.apiKey = "test" + +exports.actions = actions.BUILTIN_DEFINITIONS +exports.logic = logic.BUILTIN_DEFINITIONS diff --git a/packages/server/src/middleware/tests/usageQuota.spec.js b/packages/server/src/middleware/tests/usageQuota.spec.js index c76acb47d2..395f14c1ed 100644 --- a/packages/server/src/middleware/tests/usageQuota.spec.js +++ b/packages/server/src/middleware/tests/usageQuota.spec.js @@ -3,7 +3,7 @@ const usageQuota = require("../../utilities/usageQuota") const CouchDB = require("../../db") const env = require("../../environment") -jest.mock("../../db"); +jest.mock("../../db") jest.mock("../../utilities/usageQuota") jest.mock("../../environment") diff --git a/packages/server/src/selfhost/tests/setup.spec.js b/packages/server/src/selfhost/tests/setup.spec.js new file mode 100644 index 0000000000..13eeda0b1b --- /dev/null +++ b/packages/server/src/selfhost/tests/setup.spec.js @@ -0,0 +1,26 @@ +const selfhost = require("..") +const env = require("../../environment") + +describe("test the setup process", () => { + beforeAll(() => { + env.SELF_HOSTED = true + }) + + beforeEach(async () => { + await selfhost.init() + }) + + afterAll(() => { + env.SELF_HOSTED = false + }) + + it("getSelfHostInfo", async () => { + let info = await selfhost.getSelfHostInfo() + expect(info._id).toEqual("self-host-info") + }) + + it("getSelfHostAPIKey", async () => { + let apiKey = await selfhost.getSelfHostAPIKey() + expect(typeof apiKey).toEqual("string") + }) +}) \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js similarity index 91% rename from packages/server/src/api/routes/tests/utilities/TestConfiguration.js rename to packages/server/src/tests/utilities/TestConfiguration.js index 0ff742293d..9651bfb670 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -1,6 +1,6 @@ -const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") +const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles") const jwt = require("jsonwebtoken") -const env = require("../../../../environment") +const env = require("../../environment") const { basicTable, basicRow, @@ -15,18 +15,20 @@ const { const controllers = require("./controllers") const supertest = require("supertest") const fs = require("fs") -const { budibaseAppsDir } = require("../../../../utilities/budibaseDir") +const { budibaseAppsDir } = require("../../utilities/budibaseDir") const { join } = require("path") const EMAIL = "babs@babs.com" const PASSWORD = "babs_password" class TestConfiguration { - constructor() { - env.PORT = 4002 - this.server = require("../../../../app") - // we need the request for logging in, involves cookies, hard to fake - this.request = supertest(this.server) + constructor(openServer = true) { + if (openServer) { + env.PORT = 4002 + this.server = require("../../app") + // we need the request for logging in, involves cookies, hard to fake + this.request = supertest(this.server) + } this.appId = null this.allApps = [] } @@ -61,7 +63,9 @@ class TestConfiguration { } end() { - this.server.close() + if (this.server) { + this.server.close() + } const appDir = budibaseAppsDir() const files = fs.readdirSync(appDir) for (let file of files) { @@ -163,6 +167,10 @@ class TestConfiguration { return this._req(config, { tableId: this.table._id }, controllers.row.save) } + async getRow(tableId, rowId) { + return this._req(null, { tableId, rowId }, controllers.row.find) + } + async createRole(config = null) { config = config || basicRole() return this._req(config, null, controllers.role.save) @@ -285,6 +293,9 @@ class TestConfiguration { } async login(email, password) { + if (!this.request) { + throw "Server has not been opened, cannot login." + } if (!email || !password) { await this.createUser() email = EMAIL diff --git a/packages/server/src/tests/utilities/controllers.js b/packages/server/src/tests/utilities/controllers.js new file mode 100644 index 0000000000..b07754038f --- /dev/null +++ b/packages/server/src/tests/utilities/controllers.js @@ -0,0 +1,15 @@ +module.exports = { + table: require("../../api/controllers/table"), + row: require("../../api/controllers/row"), + role: require("../../api/controllers/role"), + perms: require("../../api/controllers/permission"), + view: require("../../api/controllers/view"), + app: require("../../api/controllers/application"), + user: require("../../api/controllers/user"), + automation: require("../../api/controllers/automation"), + datasource: require("../../api/controllers/datasource"), + query: require("../../api/controllers/query"), + screen: require("../../api/controllers/screen"), + webhook: require("../../api/controllers/webhook"), + layout: require("../../api/controllers/layout"), +} diff --git a/packages/server/src/api/routes/tests/utilities/structures.js b/packages/server/src/tests/utilities/structures.js similarity index 86% rename from packages/server/src/api/routes/tests/utilities/structures.js rename to packages/server/src/tests/utilities/structures.js index ff3a239211..e6489f0903 100644 --- a/packages/server/src/api/routes/tests/utilities/structures.js +++ b/packages/server/src/tests/utilities/structures.js @@ -1,9 +1,9 @@ -const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") +const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles") const { BUILTIN_PERMISSION_IDS, -} = require("../../../../utilities/security/permissions") -const { createHomeScreen } = require("../../../../constants/screens") -const { EMPTY_LAYOUT } = require("../../../../constants/layouts") +} = require("../../utilities/security/permissions") +const { createHomeScreen } = require("../../constants/screens") +const { EMPTY_LAYOUT } = require("../../constants/layouts") const { cloneDeep } = require("lodash/fp") exports.basicTable = () => {