diff --git a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte
index c47840ea83..1e951b00cb 100644
--- a/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte
+++ b/packages/builder/src/components/common/bindings/EvaluationSidePanel.svelte
@@ -19,7 +19,7 @@
$: success = !error && !empty
$: highlightedResult = highlight(expressionResult)
$: highlightedLogs = expressionLogs.map(l => ({
- log: highlight(l.log.join(", ")),
+ log: l.log.map(part => highlight(part)).join(", "),
line: l.line,
type: l.type,
}))
@@ -95,7 +95,9 @@
{#if empty}
Your expression will be evaluated here
{:else if error}
- {formatError(expressionError)}
+
+ {formatError(expressionError)}
+
{:else}
{#each highlightedLogs as logLine}
@@ -118,13 +120,17 @@
{@html logLine.log}
{#if logLine.line}
- :{logLine.line}
+ :{logLine.line}
{/if}
{/each}
-
- {@html highlightedResult}
+
+
+ {@html highlightedResult}
+
{/if}
@@ -169,29 +175,33 @@
.header.error::before {
background: var(--error-bg);
}
+ .error-msg {
+ padding-top: var(--spacing-m);
+ }
.body {
flex: 1 1 auto;
- padding: var(--spacing-m) var(--spacing-l);
font-family: var(--font-mono);
+ margin: 0 var(--spacing-m);
font-size: 12px;
overflow-y: auto;
overflow-x: hidden;
- white-space: pre-line;
- word-wrap: break-word;
+ word-wrap: anywhere;
height: 0;
}
.output-lines {
display: flex;
flex-direction: column;
- gap: var(--spacing-xs);
}
.line {
- border-bottom: var(--border-light);
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: end;
- padding: var(--spacing-s);
+ padding: var(--spacing-m) 0;
+ word-wrap: anywhere;
+ }
+ .line:not(:first-of-type) {
+ border-top: var(--border-light);
}
.icon-log {
display: flex;
diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts
index 5c0b86d9a0..94517db67a 100644
--- a/packages/server/src/api/routes/tests/automation.spec.ts
+++ b/packages/server/src/api/routes/tests/automation.spec.ts
@@ -1,7 +1,6 @@
import {
checkBuilderEndpoint,
getAllTableRows,
- clearAllAutomations,
testAutomation,
} from "./utilities/TestFunctions"
import * as setup from "./utilities"
@@ -12,9 +11,9 @@ import {
import { configs, context, events } from "@budibase/backend-core"
import sdk from "../../../sdk"
import {
- Automation,
ConfigType,
FieldType,
+ isDidNotTriggerResponse,
SettingsConfig,
Table,
} from "@budibase/types"
@@ -22,11 +21,13 @@ import { mocks } from "@budibase/backend-core/tests"
import { removeDeprecated } from "../../../automations/utils"
import { createAutomationBuilder } from "../../../automations/tests/utilities/AutomationTestBuilder"
import { automations } from "@budibase/shared-core"
+import { basicTable } from "../../../tests/utilities/structures"
+import TestConfiguration from "../../../tests/utilities/TestConfiguration"
const FilterConditions = automations.steps.filter.FilterConditions
const MAX_RETRIES = 4
-let {
+const {
basicAutomation,
newAutomation,
automationTrigger,
@@ -37,10 +38,11 @@ let {
} = setup.structures
describe("/automations", () => {
- let request = setup.getRequest()
- let config = setup.getConfig()
+ const config = new TestConfiguration()
- afterAll(setup.afterAll)
+ afterAll(() => {
+ config.end()
+ })
beforeAll(async () => {
await config.init()
@@ -52,40 +54,26 @@ describe("/automations", () => {
describe("get definitions", () => {
it("returns a list of definitions for actions", async () => {
- const res = await request
- .get(`/api/automations/action/list`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(200)
-
- expect(Object.keys(res.body).length).not.toEqual(0)
+ const res = await config.api.automation.getActions()
+ expect(Object.keys(res).length).not.toEqual(0)
})
it("returns a list of definitions for triggerInfo", async () => {
- const res = await request
- .get(`/api/automations/trigger/list`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(200)
-
- expect(Object.keys(res.body).length).not.toEqual(0)
+ const res = await config.api.automation.getTriggers()
+ expect(Object.keys(res).length).not.toEqual(0)
})
it("returns all of the definitions in one", async () => {
- const res = await request
- .get(`/api/automations/definitions/list`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(200)
+ const { action, trigger } = await config.api.automation.getDefinitions()
let definitionsLength = Object.keys(
removeDeprecated(BUILTIN_ACTION_DEFINITIONS)
).length
- expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual(
+ expect(Object.keys(action).length).toBeGreaterThanOrEqual(
definitionsLength
)
- expect(Object.keys(res.body.trigger).length).toEqual(
+ expect(Object.keys(trigger).length).toEqual(
Object.keys(removeDeprecated(TRIGGER_DEFINITIONS)).length
)
})
@@ -93,38 +81,27 @@ describe("/automations", () => {
describe("create", () => {
it("creates an automation with no steps", async () => {
- const automation = newAutomation()
- automation.definition.steps = []
+ const { message, automation } = await config.api.automation.post(
+ newAutomation({ steps: [] })
+ )
- const res = await request
- .post(`/api/automations`)
- .set(config.defaultHeaders())
- .send(automation)
- .expect("Content-Type", /json/)
- .expect(200)
-
- expect(res.body.message).toEqual("Automation created successfully")
- expect(res.body.automation.name).toEqual("My Automation")
- expect(res.body.automation._id).not.toEqual(null)
+ expect(message).toEqual("Automation created successfully")
+ expect(automation.name).toEqual("My Automation")
+ expect(automation._id).not.toEqual(null)
expect(events.automation.created).toHaveBeenCalledTimes(1)
expect(events.automation.stepCreated).not.toHaveBeenCalled()
})
it("creates an automation with steps", async () => {
- const automation = newAutomation()
- automation.definition.steps.push(automationStep())
jest.clearAllMocks()
- const res = await request
- .post(`/api/automations`)
- .set(config.defaultHeaders())
- .send(automation)
- .expect("Content-Type", /json/)
- .expect(200)
+ const { message, automation } = await config.api.automation.post(
+ newAutomation({ steps: [automationStep(), automationStep()] })
+ )
- expect(res.body.message).toEqual("Automation created successfully")
- expect(res.body.automation.name).toEqual("My Automation")
- expect(res.body.automation._id).not.toEqual(null)
+ expect(message).toEqual("Automation created successfully")
+ expect(automation.name).toEqual("My Automation")
+ expect(automation._id).not.toEqual(null)
expect(events.automation.created).toHaveBeenCalledTimes(1)
expect(events.automation.stepCreated).toHaveBeenCalledTimes(2)
})
@@ -241,13 +218,9 @@ describe("/automations", () => {
describe("find", () => {
it("should be able to find the automation", async () => {
const automation = await config.createAutomation()
- const res = await request
- .get(`/api/automations/${automation._id}`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(200)
- expect(res.body._id).toEqual(automation._id)
- expect(res.body._rev).toEqual(automation._rev)
+ const { _id, _rev } = await config.api.automation.get(automation._id!)
+ expect(_id).toEqual(automation._id)
+ expect(_rev).toEqual(automation._rev)
})
})
@@ -348,106 +321,104 @@ describe("/automations", () => {
describe("trigger", () => {
it("does not trigger an automation when not synchronous and in dev", async () => {
- let automation = newAutomation()
- automation = await config.createAutomation(automation)
- const res = await request
- .post(`/api/automations/${automation._id}/trigger`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(400)
-
- expect(res.body.message).toEqual(
- "Only apps in production support this endpoint"
+ const { automation } = await config.api.automation.post(newAutomation())
+ await config.api.automation.trigger(
+ automation._id!,
+ {
+ fields: {},
+ timeout: 1000,
+ },
+ {
+ status: 400,
+ body: {
+ message: "Only apps in production support this endpoint",
+ },
+ }
)
})
it("triggers a synchronous automation", async () => {
mocks.licenses.useSyncAutomations()
- let automation = collectAutomation()
- automation = await config.createAutomation(automation)
- const res = await request
- .post(`/api/automations/${automation._id}/trigger`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(200)
-
- expect(res.body.success).toEqual(true)
- expect(res.body.value).toEqual([1, 2, 3])
+ const { automation } = await config.api.automation.post(
+ collectAutomation()
+ )
+ await config.api.automation.trigger(
+ automation._id!,
+ {
+ fields: {},
+ timeout: 1000,
+ },
+ {
+ status: 200,
+ body: {
+ success: true,
+ value: [1, 2, 3],
+ },
+ }
+ )
})
it("should throw an error when attempting to trigger a disabled automation", async () => {
mocks.licenses.useSyncAutomations()
- let automation = collectAutomation()
- automation = await config.createAutomation({
- ...automation,
- disabled: true,
- })
+ const { automation } = await config.api.automation.post(
+ collectAutomation({ disabled: true })
+ )
- const res = await request
- .post(`/api/automations/${automation._id}/trigger`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(400)
-
- expect(res.body.message).toEqual("Automation is disabled")
+ await config.api.automation.trigger(
+ automation._id!,
+ {
+ fields: {},
+ timeout: 1000,
+ },
+ {
+ status: 400,
+ body: {
+ message: "Automation is disabled",
+ },
+ }
+ )
})
it("triggers an asynchronous automation", async () => {
- let automation = newAutomation()
- automation = await config.createAutomation(automation)
+ const { automation } = await config.api.automation.post(newAutomation())
await config.publish()
- const res = await request
- .post(`/api/automations/${automation._id}/trigger`)
- .set(config.defaultHeaders({}, true))
- .expect("Content-Type", /json/)
- .expect(200)
-
- expect(res.body.message).toEqual(
- `Automation ${automation._id} has been triggered.`
+ await config.withProdApp(() =>
+ config.api.automation.trigger(
+ automation._id!,
+ {
+ fields: {},
+ timeout: 1000,
+ },
+ {
+ status: 200,
+ body: {
+ message: `Automation ${automation._id} has been triggered.`,
+ },
+ }
+ )
)
})
})
describe("update", () => {
- const update = async (automation: Automation) => {
- return request
- .put(`/api/automations`)
- .set(config.defaultHeaders())
- .send(automation)
- .expect("Content-Type", /json/)
- .expect(200)
- }
-
- const updateWithPost = async (automation: Automation) => {
- return request
- .post(`/api/automations`)
- .set(config.defaultHeaders())
- .send(automation)
- .expect("Content-Type", /json/)
- .expect(200)
- }
-
it("updates a automations name", async () => {
- const automation = await config.createAutomation(newAutomation())
+ const { automation } = await config.api.automation.post(basicAutomation())
automation.name = "Updated Name"
jest.clearAllMocks()
- const res = await update(automation)
+ const { automation: updatedAutomation, message } =
+ await config.api.automation.update(automation)
- const automationRes = res.body.automation
- const message = res.body.message
+ expect(updatedAutomation._id).toEqual(automation._id)
+ expect(updatedAutomation._rev).toBeDefined()
+ expect(updatedAutomation._rev).not.toEqual(automation._rev)
- // doc attributes
- expect(automationRes._id).toEqual(automation._id)
- expect(automationRes._rev).toBeDefined()
- expect(automationRes._rev).not.toEqual(automation._rev)
- // content updates
- expect(automationRes.name).toEqual("Updated Name")
+ expect(updatedAutomation.name).toEqual("Updated Name")
expect(message).toEqual(
`Automation ${automation._id} updated successfully.`
)
- // events
+
expect(events.automation.created).not.toHaveBeenCalled()
expect(events.automation.stepCreated).not.toHaveBeenCalled()
expect(events.automation.stepDeleted).not.toHaveBeenCalled()
@@ -455,26 +426,23 @@ describe("/automations", () => {
})
it("updates a automations name using POST request", async () => {
- const automation = await config.createAutomation(newAutomation())
+ const { automation } = await config.api.automation.post(basicAutomation())
automation.name = "Updated Name"
jest.clearAllMocks()
- // the POST request will defer to the update
- // when an id has been supplied.
- const res = await updateWithPost(automation)
+ // the POST request will defer to the update when an id has been supplied.
+ const { automation: updatedAutomation, message } =
+ await config.api.automation.post(automation)
- const automationRes = res.body.automation
- const message = res.body.message
- // doc attributes
- expect(automationRes._id).toEqual(automation._id)
- expect(automationRes._rev).toBeDefined()
- expect(automationRes._rev).not.toEqual(automation._rev)
- // content updates
- expect(automationRes.name).toEqual("Updated Name")
+ expect(updatedAutomation._id).toEqual(automation._id)
+ expect(updatedAutomation._rev).toBeDefined()
+ expect(updatedAutomation._rev).not.toEqual(automation._rev)
+
+ expect(updatedAutomation.name).toEqual("Updated Name")
expect(message).toEqual(
`Automation ${automation._id} updated successfully.`
)
- // events
+
expect(events.automation.created).not.toHaveBeenCalled()
expect(events.automation.stepCreated).not.toHaveBeenCalled()
expect(events.automation.stepDeleted).not.toHaveBeenCalled()
@@ -482,16 +450,14 @@ describe("/automations", () => {
})
it("updates an automation trigger", async () => {
- let automation = newAutomation()
- automation = await config.createAutomation(automation)
+ const { automation } = await config.api.automation.post(newAutomation())
automation.definition.trigger = automationTrigger(
TRIGGER_DEFINITIONS.WEBHOOK
)
jest.clearAllMocks()
- await update(automation)
+ await config.api.automation.update(automation)
- // events
expect(events.automation.created).not.toHaveBeenCalled()
expect(events.automation.stepCreated).not.toHaveBeenCalled()
expect(events.automation.stepDeleted).not.toHaveBeenCalled()
@@ -499,16 +465,13 @@ describe("/automations", () => {
})
it("adds automation steps", async () => {
- let automation = newAutomation()
- automation = await config.createAutomation(automation)
+ const { automation } = await config.api.automation.post(newAutomation())
automation.definition.steps.push(automationStep())
automation.definition.steps.push(automationStep())
jest.clearAllMocks()
- // check the post request honours updates with same id
- await update(automation)
+ await config.api.automation.update(automation)
- // events
expect(events.automation.stepCreated).toHaveBeenCalledTimes(2)
expect(events.automation.created).not.toHaveBeenCalled()
expect(events.automation.stepDeleted).not.toHaveBeenCalled()
@@ -516,32 +479,25 @@ describe("/automations", () => {
})
it("removes automation steps", async () => {
- let automation = newAutomation()
- automation.definition.steps.push(automationStep())
- automation = await config.createAutomation(automation)
+ const { automation } = await config.api.automation.post(newAutomation())
automation.definition.steps = []
jest.clearAllMocks()
- // check the post request honours updates with same id
- await update(automation)
+ await config.api.automation.update(automation)
- // events
- expect(events.automation.stepDeleted).toHaveBeenCalledTimes(2)
+ expect(events.automation.stepDeleted).toHaveBeenCalledTimes(1)
expect(events.automation.stepCreated).not.toHaveBeenCalled()
expect(events.automation.created).not.toHaveBeenCalled()
expect(events.automation.triggerUpdated).not.toHaveBeenCalled()
})
it("adds and removes automation steps", async () => {
- let automation = newAutomation()
- automation = await config.createAutomation(automation)
+ const { automation } = await config.api.automation.post(newAutomation())
automation.definition.steps = [automationStep(), automationStep()]
jest.clearAllMocks()
- // check the post request honours updates with same id
- await update(automation)
+ await config.api.automation.update(automation)
- // events
expect(events.automation.stepCreated).toHaveBeenCalledTimes(2)
expect(events.automation.stepDeleted).toHaveBeenCalledTimes(1)
expect(events.automation.created).not.toHaveBeenCalled()
@@ -551,16 +507,24 @@ describe("/automations", () => {
describe("fetch", () => {
it("return all the automations for an instance", async () => {
- await clearAllAutomations(config)
- const autoConfig = await config.createAutomation(basicAutomation())
- const res = await request
- .get(`/api/automations`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(200)
+ const fetchResponse = await config.api.automation.fetch()
+ for (const auto of fetchResponse.automations) {
+ await config.api.automation.delete(auto)
+ }
- expect(res.body.automations[0]).toEqual(
- expect.objectContaining(autoConfig)
+ const { automation: automation1 } = await config.api.automation.post(
+ newAutomation()
+ )
+ const { automation: automation2 } = await config.api.automation.post(
+ newAutomation()
+ )
+ const { automation: automation3 } = await config.api.automation.post(
+ newAutomation()
+ )
+
+ const { automations } = await config.api.automation.fetch()
+ expect(automations).toEqual(
+ expect.arrayContaining([automation1, automation2, automation3])
)
})
@@ -575,29 +539,25 @@ describe("/automations", () => {
describe("destroy", () => {
it("deletes a automation by its ID", async () => {
- const automation = await config.createAutomation()
- const res = await request
- .delete(`/api/automations/${automation._id}/${automation._rev}`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(200)
+ const { automation } = await config.api.automation.post(newAutomation())
+ const { id } = await config.api.automation.delete(automation)
- expect(res.body.id).toEqual(automation._id)
+ expect(id).toEqual(automation._id)
expect(events.automation.deleted).toHaveBeenCalledTimes(1)
})
it("cannot delete a row action automation", async () => {
- const automation = await config.createAutomation(
+ const { automation } = await config.api.automation.post(
setup.structures.rowActionAutomation()
)
- await request
- .delete(`/api/automations/${automation._id}/${automation._rev}`)
- .set(config.defaultHeaders())
- .expect("Content-Type", /json/)
- .expect(422, {
+
+ await config.api.automation.delete(automation, {
+ status: 422,
+ body: {
message: "Row actions automations cannot be deleted",
status: 422,
- })
+ },
+ })
expect(events.automation.deleted).not.toHaveBeenCalled()
})
@@ -614,10 +574,19 @@ describe("/automations", () => {
describe("checkForCollectStep", () => {
it("should return true if a collect step exists in an automation", async () => {
- let automation = collectAutomation()
- await config.createAutomation(automation)
- let res = await sdk.automations.utils.checkForCollectStep(automation)
- expect(res).toEqual(true)
+ const { automation } = await config.api.automation.post(
+ collectAutomation()
+ )
+ expect(sdk.automations.utils.checkForCollectStep(automation)).toEqual(
+ true
+ )
+ })
+
+ it("should return false if a collect step does not exist in an automation", async () => {
+ const { automation } = await config.api.automation.post(newAutomation())
+ expect(sdk.automations.utils.checkForCollectStep(automation)).toEqual(
+ false
+ )
})
})
@@ -628,28 +597,45 @@ describe("/automations", () => {
])(
"triggers an update row automation and compares new to old rows with old city '%s' and new city '%s'",
async ({ oldCity, newCity }) => {
- const expectedResult = oldCity === newCity
+ let table = await config.api.table.save(basicTable())
- let table = await config.createTable()
+ const { automation } = await config.api.automation.post(
+ filterAutomation({
+ definition: {
+ trigger: {
+ inputs: {
+ tableId: table._id,
+ },
+ },
+ steps: [
+ {
+ inputs: {
+ condition: FilterConditions.EQUAL,
+ field: "{{ trigger.row.City }}",
+ value: "{{ trigger.oldRow.City }}",
+ },
+ },
+ ],
+ },
+ })
+ )
- let automation = await filterAutomation(config.getAppId())
- automation.definition.trigger.inputs.tableId = table._id
- automation.definition.steps[0].inputs = {
- condition: FilterConditions.EQUAL,
- field: "{{ trigger.row.City }}",
- value: "{{ trigger.oldRow.City }}",
- }
- automation = await config.createAutomation(automation)
- let triggerInputs = {
+ const res = await config.api.automation.test(automation._id!, {
+ fields: {},
oldRow: {
City: oldCity,
},
row: {
City: newCity,
},
+ })
+
+ if (isDidNotTriggerResponse(res)) {
+ throw new Error("Automation did not trigger")
}
- const res = await testAutomation(config, automation, triggerInputs)
- expect(res.body.steps[1].outputs.result).toEqual(expectedResult)
+
+ const expectedResult = oldCity === newCity
+ expect(res.steps[1].outputs.result).toEqual(expectedResult)
}
)
})
@@ -657,16 +643,18 @@ describe("/automations", () => {
let table: Table
beforeAll(async () => {
- table = await config.createTable({
- name: "table",
- type: "table",
- schema: {
- Approved: {
- name: "Approved",
- type: FieldType.BOOLEAN,
+ table = await config.api.table.save(
+ basicTable(undefined, {
+ name: "table",
+ type: "table",
+ schema: {
+ Approved: {
+ name: "Approved",
+ type: FieldType.BOOLEAN,
+ },
},
- },
- })
+ })
+ )
})
const testCases = [
@@ -712,33 +700,29 @@ describe("/automations", () => {
it.each(testCases)(
"$description",
async ({ filters, row, oldRow, expectToRun }) => {
- let automation = await updateRowAutomationWithFilters(
- config.getAppId(),
- table._id!
- )
- automation.definition.trigger.inputs = {
+ let req = updateRowAutomationWithFilters(config.getAppId(), table._id!)
+ req.definition.trigger.inputs = {
tableId: table._id,
filters,
}
- automation = await config.createAutomation(automation)
- const inputs = {
- row: {
- tableId: table._id,
- ...row,
- },
+ const { automation } = await config.api.automation.post(req)
+ const res = await config.api.automation.test(automation._id!, {
+ fields: {},
oldRow: {
tableId: table._id,
...oldRow,
},
- }
+ row: {
+ tableId: table._id,
+ ...row,
+ },
+ })
- const res = await testAutomation(config, automation, inputs)
-
- if (expectToRun) {
- expect(res.body.steps[1].outputs.success).toEqual(true)
+ if (isDidNotTriggerResponse(res)) {
+ expect(expectToRun).toEqual(false)
} else {
- expect(res.body.outputs.success).toEqual(false)
+ expect(res.steps[1].outputs.success).toEqual(expectToRun)
}
}
)
diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.ts b/packages/server/src/api/routes/tests/utilities/TestFunctions.ts
index 9d5417d041..a232fec859 100644
--- a/packages/server/src/api/routes/tests/utilities/TestFunctions.ts
+++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.ts
@@ -53,15 +53,6 @@ export const clearAllApps = async (
})
}
-export const clearAllAutomations = async (config: TestConfiguration) => {
- const { automations } = await config.getAllAutomations()
- for (let auto of automations) {
- await context.doInAppContext(config.getAppId(), async () => {
- await config.deleteAutomation(auto)
- })
- }
-}
-
export const wipeDb = async () => {
const couchInfo = db.getCouchInfo()
const nano = Nano({
diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts
index 2d36e7855b..1f464b2ea4 100644
--- a/packages/server/src/tests/utilities/TestConfiguration.ts
+++ b/packages/server/src/tests/utilities/TestConfiguration.ts
@@ -258,7 +258,7 @@ export default class TestConfiguration {
}
}
- async withApp(app: App | string, f: () => Promise) {
+ async withApp(app: App | string, f: () => Promise) {
const oldAppId = this.appId
this.appId = typeof app === "string" ? app : app.appId
try {
@@ -268,6 +268,10 @@ export default class TestConfiguration {
}
}
+ async withProdApp(f: () => Promise) {
+ return await this.withApp(this.getProdAppId(), f)
+ }
+
// UTILS
_req | void, Res>(
diff --git a/packages/server/src/tests/utilities/api/automation.ts b/packages/server/src/tests/utilities/api/automation.ts
index 3f51385251..c4438560ae 100644
--- a/packages/server/src/tests/utilities/api/automation.ts
+++ b/packages/server/src/tests/utilities/api/automation.ts
@@ -1,8 +1,17 @@
import {
Automation,
+ CreateAutomationResponse,
+ DeleteAutomationResponse,
FetchAutomationResponse,
+ GetAutomationActionDefinitionsResponse,
+ GetAutomationStepDefinitionsResponse,
+ GetAutomationTriggerDefinitionsResponse,
TestAutomationRequest,
TestAutomationResponse,
+ TriggerAutomationRequest,
+ TriggerAutomationResponse,
+ UpdateAutomationRequest,
+ UpdateAutomationResponse,
} from "@budibase/types"
import { Expectations, TestAPI } from "./base"
@@ -20,6 +29,39 @@ export class AutomationAPI extends TestAPI {
return result
}
+ getActions = async (
+ expectations?: Expectations
+ ): Promise => {
+ return await this._get(
+ `/api/automations/actions/list`,
+ {
+ expectations,
+ }
+ )
+ }
+
+ getTriggers = async (
+ expectations?: Expectations
+ ): Promise => {
+ return await this._get(
+ `/api/automations/triggers/list`,
+ {
+ expectations,
+ }
+ )
+ }
+
+ getDefinitions = async (
+ expectations?: Expectations
+ ): Promise => {
+ return await this._get(
+ `/api/automations/definitions/list`,
+ {
+ expectations,
+ }
+ )
+ }
+
fetch = async (
expectations?: Expectations
): Promise => {
@@ -31,11 +73,14 @@ export class AutomationAPI extends TestAPI {
post = async (
body: Automation,
expectations?: Expectations
- ): Promise => {
- const result = await this._post(`/api/automations`, {
- body,
- expectations,
- })
+ ): Promise => {
+ const result = await this._post(
+ `/api/automations`,
+ {
+ body,
+ expectations,
+ }
+ )
return result
}
@@ -52,4 +97,40 @@ export class AutomationAPI extends TestAPI {
}
)
}
+
+ trigger = async (
+ id: string,
+ body: TriggerAutomationRequest,
+ expectations?: Expectations
+ ): Promise => {
+ return await this._post(
+ `/api/automations/${id}/trigger`,
+ {
+ expectations,
+ body,
+ }
+ )
+ }
+
+ update = async (
+ body: UpdateAutomationRequest,
+ expectations?: Expectations
+ ): Promise => {
+ return await this._put(`/api/automations`, {
+ body,
+ expectations,
+ })
+ }
+
+ delete = async (
+ automation: Automation,
+ expectations?: Expectations
+ ): Promise => {
+ return await this._delete(
+ `/api/automations/${automation._id!}/${automation._rev!}`,
+ {
+ expectations,
+ }
+ )
+ }
}
diff --git a/packages/server/src/tests/utilities/api/index.ts b/packages/server/src/tests/utilities/api/index.ts
index c5eede18d6..2fdf726b6c 100644
--- a/packages/server/src/tests/utilities/api/index.ts
+++ b/packages/server/src/tests/utilities/api/index.ts
@@ -19,43 +19,43 @@ import { PluginAPI } from "./plugin"
import { WebhookAPI } from "./webhook"
export default class API {
- table: TableAPI
- legacyView: LegacyViewAPI
- viewV2: ViewV2API
- row: RowAPI
- permission: PermissionAPI
- datasource: DatasourceAPI
- screen: ScreenAPI
application: ApplicationAPI
- backup: BackupAPI
attachment: AttachmentAPI
- user: UserAPI
+ automation: AutomationAPI
+ backup: BackupAPI
+ datasource: DatasourceAPI
+ legacyView: LegacyViewAPI
+ permission: PermissionAPI
+ plugin: PluginAPI
query: QueryAPI
roles: RoleAPI
- templates: TemplateAPI
+ row: RowAPI
rowAction: RowActionAPI
- automation: AutomationAPI
- plugin: PluginAPI
+ screen: ScreenAPI
+ table: TableAPI
+ templates: TemplateAPI
+ user: UserAPI
+ viewV2: ViewV2API
webhook: WebhookAPI
constructor(config: TestConfiguration) {
- this.table = new TableAPI(config)
- this.legacyView = new LegacyViewAPI(config)
- this.viewV2 = new ViewV2API(config)
- this.row = new RowAPI(config)
- this.permission = new PermissionAPI(config)
- this.datasource = new DatasourceAPI(config)
- this.screen = new ScreenAPI(config)
this.application = new ApplicationAPI(config)
- this.backup = new BackupAPI(config)
this.attachment = new AttachmentAPI(config)
- this.user = new UserAPI(config)
+ this.automation = new AutomationAPI(config)
+ this.backup = new BackupAPI(config)
+ this.datasource = new DatasourceAPI(config)
+ this.legacyView = new LegacyViewAPI(config)
+ this.permission = new PermissionAPI(config)
+ this.plugin = new PluginAPI(config)
this.query = new QueryAPI(config)
this.roles = new RoleAPI(config)
- this.templates = new TemplateAPI(config)
+ this.row = new RowAPI(config)
this.rowAction = new RowActionAPI(config)
- this.automation = new AutomationAPI(config)
- this.plugin = new PluginAPI(config)
+ this.screen = new ScreenAPI(config)
+ this.table = new TableAPI(config)
+ this.templates = new TemplateAPI(config)
+ this.user = new UserAPI(config)
+ this.viewV2 = new ViewV2API(config)
this.webhook = new WebhookAPI(config)
}
}
diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts
index 3058a706c1..0c74a0faa2 100644
--- a/packages/server/src/tests/utilities/structures.ts
+++ b/packages/server/src/tests/utilities/structures.ts
@@ -34,6 +34,7 @@ import {
Webhook,
WebhookActionType,
BuiltinPermissionID,
+ DeepPartial,
} from "@budibase/types"
import { LoopInput } from "../../definitions/automations"
import { merge } from "lodash"
@@ -184,21 +185,12 @@ export function newAutomation({
steps,
trigger,
}: { steps?: AutomationStep[]; trigger?: AutomationTrigger } = {}) {
- const automation = basicAutomation()
-
- if (trigger) {
- automation.definition.trigger = trigger
- } else {
- automation.definition.trigger = automationTrigger()
- }
-
- if (steps) {
- automation.definition.steps = steps
- } else {
- automation.definition.steps = [automationStep()]
- }
-
- return automation
+ return basicAutomation({
+ definition: {
+ steps: steps || [automationStep()],
+ trigger: trigger || automationTrigger(),
+ },
+ })
}
export function rowActionAutomation() {
@@ -211,8 +203,8 @@ export function rowActionAutomation() {
return automation
}
-export function basicAutomation(appId?: string): Automation {
- return {
+export function basicAutomation(opts?: DeepPartial): Automation {
+ const baseAutomation: Automation = {
name: "My Automation",
screenId: "kasdkfldsafkl",
live: true,
@@ -241,8 +233,9 @@ export function basicAutomation(appId?: string): Automation {
steps: [],
},
type: "automation",
- appId: appId!,
+ appId: "appId",
}
+ return merge(baseAutomation, opts)
}
export function basicCronAutomation(appId: string, cron: string): Automation {
@@ -387,16 +380,21 @@ export function loopAutomation(
return automation as Automation
}
-export function collectAutomation(tableId?: string): Automation {
- const automation: any = {
+export function collectAutomation(opts?: DeepPartial): Automation {
+ const baseAutomation: Automation = {
+ appId: "appId",
name: "looping",
type: "automation",
definition: {
steps: [
{
id: "b",
- type: "ACTION",
+ name: "b",
+ tagline: "An automation action step",
+ icon: "Icon",
+ type: AutomationStepType.ACTION,
internal: true,
+ description: "Execute script",
stepId: AutomationActionStepId.EXECUTE_SCRIPT,
inputs: {
code: "return [1,2,3]",
@@ -405,8 +403,12 @@ export function collectAutomation(tableId?: string): Automation {
},
{
id: "c",
- type: "ACTION",
+ name: "c",
+ type: AutomationStepType.ACTION,
+ tagline: "An automation action step",
+ icon: "Icon",
internal: true,
+ description: "Collect",
stepId: AutomationActionStepId.COLLECT,
inputs: {
collection: "{{ literal steps.1.value }}",
@@ -416,24 +418,28 @@ export function collectAutomation(tableId?: string): Automation {
],
trigger: {
id: "a",
- type: "TRIGGER",
+ type: AutomationStepType.TRIGGER,
event: AutomationEventType.ROW_SAVE,
stepId: AutomationTriggerStepId.ROW_SAVED,
+ name: "trigger Step",
+ tagline: "An automation trigger",
+ description: "A trigger",
+ icon: "Icon",
inputs: {
- tableId,
+ tableId: "tableId",
},
schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema,
},
},
}
- return automation
+ return merge(baseAutomation, opts)
}
-export function filterAutomation(appId: string, tableId?: string): Automation {
+export function filterAutomation(opts?: DeepPartial): Automation {
const automation: Automation = {
name: "looping",
type: "automation",
- appId,
+ appId: "appId",
definition: {
steps: [
{
@@ -459,13 +465,13 @@ export function filterAutomation(appId: string, tableId?: string): Automation {
event: AutomationEventType.ROW_SAVE,
stepId: AutomationTriggerStepId.ROW_SAVED,
inputs: {
- tableId: tableId!,
+ tableId: "tableId",
},
schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema,
},
},
}
- return automation
+ return merge(automation, opts)
}
export function updateRowAutomationWithFilters(
diff --git a/packages/types/src/api/web/app/automation.ts b/packages/types/src/api/web/app/automation.ts
index b97dee0baf..f72966d100 100644
--- a/packages/types/src/api/web/app/automation.ts
+++ b/packages/types/src/api/web/app/automation.ts
@@ -75,6 +75,7 @@ export interface TestAutomationRequest {
revision?: string
fields: Record
row?: Row
+ oldRow?: Row
}
export type TestAutomationResponse = AutomationResults | DidNotTriggerResponse