forked from tsai/budibase
122 changed files with 2525 additions and 519 deletions
@ -1,38 +1,42 @@ |
|||||
<script> |
<script> |
||||
import { createEventDispatcher } from 'svelte' |
import { createEventDispatcher } from "svelte" |
||||
import Colorpicker from "@budibase/colorpicker" |
import Colorpicker from "@budibase/colorpicker" |
||||
|
|
||||
const dispatch = createEventDispatcher(); |
const dispatch = createEventDispatcher() |
||||
|
|
||||
export let value |
export let value |
||||
|
|
||||
const WAIT = 150; |
const WAIT = 150 |
||||
|
|
||||
function throttle(callback, wait, immediate = false) { |
function throttle(callback, wait, immediate = false) { |
||||
let timeout = null |
let timeout = null |
||||
let initialCall = true |
let initialCall = true |
||||
|
|
||||
return function() { |
return function() { |
||||
const callNow = immediate && initialCall |
const callNow = immediate && initialCall |
||||
const next = () => { |
const next = () => { |
||||
callback.apply(this, arguments) |
callback.apply(this, arguments) |
||||
timeout = null |
timeout = null |
||||
} |
} |
||||
|
|
||||
if (callNow) { |
if (callNow) { |
||||
initialCall = false |
initialCall = false |
||||
next() |
next() |
||||
} |
} |
||||
|
|
||||
if (!timeout) { |
if (!timeout) { |
||||
timeout = setTimeout(next, wait) |
timeout = setTimeout(next, wait) |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
const onChange = throttle(e => { |
const onChange = throttle( |
||||
dispatch('change', e.detail) |
e => { |
||||
}, WAIT, true) |
dispatch("change", e.detail) |
||||
|
}, |
||||
|
WAIT, |
||||
|
true |
||||
|
) |
||||
</script> |
</script> |
||||
|
|
||||
<Colorpicker value={value || '#C4C4C4'} on:change={onChange} /> |
<Colorpicker value={value || '#C4C4C4'} on:change={onChange} /> |
||||
|
|||||
@ -1,6 +1,15 @@ |
|||||
<script> |
<script> |
||||
import { store } from "builderStore" |
import { automationStore } from "builderStore" |
||||
import { params } from "@sveltech/routify" |
import { params } from "@sveltech/routify" |
||||
|
|
||||
store.actions.layouts.select($params.layout) |
if ($params.automation) { |
||||
|
const automation = $automationStore.automations.find( |
||||
|
m => m._id === $params.automation |
||||
|
) |
||||
|
if (automation) { |
||||
|
automationStore.actions.select(automation) |
||||
|
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
|
<slot /> |
||||
|
|||||
@ -0,0 +1,5 @@ |
|||||
|
<script> |
||||
|
import AutomationBuilder from "components/automation/AutomationBuilder/AutomationBuilder.svelte" |
||||
|
</script> |
||||
|
|
||||
|
<AutomationBuilder /> |
||||
@ -1,5 +1,27 @@ |
|||||
<script> |
<script> |
||||
import AutomationBuilder from "components/automation/AutomationBuilder/AutomationBuilder.svelte" |
import { goto, leftover } from "@sveltech/routify" |
||||
|
import { onMount } from "svelte" |
||||
|
import { automationStore } from "builderStore" |
||||
|
|
||||
|
onMount(async () => { |
||||
|
// navigate to first automation in list, if not already selected |
||||
|
if ( |
||||
|
!$leftover && |
||||
|
$automationStore.automations.length > 0 && |
||||
|
(!$automationStore.selectedAutomation || |
||||
|
!$automationStore.selectedAutomation?.automation?._id) |
||||
|
) { |
||||
|
$goto(`../${$automationStore.automations[0]._id}`) |
||||
|
} |
||||
|
}) |
||||
</script> |
</script> |
||||
|
|
||||
<AutomationBuilder /> |
<i>Create your first automation to get started</i> |
||||
|
|
||||
|
<style> |
||||
|
i { |
||||
|
font-size: var(--font-size-m); |
||||
|
color: var(--grey-5); |
||||
|
margin-top: 2px; |
||||
|
} |
||||
|
</style> |
||||
|
|||||
@ -0,0 +1,13 @@ |
|||||
|
<script> |
||||
|
import { params } from "@sveltech/routify" |
||||
|
import { backendUiStore } from "builderStore" |
||||
|
|
||||
|
if ($params.query) { |
||||
|
const query = $backendUiStore.queries.find(m => m._id === $params.query) |
||||
|
if (query) { |
||||
|
backendUiStore.actions.queries.select(query) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<slot /> |
||||
@ -0,0 +1,23 @@ |
|||||
|
<script> |
||||
|
import { backendUiStore } from "builderStore" |
||||
|
import { goto } from "@sveltech/routify" |
||||
|
import { onMount } from "svelte" |
||||
|
|
||||
|
onMount(async () => { |
||||
|
// navigate to first table in list, if not already selected |
||||
|
$backendUiStore.datasources.length > 0 && |
||||
|
$goto(`../${$backendUiStore.datasources[0]._id}`) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
{#if $backendUiStore.tables.length === 0} |
||||
|
<i>Connect your first datasource to start building.</i> |
||||
|
{:else}<i>Select a datasource to edit</i>{/if} |
||||
|
|
||||
|
<style> |
||||
|
i { |
||||
|
font-size: var(--font-size-m); |
||||
|
color: var(--grey-5); |
||||
|
margin-top: 2px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,24 @@ |
|||||
|
const elastic = {} |
||||
|
|
||||
|
elastic.Client = function() { |
||||
|
this.index = jest.fn().mockResolvedValue({ body: [] }) |
||||
|
this.search = jest.fn().mockResolvedValue({ |
||||
|
body: { |
||||
|
hits: { |
||||
|
hits: [ |
||||
|
{ |
||||
|
_source: { |
||||
|
name: "test", |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
}, |
||||
|
}) |
||||
|
this.update = jest.fn().mockResolvedValue({ body: [] }) |
||||
|
this.delete = jest.fn().mockResolvedValue({ body: [] }) |
||||
|
|
||||
|
this.close = jest.fn() |
||||
|
} |
||||
|
|
||||
|
module.exports = elastic |
||||
@ -0,0 +1,18 @@ |
|||||
|
class Email { |
||||
|
constructor() { |
||||
|
this.apiKey = null |
||||
|
} |
||||
|
|
||||
|
setApiKey(apiKey) { |
||||
|
this.apiKey = apiKey |
||||
|
} |
||||
|
|
||||
|
async send(msg) { |
||||
|
if (msg.to === "invalid@test.com") { |
||||
|
throw "Invalid" |
||||
|
} |
||||
|
return msg |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = new Email() |
||||
@ -0,0 +1,5 @@ |
|||||
|
function Airtable() { |
||||
|
this.base = jest.fn() |
||||
|
} |
||||
|
|
||||
|
module.exports = Airtable |
||||
@ -0,0 +1,21 @@ |
|||||
|
const arangodb = {} |
||||
|
|
||||
|
arangodb.Database = function() { |
||||
|
this.query = jest.fn(() => ({ |
||||
|
all: jest.fn(), |
||||
|
})) |
||||
|
this.collection = jest.fn(() => "collection") |
||||
|
this.close = jest.fn() |
||||
|
} |
||||
|
|
||||
|
arangodb.aql = (strings, ...args) => { |
||||
|
let str = strings.join("{}") |
||||
|
|
||||
|
for (let arg of args) { |
||||
|
str = str.replace("{}", arg) |
||||
|
} |
||||
|
|
||||
|
return str |
||||
|
} |
||||
|
|
||||
|
module.exports = arangodb |
||||
@ -0,0 +1,38 @@ |
|||||
|
const aws = {} |
||||
|
|
||||
|
const response = body => () => ({ promise: () => body }) |
||||
|
|
||||
|
function DocumentClient() { |
||||
|
this.put = jest.fn(response({})) |
||||
|
this.query = jest.fn( |
||||
|
response({ |
||||
|
Items: [], |
||||
|
}) |
||||
|
) |
||||
|
this.scan = jest.fn( |
||||
|
response({ |
||||
|
Items: [ |
||||
|
{ |
||||
|
Name: "test", |
||||
|
}, |
||||
|
], |
||||
|
}) |
||||
|
) |
||||
|
this.get = jest.fn(response({})) |
||||
|
this.update = jest.fn(response({})) |
||||
|
this.delete = jest.fn(response({})) |
||||
|
} |
||||
|
|
||||
|
function S3() { |
||||
|
this.listObjects = jest.fn( |
||||
|
response({ |
||||
|
Contents: {}, |
||||
|
}) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
aws.DynamoDB = { DocumentClient } |
||||
|
aws.S3 = S3 |
||||
|
aws.config = { update: jest.fn() } |
||||
|
|
||||
|
module.exports = aws |
||||
@ -0,0 +1,19 @@ |
|||||
|
const mongodb = {} |
||||
|
|
||||
|
mongodb.MongoClient = function() { |
||||
|
this.connect = jest.fn() |
||||
|
this.close = jest.fn() |
||||
|
this.insertOne = jest.fn() |
||||
|
this.find = jest.fn(() => ({ toArray: () => [] })) |
||||
|
|
||||
|
this.collection = jest.fn(() => ({ |
||||
|
insertOne: this.insertOne, |
||||
|
find: this.find, |
||||
|
})) |
||||
|
|
||||
|
this.db = () => ({ |
||||
|
collection: this.collection, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
module.exports = mongodb |
||||
@ -0,0 +1,14 @@ |
|||||
|
const mssql = {} |
||||
|
|
||||
|
mssql.query = jest.fn(() => ({ |
||||
|
recordset: [ |
||||
|
{ |
||||
|
a: "string", |
||||
|
b: 1, |
||||
|
}, |
||||
|
], |
||||
|
})) |
||||
|
|
||||
|
mssql.connect = jest.fn(() => ({ recordset: [] })) |
||||
|
|
||||
|
module.exports = mssql |
||||
@ -0,0 +1,10 @@ |
|||||
|
const mysql = {} |
||||
|
|
||||
|
const client = { |
||||
|
connect: jest.fn(), |
||||
|
query: jest.fn(), |
||||
|
} |
||||
|
|
||||
|
mysql.createConnection = jest.fn(() => client) |
||||
|
|
||||
|
module.exports = mysql |
||||
@ -1,17 +1,35 @@ |
|||||
const fetch = jest.requireActual("node-fetch") |
const fetch = jest.requireActual("node-fetch") |
||||
|
|
||||
module.exports = async (url, opts) => { |
module.exports = async (url, opts) => { |
||||
// mocked data based on url
|
function json(body, status = 200) { |
||||
if (url.includes("api/apps")) { |
|
||||
return { |
return { |
||||
|
status, |
||||
json: async () => { |
json: async () => { |
||||
return { |
return body |
||||
app1: { |
|
||||
url: "/app1", |
|
||||
}, |
|
||||
} |
|
||||
}, |
}, |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
// mocked data based on url
|
||||
|
if (url.includes("api/apps")) { |
||||
|
return json({ |
||||
|
app1: { |
||||
|
url: "/app1", |
||||
|
}, |
||||
|
}) |
||||
|
} else if (url.includes("test.com")) { |
||||
|
return json({ |
||||
|
body: opts.body, |
||||
|
url, |
||||
|
method: opts.method, |
||||
|
}) |
||||
|
} else if (url.includes("invalid.com")) { |
||||
|
return json( |
||||
|
{ |
||||
|
invalid: true, |
||||
|
}, |
||||
|
404 |
||||
|
) |
||||
|
} |
||||
return fetch(url, opts) |
return fetch(url, opts) |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,9 @@ |
|||||
|
function CouchDB() { |
||||
|
this.post = jest.fn() |
||||
|
this.allDocs = jest.fn(() => ({ rows: [] })) |
||||
|
this.put = jest.fn() |
||||
|
this.remove = jest.fn() |
||||
|
this.plugin = jest.fn() |
||||
|
} |
||||
|
|
||||
|
module.exports = CouchDB |
||||
@ -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"), |
|
||||
} |
|
||||
@ -0,0 +1,152 @@ |
|||||
|
const automation = require("../index") |
||||
|
const usageQuota = require("../../utilities/usageQuota") |
||||
|
const thread = require("../thread") |
||||
|
const triggers = require("../triggers") |
||||
|
const { basicAutomation, basicTable } = require("../../tests/utilities/structures") |
||||
|
const { wait } = require("../../utilities") |
||||
|
const env = require("../../environment") |
||||
|
const { makePartial } = require("../../tests/utilities") |
||||
|
const { cleanInputValues } = require("../automationUtils") |
||||
|
const setup = require("./utilities") |
||||
|
|
||||
|
let workerJob |
||||
|
|
||||
|
jest.mock("../../utilities/usageQuota") |
||||
|
usageQuota.getAPIKey.mockReturnValue({ apiKey: "test" }) |
||||
|
jest.mock("../thread") |
||||
|
jest.spyOn(global.console, "error") |
||||
|
jest.mock("worker-farm", () => { |
||||
|
return () => { |
||||
|
const value = jest |
||||
|
.fn() |
||||
|
.mockReturnValueOnce(undefined) |
||||
|
.mockReturnValueOnce("Error") |
||||
|
return (input, callback) => { |
||||
|
workerJob = input |
||||
|
if (callback) { |
||||
|
callback(value()) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
describe("Run through some parts of the automations system", () => { |
||||
|
let config = setup.getConfig() |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
await automation.init() |
||||
|
await config.init() |
||||
|
}) |
||||
|
|
||||
|
afterAll(setup.afterAll) |
||||
|
|
||||
|
it("should be able to init in builder", async () => { |
||||
|
await triggers.externalTrigger(basicAutomation(), { a: 1 }) |
||||
|
await wait(100) |
||||
|
expect(workerJob).toBeUndefined() |
||||
|
expect(thread).toHaveBeenCalled() |
||||
|
}) |
||||
|
|
||||
|
it("should be able to init in cloud", async () => { |
||||
|
env.CLOUD = true |
||||
|
env.BUDIBASE_ENVIRONMENT = "PRODUCTION" |
||||
|
await triggers.externalTrigger(basicAutomation(), { a: 1 }) |
||||
|
await wait(100) |
||||
|
// haven't added a mock implementation so getAPIKey of usageQuota just returns undefined
|
||||
|
expect(usageQuota.update).toHaveBeenCalledWith("test", "automationRuns", 1) |
||||
|
expect(workerJob).toBeDefined() |
||||
|
env.BUDIBASE_ENVIRONMENT = "JEST" |
||||
|
env.CLOUD = false |
||||
|
}) |
||||
|
|
||||
|
it("try error scenario", async () => { |
||||
|
env.CLOUD = true |
||||
|
env.BUDIBASE_ENVIRONMENT = "PRODUCTION" |
||||
|
// the second call will throw an error
|
||||
|
await triggers.externalTrigger(basicAutomation(), { a: 1 }) |
||||
|
await wait(100) |
||||
|
expect(console.error).toHaveBeenCalled() |
||||
|
env.BUDIBASE_ENVIRONMENT = "JEST" |
||||
|
env.CLOUD = false |
||||
|
}) |
||||
|
|
||||
|
it("should be able to check triggering row filling", async () => { |
||||
|
const automation = basicAutomation() |
||||
|
let table = basicTable() |
||||
|
table.schema.boolean = { |
||||
|
type: "boolean", |
||||
|
constraints: { |
||||
|
type: "boolean", |
||||
|
}, |
||||
|
} |
||||
|
table.schema.number = { |
||||
|
type: "number", |
||||
|
constraints: { |
||||
|
type: "number", |
||||
|
}, |
||||
|
} |
||||
|
table.schema.datetime = { |
||||
|
type: "datetime", |
||||
|
constraints: { |
||||
|
type: "datetime", |
||||
|
}, |
||||
|
} |
||||
|
table = await config.createTable(table) |
||||
|
automation.definition.trigger.inputs.tableId = table._id |
||||
|
const params = await triggers.fillRowOutput(automation, { appId: config.getAppId() }) |
||||
|
expect(params.row).toBeDefined() |
||||
|
const date = new Date(params.row.datetime) |
||||
|
expect(typeof params.row.name).toBe("string") |
||||
|
expect(typeof params.row.boolean).toBe("boolean") |
||||
|
expect(typeof params.row.number).toBe("number") |
||||
|
expect(date.getFullYear()).toBe(1970) |
||||
|
}) |
||||
|
|
||||
|
it("should check coercion", async () => { |
||||
|
const table = await config.createTable() |
||||
|
const automation = basicAutomation() |
||||
|
automation.definition.trigger.inputs.tableId = table._id |
||||
|
automation.definition.trigger.stepId = "APP" |
||||
|
automation.definition.trigger.inputs.fields = { a: "number" } |
||||
|
await triggers.externalTrigger(automation, { |
||||
|
appId: config.getAppId(), |
||||
|
fields: { |
||||
|
a: "1" |
||||
|
} |
||||
|
}) |
||||
|
await wait(100) |
||||
|
expect(thread).toHaveBeenCalledWith(makePartial({ |
||||
|
data: { |
||||
|
event: { |
||||
|
fields: { |
||||
|
a: 1 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
})) |
||||
|
}) |
||||
|
|
||||
|
it("should be able to clean inputs with the utilities", () => { |
||||
|
// can't clean without a schema
|
||||
|
let output = cleanInputValues({a: "1"}) |
||||
|
expect(output.a).toBe("1") |
||||
|
output = cleanInputValues({a: "1", b: "true", c: "false", d: 1, e: "help"}, { |
||||
|
properties: { |
||||
|
a: { |
||||
|
type: "number", |
||||
|
}, |
||||
|
b: { |
||||
|
type: "boolean", |
||||
|
}, |
||||
|
c: { |
||||
|
type: "boolean", |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
expect(output.a).toBe(1) |
||||
|
expect(output.b).toBe(true) |
||||
|
expect(output.c).toBe(false) |
||||
|
expect(output.d).toBe(1) |
||||
|
expect(output.e).toBe("help") |
||||
|
}) |
||||
|
}) |
||||
@ -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) |
||||
|
}) |
||||
|
}) |
||||
@ -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 |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,12 @@ |
|||||
|
const setup = require("./utilities") |
||||
|
|
||||
|
describe("test the delay logic", () => { |
||||
|
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) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,58 @@ |
|||||
|
const usageQuota = require("../../utilities/usageQuota") |
||||
|
const env = require("../../environment") |
||||
|
const setup = require("./utilities") |
||||
|
|
||||
|
jest.mock("../../utilities/usageQuota") |
||||
|
|
||||
|
describe("test the delete row action", () => { |
||||
|
let table, row, inputs |
||||
|
let config = setup.getConfig() |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
await config.init() |
||||
|
table = await config.createTable() |
||||
|
row = await config.createRow() |
||||
|
inputs = { |
||||
|
tableId: table._id, |
||||
|
id: row._id, |
||||
|
revision: row._rev, |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
afterAll(setup.afterAll) |
||||
|
|
||||
|
it("should be able to run the action", async () => { |
||||
|
const res = await setup.runStep(setup.actions.DELETE_ROW.stepId, inputs) |
||||
|
expect(res.success).toEqual(true) |
||||
|
expect(res.response).toBeDefined() |
||||
|
expect(res.row._id).toEqual(row._id) |
||||
|
let error |
||||
|
try { |
||||
|
await config.getRow(table._id, res.id) |
||||
|
} catch (err) { |
||||
|
error = err |
||||
|
} |
||||
|
expect(error).toBeDefined() |
||||
|
}) |
||||
|
|
||||
|
it("check usage quota attempts", async () => { |
||||
|
env.CLOUD = true |
||||
|
await setup.runStep(setup.actions.DELETE_ROW.stepId, inputs) |
||||
|
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.DELETE_ROW.stepId, {}) |
||||
|
expect(res.success).toEqual(false) |
||||
|
}) |
||||
|
|
||||
|
it("should return an error when table doesn't exist", async () => { |
||||
|
const res = await setup.runStep(setup.actions.DELETE_ROW.stepId, { |
||||
|
tableId: "invalid", |
||||
|
id: "invalid", |
||||
|
revision: "invalid", |
||||
|
}) |
||||
|
expect(res.success).toEqual(false) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,48 @@ |
|||||
|
const setup = require("./utilities") |
||||
|
const { LogicConditions } = require("../steps/filter") |
||||
|
|
||||
|
describe("test the filter logic", () => { |
||||
|
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) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,39 @@ |
|||||
|
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 = { |
||||
|
requestMethod: "POST", |
||||
|
url: "www.test.com", |
||||
|
requestBody: JSON.stringify({ |
||||
|
a: 1, |
||||
|
}), |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
afterAll(setup.afterAll) |
||||
|
|
||||
|
it("should be able to run the action", async () => { |
||||
|
const res = await setup.runStep(setup.actions.OUTGOING_WEBHOOK.stepId, inputs) |
||||
|
expect(res.success).toEqual(true) |
||||
|
expect(res.response.url).toEqual("http://www.test.com") |
||||
|
expect(res.response.method).toEqual("POST") |
||||
|
expect(res.response.body.a).toEqual(1) |
||||
|
}) |
||||
|
|
||||
|
it("should return an error if something goes wrong in fetch", async () => { |
||||
|
const res = await setup.runStep(setup.actions.OUTGOING_WEBHOOK.stepId, { |
||||
|
requestMethod: "GET", |
||||
|
url: "www.invalid.com" |
||||
|
}) |
||||
|
expect(res.success).toEqual(false) |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
@ -0,0 +1,36 @@ |
|||||
|
const setup = require("./utilities") |
||||
|
|
||||
|
jest.mock("@sendgrid/mail") |
||||
|
|
||||
|
describe("test the send email action", () => { |
||||
|
let inputs |
||||
|
let config = setup.getConfig() |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
await config.init() |
||||
|
inputs = { |
||||
|
to: "me@test.com", |
||||
|
from: "budibase@test.com", |
||||
|
subject: "Testing", |
||||
|
text: "Email contents", |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
afterAll(setup.afterAll) |
||||
|
|
||||
|
it("should be able to run the action", async () => { |
||||
|
const res = await setup.runStep(setup.actions.SEND_EMAIL.stepId, inputs) |
||||
|
expect(res.success).toEqual(true) |
||||
|
// the mocked module throws back the input
|
||||
|
expect(res.response.to).toEqual("me@test.com") |
||||
|
}) |
||||
|
|
||||
|
it("should return an error if input an invalid email address", async () => { |
||||
|
const res = await setup.runStep(setup.actions.SEND_EMAIL.stepId, { |
||||
|
...inputs, |
||||
|
to: "invalid@test.com", |
||||
|
}) |
||||
|
expect(res.success).toEqual(false) |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
@ -0,0 +1,45 @@ |
|||||
|
const env = require("../../environment") |
||||
|
const setup = require("./utilities") |
||||
|
|
||||
|
describe("test the update row action", () => { |
||||
|
let table, row, inputs |
||||
|
let config = setup.getConfig() |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
await config.init() |
||||
|
table = await config.createTable() |
||||
|
row = await config.createRow() |
||||
|
inputs = { |
||||
|
rowId: row._id, |
||||
|
row: { |
||||
|
...row, |
||||
|
name: "Updated name", |
||||
|
// put a falsy option in to be removed
|
||||
|
description: "", |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
afterAll(setup.afterAll) |
||||
|
|
||||
|
it("should be able to run the action", async () => { |
||||
|
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, inputs) |
||||
|
expect(res.success).toEqual(true) |
||||
|
const updatedRow = await config.getRow(table._id, res.id) |
||||
|
expect(updatedRow.name).toEqual("Updated name") |
||||
|
expect(updatedRow.description).not.toEqual("") |
||||
|
}) |
||||
|
|
||||
|
it("should check invalid inputs return an error", async () => { |
||||
|
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {}) |
||||
|
expect(res.success).toEqual(false) |
||||
|
}) |
||||
|
|
||||
|
it("should return an error when table doesn't exist", async () => { |
||||
|
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, { |
||||
|
row: { _id: "invalid" }, |
||||
|
rowId: "invalid", |
||||
|
}) |
||||
|
expect(res.success).toEqual(false) |
||||
|
}) |
||||
|
}) |
||||
@ -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 |
||||
@ -0,0 +1,218 @@ |
|||||
|
const TestConfig = require("../../tests/utilities/TestConfiguration") |
||||
|
const { basicRow, basicLinkedRow, basicTable } = require("../../tests/utilities/structures") |
||||
|
const LinkController = require("../linkedRows/LinkController") |
||||
|
const { RelationshipTypes } = require("../../constants") |
||||
|
const { cloneDeep } = require("lodash/fp") |
||||
|
|
||||
|
describe("test the link controller", () => { |
||||
|
let config = new TestConfig(false) |
||||
|
let table1, table2 |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
await config.init() |
||||
|
const { _id } = await config.createTable() |
||||
|
table2 = await config.createLinkedTable(RelationshipTypes.MANY_TO_MANY, ["link", "link2"]) |
||||
|
// update table after creating link
|
||||
|
table1 = await config.getTable(_id) |
||||
|
}) |
||||
|
|
||||
|
afterAll(config.end) |
||||
|
|
||||
|
function createLinkController(table, row = null, oldTable = null) { |
||||
|
const linkConfig = { |
||||
|
appId: config.getAppId(), |
||||
|
tableId: table._id, |
||||
|
table, |
||||
|
} |
||||
|
if (row) { |
||||
|
linkConfig.row = row |
||||
|
} |
||||
|
if (oldTable) { |
||||
|
linkConfig.oldTable = oldTable |
||||
|
} |
||||
|
return new LinkController(linkConfig) |
||||
|
} |
||||
|
|
||||
|
async function createLinkedRow(linkField = "link", t1 = table1, t2 = table2) { |
||||
|
const row = await config.createRow(basicRow(t2._id)) |
||||
|
const { _id } = await config.createRow(basicLinkedRow(t1._id, row._id, linkField)) |
||||
|
return config.getRow(t1._id, _id) |
||||
|
} |
||||
|
|
||||
|
it("should be able to confirm if two table schemas are equal", () => { |
||||
|
const controller = createLinkController(table1) |
||||
|
let equal = controller.areLinkSchemasEqual(table2.schema.link, table2.schema.link) |
||||
|
expect(equal).toEqual(true) |
||||
|
equal = controller.areLinkSchemasEqual(table1.schema.link, table2.schema.link) |
||||
|
expect(equal).toEqual(false) |
||||
|
}) |
||||
|
|
||||
|
it("should be able to check the relationship types across two fields", () => { |
||||
|
const controller = createLinkController(table1) |
||||
|
// empty case
|
||||
|
let output = controller.handleRelationshipType({}, {}) |
||||
|
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY) |
||||
|
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY) |
||||
|
output = controller.handleRelationshipType({ relationshipType: RelationshipTypes.MANY_TO_MANY }, {}) |
||||
|
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY) |
||||
|
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY) |
||||
|
output = controller.handleRelationshipType({ relationshipType: RelationshipTypes.MANY_TO_ONE }, {}) |
||||
|
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.ONE_TO_MANY) |
||||
|
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.MANY_TO_ONE) |
||||
|
output = controller.handleRelationshipType({ relationshipType: RelationshipTypes.ONE_TO_MANY }, {}) |
||||
|
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.MANY_TO_ONE) |
||||
|
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.ONE_TO_MANY) |
||||
|
}) |
||||
|
|
||||
|
it("should be able to delete a row", async () => { |
||||
|
const row = await createLinkedRow() |
||||
|
const controller = createLinkController(table1, row) |
||||
|
// get initial count
|
||||
|
const beforeLinks = await controller.getRowLinkDocs(row._id) |
||||
|
await controller.rowDeleted() |
||||
|
let afterLinks = await controller.getRowLinkDocs(row._id) |
||||
|
expect(beforeLinks.length).toEqual(1) |
||||
|
expect(afterLinks.length).toEqual(0) |
||||
|
}) |
||||
|
|
||||
|
it("shouldn't throw an error when deleting a row with no links", async () => { |
||||
|
const row = await config.createRow(basicRow(table1._id)) |
||||
|
const controller = createLinkController(table1, row) |
||||
|
let error |
||||
|
try { |
||||
|
await controller.rowDeleted() |
||||
|
} catch (err) { |
||||
|
error = err |
||||
|
} |
||||
|
expect(error).toBeUndefined() |
||||
|
}) |
||||
|
|
||||
|
it("should throw an error when validating a table which is invalid", () => { |
||||
|
const controller = createLinkController(table1) |
||||
|
const copyTable = { |
||||
|
...table1 |
||||
|
} |
||||
|
copyTable.schema.otherTableLink = { |
||||
|
type: "link", |
||||
|
fieldName: "link", |
||||
|
tableId: table2._id, |
||||
|
} |
||||
|
let error |
||||
|
try { |
||||
|
controller.validateTable(copyTable) |
||||
|
} catch (err) { |
||||
|
error = err |
||||
|
} |
||||
|
expect(error).toBeDefined() |
||||
|
expect(error.message).toEqual("Cannot re-use the linked column name for a linked table.") |
||||
|
}) |
||||
|
|
||||
|
it("should be able to remove a link when saving/updating the row", async () => { |
||||
|
const row = await createLinkedRow() |
||||
|
// remove the link from the row
|
||||
|
row.link = [] |
||||
|
const controller = createLinkController(table1, row) |
||||
|
await controller.rowSaved() |
||||
|
let links = await controller.getRowLinkDocs(row._id) |
||||
|
expect(links.length).toEqual(0) |
||||
|
}) |
||||
|
|
||||
|
it("should be able to delete a table and have links deleted", async () => { |
||||
|
await createLinkedRow() |
||||
|
const controller = createLinkController(table1) |
||||
|
let before = await controller.getTableLinkDocs() |
||||
|
await controller.tableDeleted() |
||||
|
let after = await controller.getTableLinkDocs() |
||||
|
expect(before.length).toEqual(1) |
||||
|
expect(after.length).toEqual(0) |
||||
|
}) |
||||
|
|
||||
|
it("should be able to remove a linked field from a table", async () => { |
||||
|
await createLinkedRow() |
||||
|
await createLinkedRow("link2") |
||||
|
const controller = createLinkController(table1, null, table1) |
||||
|
let before = await controller.getTableLinkDocs() |
||||
|
await controller.removeFieldFromTable("link") |
||||
|
let after = await controller.getTableLinkDocs() |
||||
|
expect(before.length).toEqual(2) |
||||
|
// shouldn't delete the other field
|
||||
|
expect(after.length).toEqual(1) |
||||
|
}) |
||||
|
|
||||
|
it("should throw an error when overwriting a link column", async () => { |
||||
|
const update = cloneDeep(table1) |
||||
|
update.schema.link.relationshipType = RelationshipTypes.MANY_TO_ONE |
||||
|
let error |
||||
|
try { |
||||
|
const controller = createLinkController(update) |
||||
|
await controller.tableSaved() |
||||
|
} catch (err) { |
||||
|
error = err |
||||
|
} |
||||
|
expect(error).toBeDefined() |
||||
|
}) |
||||
|
|
||||
|
it("should be able to remove a field view table update", async () => { |
||||
|
await createLinkedRow() |
||||
|
await createLinkedRow() |
||||
|
const newTable = cloneDeep(table1) |
||||
|
delete newTable.schema.link |
||||
|
const controller = createLinkController(newTable, null, table1) |
||||
|
await controller.tableUpdated() |
||||
|
const links = await controller.getTableLinkDocs() |
||||
|
expect(links.length).toEqual(0) |
||||
|
}) |
||||
|
|
||||
|
it("shouldn't allow one to many having many relationships against it", async () => { |
||||
|
const firstTable = await config.createTable() |
||||
|
const { _id } = await config.createLinkedTable(RelationshipTypes.MANY_TO_ONE, ["link"]) |
||||
|
const linkTable = await config.getTable(_id) |
||||
|
// an initial row to link around
|
||||
|
const row = await createLinkedRow("link", linkTable, firstTable) |
||||
|
let error |
||||
|
try { |
||||
|
// create another row to initiate the error
|
||||
|
await config.createRow(basicLinkedRow(row.tableId, row.link[0])) |
||||
|
} catch (err) { |
||||
|
error = err |
||||
|
} |
||||
|
expect(error).toBeDefined() |
||||
|
}) |
||||
|
|
||||
|
it("should not error if a link being created doesn't exist", async () => { |
||||
|
let error |
||||
|
try { |
||||
|
await config.createRow(basicLinkedRow(table1._id, "invalid")) |
||||
|
} catch (err) { |
||||
|
error = err |
||||
|
} |
||||
|
expect(error).toBeUndefined() |
||||
|
}) |
||||
|
|
||||
|
it("make sure auto column goes onto other row too", async () => { |
||||
|
const table = await config.createTable() |
||||
|
const tableCfg = basicTable() |
||||
|
tableCfg.schema.link = { |
||||
|
type: "link", |
||||
|
fieldName: "link", |
||||
|
tableId: table._id, |
||||
|
name: "link", |
||||
|
autocolumn: true, |
||||
|
} |
||||
|
await config.createTable(tableCfg) |
||||
|
const afterTable = await config.getTable(table._id) |
||||
|
expect(afterTable.schema.link.autocolumn).toBe(true) |
||||
|
}) |
||||
|
|
||||
|
it("should be able to link to self", async () => { |
||||
|
const table = await config.createTable() |
||||
|
table.schema.link = { |
||||
|
type: "link", |
||||
|
fieldName: "link", |
||||
|
tableId: table._id, |
||||
|
name: "link", |
||||
|
autocolumn: true, |
||||
|
} |
||||
|
await config.updateTable(table) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,74 @@ |
|||||
|
const TestConfig = require("../../tests/utilities/TestConfiguration") |
||||
|
const { basicTable, basicLinkedRow } = require("../../tests/utilities/structures") |
||||
|
const linkUtils = require("../linkedRows/linkUtils") |
||||
|
const links = require("../linkedRows") |
||||
|
const CouchDB = require("../index") |
||||
|
|
||||
|
describe("test link functionality", () => { |
||||
|
const config = new TestConfig(false) |
||||
|
|
||||
|
describe("getLinkedTable", () => { |
||||
|
let db, table |
||||
|
beforeEach(async () => { |
||||
|
await config.init() |
||||
|
db = new CouchDB(config.getAppId()) |
||||
|
table = await config.createTable() |
||||
|
}) |
||||
|
|
||||
|
it("should be able to retrieve a linked table from a list", async () => { |
||||
|
const retrieved = await linkUtils.getLinkedTable(db, table._id, [table]) |
||||
|
expect(retrieved._id).toBe(table._id) |
||||
|
}) |
||||
|
|
||||
|
it("should be able to retrieve a table from DB and update list", async () => { |
||||
|
const tables = [] |
||||
|
const retrieved = await linkUtils.getLinkedTable(db, table._id, tables) |
||||
|
expect(retrieved._id).toBe(table._id) |
||||
|
expect(tables[0]).toBeDefined() |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
describe("getRelatedTableForField", () => { |
||||
|
let link = basicTable() |
||||
|
link.schema.link = { |
||||
|
fieldName: "otherLink", |
||||
|
tableId: "tableID", |
||||
|
type: "link", |
||||
|
} |
||||
|
|
||||
|
it("should get the field from the table directly", () => { |
||||
|
expect(linkUtils.getRelatedTableForField(link, "link")).toBe("tableID") |
||||
|
}) |
||||
|
|
||||
|
it("should get the field from the link", () => { |
||||
|
expect(linkUtils.getRelatedTableForField(link, "otherLink")).toBe("tableID") |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
describe("getLinkDocuments", () => { |
||||
|
it("should create the link view when it doesn't exist", async () => { |
||||
|
// create the DB and a very basic app design DB
|
||||
|
const db = new CouchDB("test") |
||||
|
await db.put({ _id: "_design/database", views: {} }) |
||||
|
const output = await linkUtils.getLinkDocuments({ |
||||
|
appId: "test", |
||||
|
tableId: "test", |
||||
|
rowId: "test", |
||||
|
includeDocs: false, |
||||
|
}) |
||||
|
expect(Array.isArray(output)).toBe(true) |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
describe("attachLinkIDs", () => { |
||||
|
it("should be able to attach linkIDs", async () => { |
||||
|
await config.init() |
||||
|
await config.createTable() |
||||
|
const table = await config.createLinkedTable() |
||||
|
const row = await config.createRow() |
||||
|
const linkRow = await config.createRow(basicLinkedRow(table._id, row._id)) |
||||
|
const attached = await links.attachLinkIDs(config.getAppId(), [linkRow]) |
||||
|
expect(attached[0].link[0]).toBe(row._id) |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,70 @@ |
|||||
|
const Airtable = require("airtable") |
||||
|
const AirtableIntegration = require("../airtable") |
||||
|
jest.mock("airtable") |
||||
|
|
||||
|
class TestConfiguration { |
||||
|
constructor(config = {}) { |
||||
|
this.integration = new AirtableIntegration.integration(config) |
||||
|
this.client = { |
||||
|
create: jest.fn(), |
||||
|
select: jest.fn(), |
||||
|
update: jest.fn(), |
||||
|
destroy: jest.fn(), |
||||
|
} |
||||
|
this.integration.client = () => this.client |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
describe("Airtable Integration", () => { |
||||
|
let config |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
config = new TestConfiguration() |
||||
|
}) |
||||
|
|
||||
|
it("calls the create method with the correct params", async () => { |
||||
|
const response = await config.integration.create({ |
||||
|
table: "test", |
||||
|
json: {} |
||||
|
}) |
||||
|
expect(config.client.create).toHaveBeenCalledWith([ |
||||
|
{ |
||||
|
fields: {} |
||||
|
} |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("calls the read method with the correct params", async () => { |
||||
|
const response = await config.integration.read({ |
||||
|
table: "test", |
||||
|
view: "Grid view" |
||||
|
}) |
||||
|
expect(config.client.select).toHaveBeenCalledWith({ |
||||
|
maxRecords: 10, view: "Grid view" |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
it("calls the update method with the correct params", async () => { |
||||
|
const response = await config.integration.update({ |
||||
|
table: "test", |
||||
|
id: "123", |
||||
|
json: { |
||||
|
name: "test" |
||||
|
} |
||||
|
}) |
||||
|
expect(config.client.update).toHaveBeenCalledWith([ |
||||
|
{ |
||||
|
id: "123", |
||||
|
fields: { name: "test" } |
||||
|
} |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("calls the delete method with the correct params", async () => { |
||||
|
const ids = [1,2,3,4] |
||||
|
const response = await config.integration.delete({ |
||||
|
ids |
||||
|
}) |
||||
|
expect(config.client.destroy).toHaveBeenCalledWith(ids) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,35 @@ |
|||||
|
const arangodb = require("arangojs") |
||||
|
const ArangoDBIntegration = require("../arangodb") |
||||
|
jest.mock("arangojs") |
||||
|
|
||||
|
class TestConfiguration { |
||||
|
constructor(config = {}) { |
||||
|
this.integration = new ArangoDBIntegration.integration(config) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
describe("ArangoDB Integration", () => { |
||||
|
let config |
||||
|
let indexName = "Users" |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
config = new TestConfiguration() |
||||
|
}) |
||||
|
|
||||
|
it("calls the create method with the correct params", async () => { |
||||
|
const body = { |
||||
|
json: "Hello" |
||||
|
} |
||||
|
|
||||
|
const response = await config.integration.create(body) |
||||
|
expect(config.integration.client.query).toHaveBeenCalledWith(`INSERT Hello INTO collection RETURN NEW`) |
||||
|
}) |
||||
|
|
||||
|
it("calls the read method with the correct params", async () => { |
||||
|
const query = { |
||||
|
json: `test`, |
||||
|
} |
||||
|
const response = await config.integration.read(query) |
||||
|
expect(config.integration.client.query).toHaveBeenCalledWith(query.sql) |
||||
|
}) |
||||
|
}) |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue