Server and Worker tests (#8928)
* automation unit tests * row processor tests * update executeQuery test * update groups assertion * some more worker tests * plugin tests and tidying up * linting * temporarily disable group tests * more tests * fix import * fix backup tests * add pro mocks to worker * check for app existence in import test * test new tsconfig * testing changes * Pro test utils (#9020) * Pro test utils changes * Add test fixes * Remove ts-ignore Co-authored-by: mike12345567 <me@michaeldrury.co.uk> Co-authored-by: Rory Powell <rory.codes@gmail.com>
This commit is contained in:
parent
eb27859af1
commit
9d841bc947
|
@ -106,3 +106,5 @@ stats.html
|
|||
*.tsbuildinfo
|
||||
budibase-component
|
||||
budibase-datasource
|
||||
|
||||
*.iml
|
|
@ -8,7 +8,7 @@
|
|||
"editor.defaultFormatter": "vscode.json-language-features"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"debug.javascript.terminalOptions": {
|
||||
"skipFiles": [
|
||||
|
@ -16,4 +16,7 @@
|
|||
"<node_internals>/**"
|
||||
]
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
}
|
||||
|
|
|
@ -78,4 +78,4 @@
|
|||
"typescript": "4.7.3"
|
||||
},
|
||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
function isTest() {
|
||||
return (
|
||||
process.env.NODE_ENV === "jest" ||
|
||||
process.env.NODE_ENV === "cypress" ||
|
||||
process.env.JEST_WORKER_ID != null
|
||||
)
|
||||
return isCypress() || isJest()
|
||||
}
|
||||
|
||||
function isJest() {
|
||||
return !!(process.env.NODE_ENV === "jest" || process.env.JEST_WORKER_ID)
|
||||
}
|
||||
|
||||
function isCypress() {
|
||||
return process.env.NODE_ENV === "cypress"
|
||||
}
|
||||
|
||||
function isDev() {
|
||||
|
@ -27,6 +31,7 @@ const DefaultBucketName = {
|
|||
|
||||
const environment = {
|
||||
isTest,
|
||||
isJest,
|
||||
isDev,
|
||||
JS_BCRYPT: process.env.JS_BCRYPT,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
|
|
|
@ -117,3 +117,7 @@ jest.spyOn(events.view, "filterDeleted")
|
|||
jest.spyOn(events.view, "calculationCreated")
|
||||
jest.spyOn(events.view, "calculationUpdated")
|
||||
jest.spyOn(events.view, "calculationDeleted")
|
||||
|
||||
jest.spyOn(events.plugin, "init")
|
||||
jest.spyOn(events.plugin, "imported")
|
||||
jest.spyOn(events.plugin, "deleted")
|
||||
|
|
|
@ -2,4 +2,5 @@ import "./posthog"
|
|||
import "./events"
|
||||
export * as accounts from "./accounts"
|
||||
export * as date from "./date"
|
||||
export * as licenses from "./licenses"
|
||||
export { default as fetch } from "./fetch"
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import { Feature, License, Quotas } from "@budibase/types"
|
||||
import _ from "lodash"
|
||||
|
||||
let CLOUD_FREE_LICENSE: License
|
||||
let TEST_LICENSE: License
|
||||
let getCachedLicense: any
|
||||
|
||||
// init for the packages other than pro
|
||||
export function init(proPkg: any) {
|
||||
initInternal({
|
||||
CLOUD_FREE_LICENSE: proPkg.constants.licenses.CLOUD_FREE_LICENSE,
|
||||
TEST_LICENSE: proPkg.constants.licenses.DEVELOPER_FREE_LICENSE,
|
||||
getCachedLicense: proPkg.licensing.cache.getCachedLicense,
|
||||
})
|
||||
}
|
||||
|
||||
// init for the pro package
|
||||
export function initInternal(opts: {
|
||||
CLOUD_FREE_LICENSE: License
|
||||
TEST_LICENSE: License
|
||||
getCachedLicense: any
|
||||
}) {
|
||||
CLOUD_FREE_LICENSE = opts.CLOUD_FREE_LICENSE
|
||||
TEST_LICENSE = opts.TEST_LICENSE
|
||||
getCachedLicense = opts.getCachedLicense
|
||||
}
|
||||
|
||||
export interface UseLicenseOpts {
|
||||
features?: Feature[]
|
||||
quotas?: Quotas
|
||||
}
|
||||
|
||||
// LICENSES
|
||||
|
||||
export const useLicense = (license: License, opts?: UseLicenseOpts) => {
|
||||
if (opts) {
|
||||
if (opts.features) {
|
||||
license.features.push(...opts.features)
|
||||
}
|
||||
if (opts.quotas) {
|
||||
license.quotas = opts.quotas
|
||||
}
|
||||
}
|
||||
|
||||
getCachedLicense.mockReturnValue(license)
|
||||
|
||||
return license
|
||||
}
|
||||
|
||||
export const useUnlimited = (opts?: UseLicenseOpts) => {
|
||||
return useLicense(TEST_LICENSE, opts)
|
||||
}
|
||||
|
||||
export const useCloudFree = () => {
|
||||
return useLicense(CLOUD_FREE_LICENSE)
|
||||
}
|
||||
|
||||
// FEATURES
|
||||
|
||||
const useFeature = (feature: Feature) => {
|
||||
const license = _.cloneDeep(TEST_LICENSE)
|
||||
const opts: UseLicenseOpts = {
|
||||
features: [feature],
|
||||
}
|
||||
|
||||
return useLicense(license, opts)
|
||||
}
|
||||
|
||||
export const useBackups = () => {
|
||||
return useFeature(Feature.APP_BACKUPS)
|
||||
}
|
||||
|
||||
export const useGroups = () => {
|
||||
return useFeature(Feature.USER_GROUPS)
|
||||
}
|
||||
|
||||
// QUOTAS
|
||||
|
||||
export const setAutomationLogsQuota = (value: number) => {
|
||||
const license = _.cloneDeep(TEST_LICENSE)
|
||||
license.quotas.constant.automationLogRetentionDays.value = value
|
||||
return useLicense(license)
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import fs from "fs"
|
||||
module FetchMock {
|
||||
const fetch = jest.requireActual("node-fetch")
|
||||
let failCount = 0
|
||||
|
@ -92,6 +93,83 @@ module FetchMock {
|
|||
value:
|
||||
'<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-GB"></html>',
|
||||
})
|
||||
} else if (
|
||||
url === "https://api.github.com/repos/my-repo/budibase-comment-box"
|
||||
) {
|
||||
return Promise.resolve({
|
||||
json: () => {
|
||||
return {
|
||||
name: "budibase-comment-box",
|
||||
releases_url:
|
||||
"https://api.github.com/repos/my-repo/budibase-comment-box{/id}",
|
||||
}
|
||||
},
|
||||
})
|
||||
} else if (
|
||||
url === "https://api.github.com/repos/my-repo/budibase-comment-box/latest"
|
||||
) {
|
||||
return Promise.resolve({
|
||||
json: () => {
|
||||
return {
|
||||
assets: [
|
||||
{
|
||||
content_type: "application/gzip",
|
||||
browser_download_url:
|
||||
"https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
} else if (
|
||||
url ===
|
||||
"https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz"
|
||||
) {
|
||||
return Promise.resolve({
|
||||
body: fs.createReadStream(
|
||||
"src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
|
||||
),
|
||||
ok: true,
|
||||
})
|
||||
} else if (url === "https://www.npmjs.com/package/budibase-component") {
|
||||
return Promise.resolve({
|
||||
status: 200,
|
||||
json: () => {
|
||||
return {
|
||||
name: "budibase-component",
|
||||
"dist-tags": {
|
||||
latest: "1.0.0",
|
||||
},
|
||||
versions: {
|
||||
"1.0.0": {
|
||||
dist: {
|
||||
tarball:
|
||||
"https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.2.tgz",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
} else if (
|
||||
url ===
|
||||
"https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.2.tgz"
|
||||
) {
|
||||
return Promise.resolve({
|
||||
body: fs.createReadStream(
|
||||
"src/api/routes/tests/data/budibase-component-1.0.2.tgz"
|
||||
),
|
||||
ok: true,
|
||||
})
|
||||
} else if (
|
||||
url === "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz"
|
||||
) {
|
||||
return Promise.resolve({
|
||||
body: fs.createReadStream(
|
||||
"src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
|
||||
),
|
||||
ok: true,
|
||||
})
|
||||
} else if (url.includes("failonce.com")) {
|
||||
failCount++
|
||||
if (failCount === 1) {
|
||||
|
|
|
@ -12,13 +12,11 @@ jest.mock("../../../utilities/redis", () => ({
|
|||
shutdown: jest.fn(),
|
||||
}))
|
||||
|
||||
const {
|
||||
clearAllApps,
|
||||
checkBuilderEndpoint,
|
||||
} = require("./utilities/TestFunctions")
|
||||
const setup = require("./utilities")
|
||||
const { AppStatus } = require("../../../db/utils")
|
||||
const { events } = require("@budibase/backend-core")
|
||||
import { clearAllApps, checkBuilderEndpoint } from "./utilities/TestFunctions"
|
||||
import * as setup from "./utilities"
|
||||
import { AppStatus } from "../../../db/utils"
|
||||
import { events } from "@budibase/backend-core"
|
||||
import env from "../../../environment"
|
||||
|
||||
describe("/applications", () => {
|
||||
let request = setup.getRequest()
|
||||
|
@ -234,4 +232,39 @@ describe("/applications", () => {
|
|||
expect(getRes.body.application.updatedAt).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("sync", () => {
|
||||
it("app should sync correctly", async () => {
|
||||
const res = await request
|
||||
.post(`/api/applications/${config.getAppId()}/sync`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body.message).toEqual("App sync completed successfully.")
|
||||
})
|
||||
|
||||
it("app should not sync if production", async () => {
|
||||
const res = await request
|
||||
.post(`/api/applications/app_123456/sync`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(400)
|
||||
expect(res.body.message).toEqual(
|
||||
"This action cannot be performed for production apps"
|
||||
)
|
||||
})
|
||||
|
||||
it("app should not sync if sync is disabled", async () => {
|
||||
env._set("DISABLE_AUTO_PROD_APP_SYNC", true)
|
||||
const res = await request
|
||||
.post(`/api/applications/${config.getAppId()}/sync`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body.message).toEqual(
|
||||
"App sync disabled. You can reenable with the DISABLE_AUTO_PROD_APP_SYNC environment variable."
|
||||
)
|
||||
env._set("DISABLE_AUTO_PROD_APP_SYNC", false)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -8,10 +8,10 @@ jest.mock("@budibase/backend-core", () => {
|
|||
}
|
||||
})
|
||||
|
||||
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||
const setup = require("./utilities")
|
||||
const { events } = require("@budibase/backend-core")
|
||||
|
||||
import * as setup from "./utilities"
|
||||
import { events } from "@budibase/backend-core"
|
||||
import sdk from "../../../sdk"
|
||||
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
|
||||
describe("/backups", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
|
@ -30,7 +30,7 @@ describe("/backups", () => {
|
|||
.expect(200)
|
||||
expect(res.text).toBeDefined()
|
||||
expect(res.headers["content-type"]).toEqual("application/gzip")
|
||||
expect(events.app.exported.mock.calls.length).toBe(1)
|
||||
expect(events.app.exported).toBeCalledTimes(1)
|
||||
})
|
||||
|
||||
it("should apply authorization to endpoint", async () => {
|
||||
|
@ -41,4 +41,15 @@ describe("/backups", () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("calculateBackupStats", () => {
|
||||
it("should be able to calculate the backup statistics", async () => {
|
||||
config.createAutomation()
|
||||
config.createScreen()
|
||||
let res = await sdk.backups.calculateBackupStats(config.getAppId())
|
||||
expect(res.automations).toEqual(1)
|
||||
expect(res.datasources).toEqual(1)
|
||||
expect(res.screens).toEqual(1)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,66 @@
|
|||
import { db as dbCore } from "@budibase/backend-core"
|
||||
import { AppStatus } from "../../../db/utils"
|
||||
|
||||
import * as setup from "./utilities"
|
||||
|
||||
describe("/cloud", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// clear all mocks
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("import", () => {
|
||||
it("should be able to import apps", async () => {
|
||||
// first we need to delete any existing apps on the system so it looks clean otherwise the
|
||||
// import will not run
|
||||
await request
|
||||
.delete(
|
||||
`/api/applications/${dbCore.getProdAppID(
|
||||
config.getAppId()
|
||||
)}?unpublish=true`
|
||||
)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
await request
|
||||
.delete(`/api/applications/${config.getAppId()}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
// get a count of apps before the import
|
||||
const preImportApps = await request
|
||||
.get(`/api/applications?status=${AppStatus.ALL}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
// Perform the import
|
||||
const res = await request
|
||||
.post(`/api/cloud/import`)
|
||||
.attach("importFile", "src/api/routes/tests/data/export-test.tar.gz")
|
||||
.set(config.defaultHeaders())
|
||||
.expect(200)
|
||||
expect(res.body.message).toEqual("Apps successfully imported.")
|
||||
|
||||
// get a count of apps after the import
|
||||
const postImportApps = await request
|
||||
.get(`/api/applications?status=${AppStatus.ALL}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
// There are two apps in the file that was imported so check for this
|
||||
expect(postImportApps.body.length).toEqual(2)
|
||||
})
|
||||
})
|
||||
})
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,16 +1,16 @@
|
|||
jest.mock("pg")
|
||||
import * as setup from "./utilities"
|
||||
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
|
||||
import { checkCacheForDynamicVariable } from "../../../threads/utils"
|
||||
import { events } from "@budibase/backend-core"
|
||||
|
||||
let setup = require("./utilities")
|
||||
let { basicDatasource } = setup.structures
|
||||
let { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||
const pg = require("pg")
|
||||
const { checkCacheForDynamicVariable } = require("../../../threads/utils")
|
||||
const { events } = require("@budibase/backend-core")
|
||||
|
||||
describe("/datasources", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
let datasource
|
||||
let datasource: any
|
||||
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
|
@ -26,7 +26,7 @@ describe("/datasources", () => {
|
|||
.post(`/api/datasources`)
|
||||
.send(basicDatasource())
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.datasource.name).toEqual("Test")
|
||||
|
@ -42,7 +42,7 @@ describe("/datasources", () => {
|
|||
.put(`/api/datasources/${datasource._id}`)
|
||||
.send(datasource)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.datasource.name).toEqual("Updated Test")
|
||||
|
@ -51,25 +51,34 @@ describe("/datasources", () => {
|
|||
})
|
||||
|
||||
describe("dynamic variables", () => {
|
||||
async function preview(datasource, fields) {
|
||||
async function preview(
|
||||
datasource: any,
|
||||
fields: { path: string; queryString: string }
|
||||
) {
|
||||
return config.previewQuery(request, config, datasource, fields)
|
||||
}
|
||||
|
||||
it("should invalidate changed or removed variables", async () => {
|
||||
const { datasource, query } = await config.dynamicVariableDatasource()
|
||||
// preview once to cache variables
|
||||
await preview(datasource, { path: "www.test.com", queryString: "test={{ variable3 }}" })
|
||||
await preview(datasource, {
|
||||
path: "www.test.com",
|
||||
queryString: "test={{ variable3 }}",
|
||||
})
|
||||
// check variables in cache
|
||||
let contents = await checkCacheForDynamicVariable(query._id, "variable3")
|
||||
let contents = await checkCacheForDynamicVariable(
|
||||
query._id,
|
||||
"variable3"
|
||||
)
|
||||
expect(contents.rows.length).toEqual(1)
|
||||
|
||||
|
||||
// update the datasource to remove the variables
|
||||
datasource.config.dynamicVariables = []
|
||||
const res = await request
|
||||
.put(`/api/datasources/${datasource._id}`)
|
||||
.send(datasource)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body.errors).toBeUndefined()
|
||||
|
||||
|
@ -85,7 +94,7 @@ describe("/datasources", () => {
|
|||
const res = await request
|
||||
.get(`/api/datasources`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
const datasources = res.body
|
||||
|
@ -160,7 +169,7 @@ describe("/datasources", () => {
|
|||
const res = await request
|
||||
.get(`/api/datasources`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.length).toEqual(1)
|
||||
|
@ -174,6 +183,5 @@ describe("/datasources", () => {
|
|||
url: `/api/datasources/${datasource._id}/${datasource._rev}`,
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
})
|
|
@ -0,0 +1,179 @@
|
|||
let mockObjectStore = jest.fn().mockImplementation(() => {
|
||||
return [{ name: "test.js" }]
|
||||
})
|
||||
|
||||
let deleteFolder = jest.fn().mockImplementation()
|
||||
jest.mock("@budibase/backend-core", () => {
|
||||
const core = jest.requireActual("@budibase/backend-core")
|
||||
return {
|
||||
...core,
|
||||
objectStore: {
|
||||
...core.objectStore,
|
||||
upload: jest.fn(),
|
||||
uploadDirectory: mockObjectStore,
|
||||
deleteFolder: deleteFolder,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
import { events } from "@budibase/backend-core"
|
||||
import * as setup from "./utilities"
|
||||
|
||||
describe("/plugins", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
const createPlugin = async (status?: number) => {
|
||||
return request
|
||||
.post(`/api/plugin/upload`)
|
||||
.attach("file", "src/api/routes/tests/data/comment-box-1.0.2.tar.gz")
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(status ? status : 200)
|
||||
}
|
||||
|
||||
const getPlugins = async (status?: number) => {
|
||||
return request
|
||||
.get(`/api/plugin`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(status ? status : 200)
|
||||
}
|
||||
|
||||
describe("upload", () => {
|
||||
it("should be able to upload a plugin", async () => {
|
||||
let res = await createPlugin()
|
||||
expect(res.body).toBeDefined()
|
||||
expect(res.body.plugins).toBeDefined()
|
||||
expect(res.body.plugins[0]._id).toEqual("plg_comment-box")
|
||||
expect(events.plugin.imported).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("should not be able to create a plugin if there is an error", async () => {
|
||||
mockObjectStore.mockImplementationOnce(() => {
|
||||
throw new Error()
|
||||
})
|
||||
let res = await createPlugin(400)
|
||||
expect(res.body.message).toEqual("Failed to import plugin: Error")
|
||||
expect(events.plugin.imported).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("fetch", () => {
|
||||
it("should be able to fetch plugins", async () => {
|
||||
await createPlugin()
|
||||
const res = await getPlugins()
|
||||
expect(res.body).toBeDefined()
|
||||
expect(res.body[0]._id).toEqual("plg_comment-box")
|
||||
})
|
||||
})
|
||||
|
||||
describe("destroy", () => {
|
||||
it("should be able to delete a plugin", async () => {
|
||||
await createPlugin()
|
||||
const res = await request
|
||||
.delete(`/api/plugin/plg_comment-box`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body).toBeDefined()
|
||||
expect(res.body.message).toEqual("Plugin plg_comment-box deleted.")
|
||||
|
||||
const plugins = await getPlugins()
|
||||
expect(plugins.body).toBeDefined()
|
||||
expect(plugins.body.length).toEqual(0)
|
||||
expect(events.plugin.deleted).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
it("should handle an error deleting a plugin", async () => {
|
||||
deleteFolder.mockImplementationOnce(() => {
|
||||
throw new Error()
|
||||
})
|
||||
|
||||
await createPlugin()
|
||||
const res = await request
|
||||
.delete(`/api/plugin/plg_comment-box`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(400)
|
||||
|
||||
expect(res.body.message).toEqual("Failed to delete plugin: Error")
|
||||
expect(events.plugin.deleted).toHaveBeenCalledTimes(0)
|
||||
const plugins = await getPlugins()
|
||||
expect(plugins.body).toBeDefined()
|
||||
expect(plugins.body.length).toEqual(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("github", () => {
|
||||
const createGithubPlugin = async (status?: number, url?: string) => {
|
||||
return await request
|
||||
.post(`/api/plugin`)
|
||||
.send({
|
||||
source: "Github",
|
||||
url,
|
||||
githubToken: "token",
|
||||
})
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(status ? status : 200)
|
||||
}
|
||||
it("should be able to create a plugin from github", async () => {
|
||||
const res = await createGithubPlugin(
|
||||
200,
|
||||
"https://github.com/my-repo/budibase-comment-box.git"
|
||||
)
|
||||
expect(res.body).toBeDefined()
|
||||
expect(res.body.plugin).toBeDefined()
|
||||
expect(res.body.plugin._id).toEqual("plg_comment-box")
|
||||
})
|
||||
it("should fail if the url is not from github", async () => {
|
||||
const res = await createGithubPlugin(
|
||||
400,
|
||||
"https://notgithub.com/my-repo/budibase-comment-box"
|
||||
)
|
||||
expect(res.body.message).toEqual(
|
||||
"Failed to import plugin: The plugin origin must be from Github"
|
||||
)
|
||||
})
|
||||
})
|
||||
describe("npm", () => {
|
||||
it("should be able to create a plugin from npm", async () => {
|
||||
const res = await request
|
||||
.post(`/api/plugin`)
|
||||
.send({
|
||||
source: "NPM",
|
||||
url: "https://www.npmjs.com/package/budibase-component",
|
||||
})
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body).toBeDefined()
|
||||
expect(res.body.plugin._id).toEqual("plg_budibase-component")
|
||||
expect(events.plugin.imported).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("url", () => {
|
||||
it("should be able to create a plugin from a URL", async () => {
|
||||
const res = await request
|
||||
.post(`/api/plugin`)
|
||||
.send({
|
||||
source: "URL",
|
||||
url: "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz",
|
||||
})
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body).toBeDefined()
|
||||
expect(res.body.plugin._id).toEqual("plg_comment-box")
|
||||
expect(events.plugin.imported).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -9,7 +9,6 @@ const route = "/test"
|
|||
// there are checks which are disabled in test env,
|
||||
// these checks need to be enabled for this test
|
||||
|
||||
|
||||
describe("/routing", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
|
|
|
@ -51,7 +51,7 @@ describe("/tables", () => {
|
|||
table.dataImport.schema = table.schema
|
||||
|
||||
const res = await createTable(table)
|
||||
|
||||
|
||||
expect(events.table.created).toBeCalledTimes(1)
|
||||
expect(events.table.created).toBeCalledWith(res.body)
|
||||
expect(events.table.imported).toBeCalledTimes(1)
|
||||
|
@ -87,6 +87,12 @@ describe("/tables", () => {
|
|||
|
||||
it("updates all the row fields for a table when a schema key is renamed", async () => {
|
||||
const testTable = await config.createTable()
|
||||
await config.createView({
|
||||
name: "TestView",
|
||||
field: "Price",
|
||||
calculation: "stats",
|
||||
tableId: testTable._id,
|
||||
})
|
||||
|
||||
const testRow = await request
|
||||
.post(`/api/${testTable._id}/rows`)
|
||||
|
@ -109,7 +115,7 @@ describe("/tables", () => {
|
|||
updated: "updatedName"
|
||||
},
|
||||
schema: {
|
||||
updatedName: {type: "string"}
|
||||
updatedName: { type: "string" }
|
||||
}
|
||||
})
|
||||
.set(config.defaultHeaders())
|
||||
|
|
|
@ -90,4 +90,90 @@ describe("/users", () => {
|
|||
expect(res.body.tableId).toBeDefined()
|
||||
})
|
||||
})
|
||||
describe("setFlag", () => {
|
||||
it("should throw an error if a flag is not provided", async () => {
|
||||
await config.createUser()
|
||||
const res = await request
|
||||
.post(`/api/users/flags`)
|
||||
.set(config.defaultHeaders())
|
||||
.send({ value: "test" })
|
||||
.expect(400)
|
||||
.expect("Content-Type", /json/)
|
||||
expect(res.body.message).toEqual("Must supply a 'flag' field in request body.")
|
||||
|
||||
})
|
||||
|
||||
it("should be able to set a flag on the user", async () => {
|
||||
await config.createUser()
|
||||
const res = await request
|
||||
.post(`/api/users/flags`)
|
||||
.set(config.defaultHeaders())
|
||||
.send({ value: "test", flag: "test" })
|
||||
.expect(200)
|
||||
.expect("Content-Type", /json/)
|
||||
expect(res.body.message).toEqual("Flag set successfully")
|
||||
})
|
||||
})
|
||||
|
||||
describe("getFlags", () => {
|
||||
it("should get flags for a specific user", async () => {
|
||||
let flagData = { value: "test", flag: "test" }
|
||||
await config.createUser()
|
||||
await request
|
||||
.post(`/api/users/flags`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(flagData)
|
||||
.expect(200)
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
const res = await request
|
||||
.get(`/api/users/flags`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect(200)
|
||||
.expect("Content-Type", /json/)
|
||||
expect(res.body[flagData.value]).toEqual(flagData.flag)
|
||||
})
|
||||
})
|
||||
|
||||
describe("setFlag", () => {
|
||||
it("should throw an error if a flag is not provided", async () => {
|
||||
await config.createUser()
|
||||
const res = await request
|
||||
.post(`/api/users/flags`)
|
||||
.set(config.defaultHeaders())
|
||||
.send({ value: "test" })
|
||||
.expect(400)
|
||||
.expect("Content-Type", /json/)
|
||||
expect(res.body.message).toEqual("Must supply a 'flag' field in request body.")
|
||||
|
||||
})
|
||||
|
||||
it("should be able to set a flag on the user", async () => {
|
||||
await config.createUser()
|
||||
const res = await request
|
||||
.post(`/api/users/flags`)
|
||||
.set(config.defaultHeaders())
|
||||
.send({ value: "test", flag: "test" })
|
||||
.expect(200)
|
||||
.expect("Content-Type", /json/)
|
||||
expect(res.body.message).toEqual("Flag set successfully")
|
||||
})
|
||||
})
|
||||
|
||||
describe("syncUser", () => {
|
||||
it("should sync the user", async () => {
|
||||
let user = await config.createUser()
|
||||
await config.createApp('New App')
|
||||
let res = await request
|
||||
.post(`/api/users/metadata/sync/${user._id}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect(200)
|
||||
.expect("Content-Type", /json/)
|
||||
expect(res.body.message).toEqual('User synced.')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
|
|
@ -63,14 +63,14 @@ export function afterAll() {
|
|||
|
||||
export function getRequest() {
|
||||
if (!request) {
|
||||
exports.beforeAll()
|
||||
beforeAll()
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
export function getConfig() {
|
||||
if (!config) {
|
||||
exports.beforeAll()
|
||||
beforeAll()
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
} from "@budibase/string-templates"
|
||||
import sdk from "../sdk"
|
||||
import { Row } from "@budibase/types"
|
||||
import { LoopStep, LoopStepType, LoopInput } from "../definitions/automations"
|
||||
|
||||
/**
|
||||
* When values are input to the system generally they will be of type string as this is required for template strings.
|
||||
|
@ -123,3 +124,26 @@ export function stringSplit(value: string | string[]) {
|
|||
}
|
||||
return value
|
||||
}
|
||||
|
||||
export function typecastForLooping(loopStep: LoopStep, input: LoopInput) {
|
||||
if (!input || !input.binding) {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
switch (loopStep.inputs.option) {
|
||||
case LoopStepType.ARRAY:
|
||||
if (typeof input.binding === "string") {
|
||||
return JSON.parse(input.binding)
|
||||
}
|
||||
break
|
||||
case LoopStepType.STRING:
|
||||
if (Array.isArray(input.binding)) {
|
||||
return input.binding.join(",")
|
||||
}
|
||||
break
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error("Unable to cast to correct type")
|
||||
}
|
||||
return input.binding
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
const setup = require("./utilities")
|
||||
|
||||
describe("test the bash action", () => {
|
||||
let config = setup.getConfig()
|
||||
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
})
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
it("should be able to execute a script", async () => {
|
||||
|
||||
let res = await setup.runStep("EXECUTE_BASH",
|
||||
inputs = {
|
||||
code: "echo 'test'"
|
||||
}
|
||||
|
||||
)
|
||||
expect(res.stdout).toEqual("test\n")
|
||||
expect(res.success).toEqual(true)
|
||||
})
|
||||
|
||||
it("should handle a null value", async () => {
|
||||
|
||||
let res = await setup.runStep("EXECUTE_BASH",
|
||||
inputs = {
|
||||
code: null
|
||||
}
|
||||
|
||||
|
||||
)
|
||||
expect(res.stdout).toEqual("Budibase bash automation failed: Invalid inputs")
|
||||
})
|
||||
})
|
|
@ -0,0 +1,27 @@
|
|||
const setup = require("./utilities")
|
||||
const fetch = require("node-fetch")
|
||||
|
||||
jest.mock("node-fetch")
|
||||
|
||||
describe("test the outgoing webhook action", () => {
|
||||
let inputs
|
||||
let config = setup.getConfig()
|
||||
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
inputs = {
|
||||
username: "joe_bloggs",
|
||||
url: "http://www.test.com",
|
||||
}
|
||||
})
|
||||
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
it("should be able to run the action", async () => {
|
||||
const res = await setup.runStep(setup.actions.discord.stepId, inputs)
|
||||
expect(res.response.url).toEqual("http://www.test.com")
|
||||
expect(res.response.method).toEqual("post")
|
||||
expect(res.success).toEqual(true)
|
||||
})
|
||||
|
||||
})
|
|
@ -0,0 +1,49 @@
|
|||
const setup = require("./utilities")
|
||||
|
||||
describe("test the execute query action", () => {
|
||||
let datasource
|
||||
let config = setup.getConfig()
|
||||
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
|
||||
await config.createDatasource()
|
||||
query = await config.createQuery()
|
||||
|
||||
})
|
||||
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
it("should be able to execute a query", async () => {
|
||||
let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId,
|
||||
inputs = {
|
||||
query: { queryId: query._id }
|
||||
}
|
||||
)
|
||||
expect(res.response).toEqual([{ a: 'string', b: 1 }])
|
||||
expect(res.success).toEqual(true)
|
||||
})
|
||||
|
||||
it("should handle a null query value", async () => {
|
||||
let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId,
|
||||
inputs = {
|
||||
query: null
|
||||
}
|
||||
)
|
||||
expect(res.response.message).toEqual("Invalid inputs")
|
||||
expect(res.success).toEqual(false)
|
||||
})
|
||||
|
||||
|
||||
it("should handle an error executing a query", async () => {
|
||||
let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId,
|
||||
inputs = {
|
||||
query: { queryId: "wrong_id" }
|
||||
}
|
||||
)
|
||||
expect(res.response).toEqual('{"status":404,"name":"not_found","message":"missing","reason":"missing"}')
|
||||
expect(res.success).toEqual(false)
|
||||
})
|
||||
|
||||
|
||||
})
|
|
@ -0,0 +1,48 @@
|
|||
const setup = require("./utilities")
|
||||
|
||||
describe("test the execute script action", () => {
|
||||
let config = setup.getConfig()
|
||||
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
})
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
it("should be able to execute a script", async () => {
|
||||
|
||||
let res = await setup.runStep(setup.actions.EXECUTE_SCRIPT.stepId,
|
||||
inputs = {
|
||||
code: "return 1 + 1"
|
||||
}
|
||||
|
||||
)
|
||||
expect(res.value).toEqual(2)
|
||||
expect(res.success).toEqual(true)
|
||||
})
|
||||
|
||||
it("should handle a null value", async () => {
|
||||
|
||||
let res = await setup.runStep(setup.actions.EXECUTE_SCRIPT.stepId,
|
||||
inputs = {
|
||||
code: null
|
||||
}
|
||||
|
||||
|
||||
)
|
||||
expect(res.response.message).toEqual("Invalid inputs")
|
||||
expect(res.success).toEqual(false)
|
||||
})
|
||||
|
||||
it("should be able to handle an error gracefully", async () => {
|
||||
|
||||
let res = await setup.runStep(setup.actions.EXECUTE_SCRIPT.stepId,
|
||||
inputs = {
|
||||
code: "return something.map(x => x.name)"
|
||||
}
|
||||
|
||||
)
|
||||
expect(res.response).toEqual("ReferenceError: something is not defined")
|
||||
expect(res.success).toEqual(false)
|
||||
})
|
||||
|
||||
})
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
function generateResponse(to, from) {
|
||||
return {
|
||||
"success": true,
|
||||
"response": {
|
||||
"accepted": [
|
||||
to
|
||||
],
|
||||
"envelope": {
|
||||
"from": from,
|
||||
"to": [
|
||||
to
|
||||
]
|
||||
},
|
||||
"message": `Email sent to ${to}.`
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const mockFetch = jest.fn(() => ({
|
||||
headers: {
|
||||
raw: () => {
|
||||
return { "content-type": ["application/json"] }
|
||||
},
|
||||
get: () => ["application/json"],
|
||||
},
|
||||
json: jest.fn(() => response),
|
||||
status: 200,
|
||||
text: jest.fn(),
|
||||
}))
|
||||
jest.mock("node-fetch", () => mockFetch)
|
||||
const setup = require("./utilities")
|
||||
|
||||
|
||||
describe("test the outgoing webhook action", () => {
|
||||
let inputs
|
||||
let config = setup.getConfig()
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
})
|
||||
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
it("should be able to run the action", async () => {
|
||||
inputs = {
|
||||
to: "user1@test.com",
|
||||
from: "admin@test.com",
|
||||
subject: "hello",
|
||||
contents: "testing",
|
||||
}
|
||||
let resp = generateResponse(inputs.to, inputs.from)
|
||||
mockFetch.mockImplementationOnce(() => ({
|
||||
headers: {
|
||||
raw: () => {
|
||||
return { "content-type": ["application/json"] }
|
||||
},
|
||||
get: () => ["application/json"],
|
||||
},
|
||||
json: jest.fn(() => resp),
|
||||
status: 200,
|
||||
text: jest.fn(),
|
||||
}))
|
||||
const res = await setup.runStep(setup.actions.SEND_EMAIL_SMTP.stepId, inputs)
|
||||
expect(res.response).toEqual(resp)
|
||||
expect(res.success).toEqual(true)
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
|
@ -0,0 +1,22 @@
|
|||
const setup = require("./utilities")
|
||||
|
||||
describe("test the server log action", () => {
|
||||
let config = setup.getConfig()
|
||||
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
inputs = {
|
||||
text: "log message",
|
||||
}
|
||||
})
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
it("should be able to log the text", async () => {
|
||||
|
||||
let res = await setup.runStep(setup.actions.SERVER_LOG.stepId,
|
||||
inputs
|
||||
)
|
||||
expect(res.message).toEqual(`App ${config.getAppId()} - ${inputs.text}`)
|
||||
expect(res.success).toEqual(true)
|
||||
})
|
||||
})
|
|
@ -31,7 +31,7 @@ export async function runInProd(fn: any) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function runStep(stepId: string, inputs: any) {
|
||||
export async function runStep(stepId: string, inputs: any, stepContext?: any) {
|
||||
async function run() {
|
||||
let step = await getAction(stepId)
|
||||
expect(step).toBeDefined()
|
||||
|
@ -39,7 +39,7 @@ export async function runStep(stepId: string, inputs: any) {
|
|||
throw new Error("No step found")
|
||||
}
|
||||
return step({
|
||||
context: {},
|
||||
context: stepContext || {},
|
||||
inputs,
|
||||
appId: config ? config.getAppId() : null,
|
||||
// don't really need an API key, mocked out usage quota, not being tested here
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
const setup = require("./utilities")
|
||||
const fetch = require("node-fetch")
|
||||
|
||||
jest.mock("node-fetch")
|
||||
|
||||
describe("test the outgoing webhook action", () => {
|
||||
let inputs
|
||||
let config = setup.getConfig()
|
||||
|
||||
beforeEach(async () => {
|
||||
await config.init()
|
||||
inputs = {
|
||||
value1: "test",
|
||||
url: "http://www.test.com",
|
||||
}
|
||||
})
|
||||
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
it("should be able to run the action", async () => {
|
||||
const res = await setup.runStep(setup.actions.zapier.stepId, inputs)
|
||||
expect(res.response.url).toEqual("http://www.test.com")
|
||||
expect(res.response.method).toEqual("post")
|
||||
expect(res.success).toEqual(true)
|
||||
})
|
||||
|
||||
})
|
|
@ -1,17 +0,0 @@
|
|||
const automationUtils = require("../automationUtils")
|
||||
|
||||
describe("automationUtils", () => {
|
||||
test("substituteLoopStep should allow multiple loop binding substitutes", () => {
|
||||
expect(automationUtils.substituteLoopStep(
|
||||
`{{ loop.currentItem._id }} {{ loop.currentItem._id }} {{ loop.currentItem._id }}`,
|
||||
"step.2"))
|
||||
.toBe(`{{ step.2.currentItem._id }} {{ step.2.currentItem._id }} {{ step.2.currentItem._id }}`)
|
||||
})
|
||||
|
||||
test("substituteLoopStep should handle not subsituting outside of curly braces", () => {
|
||||
expect(automationUtils.substituteLoopStep(
|
||||
`loop {{ loop.currentItem._id }}loop loop{{ loop.currentItem._id }}loop`,
|
||||
"step.2"))
|
||||
.toBe(`loop {{ step.2.currentItem._id }}loop loop{{ step.2.currentItem._id }}loop`)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,65 @@
|
|||
const automationUtils = require("../automationUtils")
|
||||
|
||||
describe("automationUtils", () => {
|
||||
describe("substituteLoopStep", () => {
|
||||
it("should allow multiple loop binding substitutes", () => {
|
||||
expect(
|
||||
automationUtils.substituteLoopStep(
|
||||
`{{ loop.currentItem._id }} {{ loop.currentItem._id }} {{ loop.currentItem._id }}`,
|
||||
"step.2"
|
||||
)
|
||||
).toBe(
|
||||
`{{ step.2.currentItem._id }} {{ step.2.currentItem._id }} {{ step.2.currentItem._id }}`
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle not subsituting outside of curly braces", () => {
|
||||
expect(
|
||||
automationUtils.substituteLoopStep(
|
||||
`loop {{ loop.currentItem._id }}loop loop{{ loop.currentItem._id }}loop`,
|
||||
"step.2"
|
||||
)
|
||||
).toBe(
|
||||
`loop {{ step.2.currentItem._id }}loop loop{{ step.2.currentItem._id }}loop`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("typeCastForLooping", () => {
|
||||
it("should parse to correct type", () => {
|
||||
expect(
|
||||
automationUtils.typecastForLooping(
|
||||
{ inputs: { option: "Array" } },
|
||||
{ binding: [1, 2, 3] }
|
||||
)
|
||||
).toEqual([1, 2, 3])
|
||||
expect(
|
||||
automationUtils.typecastForLooping(
|
||||
{ inputs: { option: "Array" } },
|
||||
{ binding: "[1, 2, 3]" }
|
||||
)
|
||||
).toEqual([1, 2, 3])
|
||||
expect(
|
||||
automationUtils.typecastForLooping(
|
||||
{ inputs: { option: "String" } },
|
||||
{ binding: [1, 2, 3] }
|
||||
)
|
||||
).toEqual("1,2,3")
|
||||
})
|
||||
it("should handle null values", () => {
|
||||
// expect it to handle where the binding is null
|
||||
expect(
|
||||
automationUtils.typecastForLooping(
|
||||
{ inputs: { option: "Array" } },
|
||||
{ binding: null }
|
||||
)
|
||||
).toEqual(null)
|
||||
expect(() =>
|
||||
automationUtils.typecastForLooping(
|
||||
{ inputs: { option: "Array" } },
|
||||
{ binding: "test" }
|
||||
)
|
||||
).toThrow()
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,3 +1,12 @@
|
|||
import { mocks } from "@budibase/backend-core/tests"
|
||||
|
||||
// init the licensing mock
|
||||
import * as pro from "@budibase/pro"
|
||||
mocks.licenses.init(pro)
|
||||
|
||||
// use unlimited license by default
|
||||
mocks.licenses.useUnlimited()
|
||||
|
||||
import { init as dbInit } from "../../db"
|
||||
dbInit()
|
||||
import env from "../../environment"
|
||||
|
|
|
@ -32,31 +32,8 @@ const LOOP_STEP_ID = actions.ACTION_DEFINITIONS.LOOP.stepId
|
|||
const CRON_STEP_ID = triggerDefs.CRON.stepId
|
||||
const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED }
|
||||
|
||||
function typecastForLooping(loopStep: LoopStep, input: LoopInput) {
|
||||
if (!input || !input.binding) {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
switch (loopStep.inputs.option) {
|
||||
case LoopStepType.ARRAY:
|
||||
if (typeof input.binding === "string") {
|
||||
return JSON.parse(input.binding)
|
||||
}
|
||||
break
|
||||
case LoopStepType.STRING:
|
||||
if (Array.isArray(input.binding)) {
|
||||
return input.binding.join(",")
|
||||
}
|
||||
break
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error("Unable to cast to correct type")
|
||||
}
|
||||
return input.binding
|
||||
}
|
||||
|
||||
function getLoopIterations(loopStep: LoopStep, input: LoopInput) {
|
||||
const binding = typecastForLooping(loopStep, input)
|
||||
const binding = automationUtils.typecastForLooping(loopStep, input)
|
||||
if (!loopStep || !binding) {
|
||||
return 1
|
||||
}
|
||||
|
@ -289,7 +266,7 @@ class Orchestrator {
|
|||
|
||||
let tempOutput = { items: loopSteps, iterations: iterationCount }
|
||||
try {
|
||||
newInput.binding = typecastForLooping(
|
||||
newInput.binding = automationUtils.typecastForLooping(
|
||||
loopStep as LoopStep,
|
||||
newInput
|
||||
)
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import { fixAutoColumnSubType } from "../utils"
|
||||
import { AutoFieldDefaultNames, AutoFieldSubTypes } from "../../../constants"
|
||||
|
||||
describe("rowProcessor utility", () => {
|
||||
describe("fixAutoColumnSubType", () => {
|
||||
let schema = {
|
||||
name: "",
|
||||
type: "link",
|
||||
subtype: "", // missing subtype
|
||||
icon: "ri-magic-line",
|
||||
autocolumn: true,
|
||||
constraints: { type: "array", presence: false },
|
||||
tableId: "ta_users",
|
||||
fieldName: "test-Updated By",
|
||||
relationshipType: "many-to-many",
|
||||
sortable: false,
|
||||
}
|
||||
|
||||
it("updates the schema with the correct subtype", async () => {
|
||||
schema.name = AutoFieldDefaultNames.CREATED_BY
|
||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||
AutoFieldSubTypes.CREATED_BY
|
||||
)
|
||||
schema.subtype = ""
|
||||
|
||||
schema.name = AutoFieldDefaultNames.UPDATED_BY
|
||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||
AutoFieldSubTypes.UPDATED_BY
|
||||
)
|
||||
schema.subtype = ""
|
||||
|
||||
schema.name = AutoFieldDefaultNames.CREATED_AT
|
||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||
AutoFieldSubTypes.CREATED_AT
|
||||
)
|
||||
schema.subtype = ""
|
||||
|
||||
schema.name = AutoFieldDefaultNames.UPDATED_AT
|
||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||
AutoFieldSubTypes.UPDATED_AT
|
||||
)
|
||||
schema.subtype = ""
|
||||
|
||||
schema.name = AutoFieldDefaultNames.AUTO_ID
|
||||
expect(fixAutoColumnSubType(schema).subtype).toEqual(
|
||||
AutoFieldSubTypes.AUTO_ID
|
||||
)
|
||||
schema.subtype = ""
|
||||
})
|
||||
|
||||
it("returns the column if subtype exists", async () => {
|
||||
schema.subtype = AutoFieldSubTypes.CREATED_BY
|
||||
schema.name = AutoFieldDefaultNames.CREATED_AT
|
||||
expect(fixAutoColumnSubType(schema)).toEqual(schema)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,23 @@
|
|||
import { enrichPluginURLs } from "../plugins"
|
||||
const env = require("../../environment")
|
||||
jest.mock("../../environment")
|
||||
|
||||
describe("plugins utility", () => {
|
||||
let pluginsArray: any = [
|
||||
{
|
||||
name: "test-plugin",
|
||||
},
|
||||
]
|
||||
it("enriches the plugins url self-hosted", async () => {
|
||||
let result = enrichPluginURLs(pluginsArray)
|
||||
expect(result[0].jsUrl).toEqual("/plugins/test-plugin/plugin.min.js")
|
||||
})
|
||||
|
||||
it("enriches the plugins url cloud", async () => {
|
||||
env.SELF_HOSTED = 0
|
||||
let result = enrichPluginURLs(pluginsArray)
|
||||
expect(result[0].jsUrl).toEqual(
|
||||
"https://cdn.budi.live/test-plugin/plugin.min.js"
|
||||
)
|
||||
})
|
||||
})
|
|
@ -19,6 +19,8 @@ if (!process.env.CI) {
|
|||
}
|
||||
// add pro sources if they exist
|
||||
if (fs.existsSync("../../../budibase-pro")) {
|
||||
config.moduleNameMapper["@budibase/pro/(.*)"] =
|
||||
"<rootDir>/../../../budibase-pro/packages/pro/$1"
|
||||
config.moduleNameMapper["@budibase/pro"] =
|
||||
"<rootDir>/../../../budibase-pro/packages/pro/src"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import { events } from "@budibase/backend-core"
|
||||
import { structures, TestConfiguration, mocks } from "../../../../tests"
|
||||
|
||||
describe("/api/global/groups", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
mocks.licenses.useGroups()
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
it("should be able to create a new group", async () => {
|
||||
const group = structures.groups.UserGroup()
|
||||
await config.api.groups.saveGroup(group)
|
||||
expect(events.group.created).toBeCalledTimes(1)
|
||||
expect(events.group.updated).not.toBeCalled()
|
||||
expect(events.group.permissionsEdited).not.toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
it("should be able to update a basic group", async () => {
|
||||
const group = structures.groups.UserGroup()
|
||||
let oldGroup = await config.api.groups.saveGroup(group)
|
||||
|
||||
let updatedGroup = {
|
||||
...oldGroup.body,
|
||||
...group,
|
||||
name: "New Name",
|
||||
}
|
||||
await config.api.groups.saveGroup(updatedGroup)
|
||||
|
||||
expect(events.group.updated).toBeCalledTimes(1)
|
||||
expect(events.group.permissionsEdited).not.toBeCalled()
|
||||
})
|
||||
|
||||
describe("destroy", () => {
|
||||
it("should be able to delete a basic group", async () => {
|
||||
const group = structures.groups.UserGroup()
|
||||
let oldGroup = await config.api.groups.saveGroup(group)
|
||||
await config.api.groups.deleteGroup(
|
||||
oldGroup.body._id,
|
||||
oldGroup.body._rev
|
||||
)
|
||||
|
||||
expect(events.group.deleted).toBeCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,7 +1,5 @@
|
|||
import { TestConfiguration } from "../../../../tests"
|
||||
|
||||
// TODO
|
||||
|
||||
describe("/api/global/license", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
|
|
|
@ -1,11 +1,47 @@
|
|||
import { TestConfiguration } from "../../../../tests"
|
||||
import { structures, TestConfiguration } from "../../../../tests"
|
||||
import { context, db, permissions, roles } from "@budibase/backend-core"
|
||||
import { Mock } from "jest-mock"
|
||||
|
||||
// TODO
|
||||
jest.mock("@budibase/backend-core", () => {
|
||||
const core = jest.requireActual("@budibase/backend-core")
|
||||
return {
|
||||
...core,
|
||||
db: {
|
||||
...core.db,
|
||||
},
|
||||
context: {
|
||||
...core.context,
|
||||
getAppDB: jest.fn(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const appDb = db.getDB("app_test")
|
||||
const mockAppDB = context.getAppDB as Mock
|
||||
mockAppDB.mockReturnValue(appDb)
|
||||
|
||||
async function addAppMetadata() {
|
||||
await appDb.put({
|
||||
_id: "app_metadata",
|
||||
appId: "app_test",
|
||||
name: "New App",
|
||||
version: "version",
|
||||
url: "url",
|
||||
})
|
||||
}
|
||||
|
||||
describe("/api/global/roles", () => {
|
||||
const config = new TestConfiguration()
|
||||
const role = new roles.Role(
|
||||
db.generateRoleID("newRole"),
|
||||
roles.BUILTIN_ROLE_IDS.BASIC,
|
||||
permissions.BuiltinPermissionID.READ_ONLY
|
||||
)
|
||||
|
||||
beforeAll(async () => {
|
||||
console.debug(role)
|
||||
appDb.put(role)
|
||||
await addAppMetadata()
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
|
@ -18,10 +54,35 @@ describe("/api/global/roles", () => {
|
|||
})
|
||||
|
||||
describe("GET /api/global/roles", () => {
|
||||
it("retrieves roles", () => {})
|
||||
it("retrieves roles", async () => {
|
||||
const res = await config.api.roles.get()
|
||||
expect(res.body).toBeDefined()
|
||||
expect(res.body["app_test"].roles.length).toEqual(5)
|
||||
expect(res.body["app_test"].roles.map((r: any) => r._id)).toContain(
|
||||
role._id
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("GET /api/global/roles/:appId", () => {})
|
||||
describe("GET api/global/roles/:appId", () => {
|
||||
it("finds a role by appId", async () => {
|
||||
const res = await config.api.roles.find("app_test")
|
||||
expect(res.body).toBeDefined()
|
||||
expect(res.body.name).toEqual("New App")
|
||||
})
|
||||
})
|
||||
|
||||
describe("DELETE /api/global/roles/:appId", () => {})
|
||||
describe("DELETE /api/global/roles/:appId", () => {
|
||||
it("removes an app role", async () => {
|
||||
let user = structures.users.user()
|
||||
user.roles = {
|
||||
app_test: "role1",
|
||||
}
|
||||
const userResponse = await config.createUser(user)
|
||||
const res = await config.api.roles.remove("app_test")
|
||||
const updatedUser = await config.api.users.getUser(userResponse._id!)
|
||||
expect(updatedUser.body.roles).not.toHaveProperty("app_test")
|
||||
expect(res.body.message).toEqual("App role removed from all users")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,17 @@
|
|||
import {
|
||||
addBaseTemplates,
|
||||
EmailTemplates,
|
||||
getTemplates,
|
||||
} from "../../../../constants/templates"
|
||||
import {
|
||||
EmailTemplatePurpose,
|
||||
TemplateMetadata,
|
||||
TemplateMetadataNames,
|
||||
TemplateType,
|
||||
} from "../../../../constants"
|
||||
import { TestConfiguration } from "../../../../tests"
|
||||
import { join } from "path"
|
||||
import { readStaticFile } from "../../../../../src/utilities/fileSystem"
|
||||
|
||||
// TODO
|
||||
|
||||
|
@ -18,18 +31,85 @@ describe("/api/global/template", () => {
|
|||
})
|
||||
|
||||
describe("GET /api/global/template/definitions", () => {
|
||||
it("retrieves definitions", () => {})
|
||||
describe("retrieves definitions", () => {
|
||||
it("checks description definitions", async () => {
|
||||
let result = await config.api.templates.definitions()
|
||||
|
||||
expect(result.body.info[EmailTemplatePurpose.BASE].description).toEqual(
|
||||
TemplateMetadata[TemplateType.EMAIL][0].description
|
||||
)
|
||||
expect(
|
||||
result.body.info[EmailTemplatePurpose.PASSWORD_RECOVERY].description
|
||||
).toEqual(TemplateMetadata[TemplateType.EMAIL][1].description)
|
||||
expect(
|
||||
result.body.info[EmailTemplatePurpose.WELCOME].description
|
||||
).toEqual(TemplateMetadata[TemplateType.EMAIL][2].description)
|
||||
expect(
|
||||
result.body.info[EmailTemplatePurpose.INVITATION].description
|
||||
).toEqual(TemplateMetadata[TemplateType.EMAIL][3].description)
|
||||
expect(
|
||||
result.body.info[EmailTemplatePurpose.CUSTOM].description
|
||||
).toEqual(TemplateMetadata[TemplateType.EMAIL][4].description)
|
||||
})
|
||||
|
||||
it("checks description bindings", async () => {
|
||||
let result = await config.api.templates.definitions()
|
||||
|
||||
expect(result.body.bindings[EmailTemplatePurpose.BASE]).toEqual(
|
||||
TemplateMetadata[TemplateType.EMAIL][0].bindings
|
||||
)
|
||||
expect(
|
||||
result.body.bindings[EmailTemplatePurpose.PASSWORD_RECOVERY]
|
||||
).toEqual(TemplateMetadata[TemplateType.EMAIL][1].bindings)
|
||||
expect(result.body.bindings[EmailTemplatePurpose.WELCOME]).toEqual(
|
||||
TemplateMetadata[TemplateType.EMAIL][2].bindings
|
||||
)
|
||||
expect(result.body.bindings[EmailTemplatePurpose.INVITATION]).toEqual(
|
||||
TemplateMetadata[TemplateType.EMAIL][3].bindings
|
||||
)
|
||||
expect(result.body.bindings[EmailTemplatePurpose.CUSTOM]).toEqual(
|
||||
TemplateMetadata[TemplateType.EMAIL][4].bindings
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /api/global/template", () => {})
|
||||
describe("POST /api/global/template", () => {
|
||||
it("adds a new template", async () => {
|
||||
let purpose = "base"
|
||||
let contents = "Test contents"
|
||||
let updatedTemplate = {
|
||||
contents: contents,
|
||||
purpose: purpose,
|
||||
type: "email",
|
||||
}
|
||||
await config.api.templates.saveTemplate(updatedTemplate)
|
||||
let res = await config.api.templates.getTemplate()
|
||||
let newTemplate = res.body.find((t: any) => (t.purpose = purpose))
|
||||
expect(newTemplate.contents).toEqual(contents)
|
||||
})
|
||||
})
|
||||
|
||||
describe("GET /api/global/template", () => {})
|
||||
|
||||
describe("GET /api/global/template/:type", () => {})
|
||||
|
||||
describe("GET /api/global/template/:ownerId", () => {})
|
||||
|
||||
describe("GET /api/global/template/:id", () => {})
|
||||
|
||||
describe("DELETE /api/global/template/:id/:rev", () => {})
|
||||
describe("GET /api/global/template", () => {
|
||||
it("fetches templates", async () => {
|
||||
let res = await config.api.templates.getTemplate()
|
||||
expect(
|
||||
res.body.find((t: any) => t.purpose === EmailTemplatePurpose.BASE)
|
||||
).toBeDefined()
|
||||
expect(
|
||||
res.body.find((t: any) => t.purpose === EmailTemplatePurpose.CUSTOM)
|
||||
).toBeDefined()
|
||||
expect(
|
||||
res.body.find((t: any) => t.purpose === EmailTemplatePurpose.INVITATION)
|
||||
).toBeDefined()
|
||||
expect(
|
||||
res.body.find(
|
||||
(t: any) => t.purpose === EmailTemplatePurpose.PASSWORD_RECOVERY
|
||||
)
|
||||
).toBeDefined()
|
||||
expect(
|
||||
res.body.find((t: any) => t.purpose === EmailTemplatePurpose.WELCOME)
|
||||
).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -116,7 +116,7 @@ describe("/api/global/users", () => {
|
|||
|
||||
it("should ignore users existing in other tenants", async () => {
|
||||
const user = await config.createUser()
|
||||
jest.resetAllMocks()
|
||||
jest.clearAllMocks()
|
||||
|
||||
await tenancy.doInTenant(TENANT_1, async () => {
|
||||
const response = await config.api.users.bulkCreateUsers([user])
|
||||
|
@ -229,7 +229,7 @@ describe("/api/global/users", () => {
|
|||
|
||||
it("should not be able to create user that exists in other tenant", async () => {
|
||||
const user = await config.createUser()
|
||||
jest.resetAllMocks()
|
||||
jest.clearAllMocks()
|
||||
|
||||
await tenancy.doInTenant(TENANT_1, async () => {
|
||||
delete user._id
|
||||
|
|
|
@ -27,6 +27,14 @@ export enum EmailTemplatePurpose {
|
|||
CUSTOM = "custom",
|
||||
}
|
||||
|
||||
export enum TemplateMetadataNames {
|
||||
BASE = "Base format",
|
||||
PASSWORD_RECOVERY = "Password recovery",
|
||||
WELCOME = "User welcome",
|
||||
INVITATION = "User invitation",
|
||||
CUSTOM = "Custom",
|
||||
}
|
||||
|
||||
export enum InternalTemplateBinding {
|
||||
PLATFORM_URL = "platformUrl",
|
||||
COMPANY = "company",
|
||||
|
@ -93,7 +101,7 @@ export const TemplateBindings = {
|
|||
export const TemplateMetadata = {
|
||||
[TemplateType.EMAIL]: [
|
||||
{
|
||||
name: "Base format",
|
||||
name: TemplateMetadataNames.BASE,
|
||||
description:
|
||||
"This is the base template, all others are based on it. The {{ body }} will be replaced with another email template.",
|
||||
category: "miscellaneous",
|
||||
|
@ -110,7 +118,7 @@ export const TemplateMetadata = {
|
|||
],
|
||||
},
|
||||
{
|
||||
name: "Password recovery",
|
||||
name: TemplateMetadataNames.PASSWORD_RECOVERY,
|
||||
description:
|
||||
"When a user requests a password reset they will receive an email built with this template.",
|
||||
category: "user management",
|
||||
|
@ -129,7 +137,7 @@ export const TemplateMetadata = {
|
|||
],
|
||||
},
|
||||
{
|
||||
name: "User welcome",
|
||||
name: TemplateMetadataNames.WELCOME,
|
||||
description:
|
||||
"When a new user is added they will be sent a welcome email using this template.",
|
||||
category: "user management",
|
||||
|
@ -137,7 +145,7 @@ export const TemplateMetadata = {
|
|||
bindings: [],
|
||||
},
|
||||
{
|
||||
name: "User invitation",
|
||||
name: TemplateMetadataNames.INVITATION,
|
||||
description:
|
||||
"When inviting a user via the email on-boarding this template will be used.",
|
||||
category: "user management",
|
||||
|
@ -156,7 +164,7 @@ export const TemplateMetadata = {
|
|||
],
|
||||
},
|
||||
{
|
||||
name: "Custom",
|
||||
name: TemplateMetadataNames.CUSTOM,
|
||||
description:
|
||||
"A custom template, this is currently used for SMTP email actions in automations.",
|
||||
category: "automations",
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import "./mocks"
|
||||
import mocks from "./mocks"
|
||||
|
||||
// init the licensing mock
|
||||
import * as pro from "@budibase/pro"
|
||||
mocks.licenses.init(pro)
|
||||
|
||||
// use unlimited license by default
|
||||
mocks.licenses.useUnlimited()
|
||||
|
||||
import * as dbConfig from "../db"
|
||||
dbConfig.init()
|
||||
import env from "../environment"
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { UserGroup } from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
|
||||
export class GroupsAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
saveGroup = (group: UserGroup) => {
|
||||
return this.request
|
||||
.post(`/api/global/groups`)
|
||||
.send(group)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
}
|
||||
|
||||
deleteGroup = (id: string, rev: string) => {
|
||||
return this.request
|
||||
.delete(`/api/global/groups/${id}/${rev}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
}
|
||||
}
|
|
@ -10,7 +10,10 @@ import { MigrationAPI } from "./migrations"
|
|||
import { StatusAPI } from "./status"
|
||||
import { RestoreAPI } from "./restore"
|
||||
import { TenantAPI } from "./tenants"
|
||||
|
||||
import { GroupsAPI } from "./groups"
|
||||
import { RolesAPI } from "./roles"
|
||||
import { TemplatesAPI } from "./templates"
|
||||
import { LicenseAPI } from "./license"
|
||||
export default class API {
|
||||
accounts: AccountAPI
|
||||
auth: AuthAPI
|
||||
|
@ -23,6 +26,10 @@ export default class API {
|
|||
status: StatusAPI
|
||||
restore: RestoreAPI
|
||||
tenants: TenantAPI
|
||||
groups: GroupsAPI
|
||||
roles: RolesAPI
|
||||
templates: TemplatesAPI
|
||||
license: LicenseAPI
|
||||
|
||||
constructor(config: TestConfiguration) {
|
||||
this.accounts = new AccountAPI(config)
|
||||
|
@ -36,5 +43,9 @@ export default class API {
|
|||
this.status = new StatusAPI(config)
|
||||
this.restore = new RestoreAPI(config)
|
||||
this.tenants = new TenantAPI(config)
|
||||
this.groups = new GroupsAPI(config)
|
||||
this.roles = new RolesAPI(config)
|
||||
this.templates = new TemplatesAPI(config)
|
||||
this.license = new LicenseAPI(config)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
|
||||
export class LicenseAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
activate = async (licenseKey: string) => {
|
||||
return this.request
|
||||
.post("/api/global/license/activate")
|
||||
.send({ licenseKey: licenseKey })
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI, TestAPIOpts } from "./base"
|
||||
|
||||
export class RolesAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
get = (opts?: TestAPIOpts) => {
|
||||
return this.request
|
||||
.get(`/api/global/roles`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(opts?.status ? opts.status : 200)
|
||||
}
|
||||
|
||||
find = (appId: string, opts?: TestAPIOpts) => {
|
||||
return this.request
|
||||
.get(`/api/global/roles/${appId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(opts?.status ? opts.status : 200)
|
||||
}
|
||||
|
||||
remove = (appId: string, opts?: TestAPIOpts) => {
|
||||
return this.request
|
||||
.delete(`/api/global/roles/${appId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(opts?.status ? opts.status : 200)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI, TestAPIOpts } from "./base"
|
||||
|
||||
export class TemplatesAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
definitions = (opts?: TestAPIOpts) => {
|
||||
return this.request
|
||||
.get(`/api/global/template/definitions`)
|
||||
.set(opts?.headers ? opts.headers : this.config.defaultHeaders())
|
||||
.expect(opts?.status ? opts.status : 200)
|
||||
}
|
||||
|
||||
getTemplate = (opts?: TestAPIOpts) => {
|
||||
return this.request
|
||||
.get(`/api/global/template`)
|
||||
.set(opts?.headers ? opts.headers : this.config.defaultHeaders())
|
||||
.expect(opts?.status ? opts.status : 200)
|
||||
}
|
||||
|
||||
saveTemplate = (data: any, opts?: TestAPIOpts) => {
|
||||
return this.request
|
||||
.post(`/api/global/template`)
|
||||
.send(data)
|
||||
.set(opts?.headers ? opts.headers : this.config.defaultHeaders())
|
||||
.expect(opts?.status ? opts.status : 200)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
const email = require("./email")
|
||||
import { mocks as coreMocks } from "@budibase/backend-core/tests"
|
||||
import { mocks } from "@budibase/backend-core/tests"
|
||||
|
||||
export = {
|
||||
email,
|
||||
...coreMocks,
|
||||
...mocks,
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ export const UserGroup = () => {
|
|||
color: "var(--spectrum-global-color-blue-600)",
|
||||
icon: "UserGroup",
|
||||
name: "New group",
|
||||
roles: {},
|
||||
roles: { app_uuid1: "ADMIN", app_uuid2: "POWER" },
|
||||
users: [],
|
||||
}
|
||||
return group
|
||||
|
|
|
@ -10,7 +10,7 @@ export const user = (userProps?: any): User => {
|
|||
return {
|
||||
email: newEmail(),
|
||||
password: "test",
|
||||
roles: {},
|
||||
roles: { app_test: "admin" },
|
||||
...userProps,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"@budibase/types": ["../types/src"],
|
||||
"@budibase/backend-core": ["../backend-core/src"],
|
||||
"@budibase/backend-core/*": ["../backend-core/*"],
|
||||
"@budibase/pro": ["../../../budibase-pro/packages/pro/src"]
|
||||
"@budibase/pro": ["../../../budibase-pro/packages/pro/src"],
|
||||
"@budibase/pro/*": ["../../../budibase-pro/packages/pro/*"]
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
|
@ -25,7 +26,6 @@
|
|||
"package.json"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
|
@ -1,25 +1,30 @@
|
|||
echo "Linking backend-core"
|
||||
cd packages/backend-core
|
||||
yarn unlink
|
||||
yarn link
|
||||
cd -
|
||||
|
||||
echo "Linking string-templates"
|
||||
cd packages/string-templates
|
||||
cd packages/string-templates
|
||||
yarn unlink
|
||||
yarn link
|
||||
cd -
|
||||
|
||||
echo "Linking types"
|
||||
cd packages/types
|
||||
cd packages/types
|
||||
yarn unlink
|
||||
yarn link
|
||||
cd -
|
||||
|
||||
echo "Linking bbui"
|
||||
cd packages/bbui
|
||||
cd packages/bbui
|
||||
yarn unlink
|
||||
yarn link
|
||||
cd -
|
||||
|
||||
echo "Linking frontend-core"
|
||||
cd packages/frontend-core
|
||||
yarn unlink
|
||||
yarn link
|
||||
cd -
|
||||
|
||||
|
@ -30,6 +35,7 @@ if [ -d "../budibase-pro" ]; then
|
|||
|
||||
cd packages/pro
|
||||
echo "Linking pro"
|
||||
yarn unlink
|
||||
yarn link
|
||||
|
||||
echo "Linking backend-core to pro"
|
||||
|
|
Loading…
Reference in New Issue